179 lines
7.2 KiB
Python
179 lines
7.2 KiB
Python
import uuid
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from rest_framework import status
|
|
from rest_framework.test import APIClient
|
|
|
|
from accounts.tests.factories import AccountUserFactory
|
|
from cms.models import ContentBlock, ScanStatus
|
|
from cms.upload import UploadValidationError, validate_upload
|
|
|
|
from .factories import ContentBlockFactory, CourseThemeFactory, MediaAssetFactory, PageFactory
|
|
|
|
|
|
@pytest.fixture
|
|
def api_client():
|
|
return APIClient()
|
|
|
|
|
|
@pytest.fixture
|
|
def auth_client():
|
|
user = AccountUserFactory()
|
|
client = APIClient()
|
|
client.force_authenticate(user=user)
|
|
return client, user
|
|
|
|
|
|
# ── Upload validation ──────────────────────────────────────────────────────────
|
|
|
|
class TestValidateUpload:
|
|
def _make_file(self, name, size=1024, content=b"x" * 1024):
|
|
return SimpleUploadedFile(name, content)
|
|
|
|
def test_accepts_allowed_extension(self):
|
|
f = self._make_file("photo.jpg")
|
|
validate_upload(f) # should not raise
|
|
|
|
def test_rejects_disallowed_extension(self):
|
|
f = self._make_file("script.exe", size=100, content=b"x" * 100)
|
|
with pytest.raises(UploadValidationError, match="extension"):
|
|
validate_upload(f)
|
|
|
|
def test_rejects_oversized_file(self):
|
|
big = SimpleUploadedFile("big.jpg", b"x" * (101 * 1024 * 1024))
|
|
with pytest.raises(UploadValidationError, match="size"):
|
|
validate_upload(big)
|
|
|
|
|
|
# ── MediaUploadView ────────────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.django_db
|
|
class TestMediaUploadView:
|
|
url = "/api/cms/upload/"
|
|
|
|
def test_unauthenticated_returns_401(self, api_client):
|
|
f = SimpleUploadedFile("img.jpg", b"data", content_type="image/jpeg")
|
|
resp = api_client.post(self.url, {"file": f}, format="multipart")
|
|
assert resp.status_code == status.HTTP_401_UNAUTHORIZED
|
|
|
|
def test_upload_creates_asset(self, auth_client):
|
|
client, user = auth_client
|
|
content = b"fake image data"
|
|
f = SimpleUploadedFile("test.jpg", content, content_type="image/jpeg")
|
|
with patch("cms.views.save_upload") as mock_save:
|
|
asset = MediaAssetFactory(uploaded_by=user)
|
|
mock_save.return_value = asset
|
|
resp = client.post(self.url, {"file": f, "org_id": str(uuid.uuid4())}, format="multipart")
|
|
assert resp.status_code == status.HTTP_201_CREATED
|
|
assert "id" in resp.data
|
|
|
|
def test_missing_file_returns_400(self, auth_client):
|
|
client, _ = auth_client
|
|
resp = client.post(self.url, {}, format="multipart")
|
|
assert resp.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
def test_invalid_file_returns_400(self, auth_client):
|
|
client, _ = auth_client
|
|
f = SimpleUploadedFile("malware.exe", b"MZ", content_type="application/octet-stream")
|
|
resp = client.post(self.url, {"file": f}, format="multipart")
|
|
assert resp.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
|
|
# ── PageContentBlocksView ──────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.django_db
|
|
class TestPageContentBlocksView:
|
|
def test_list_blocks_for_page(self, auth_client):
|
|
client, _ = auth_client
|
|
page = PageFactory()
|
|
b1 = ContentBlockFactory(page=page, order=0)
|
|
b2 = ContentBlockFactory(page=page, order=1)
|
|
url = f"/api/cms/pages/{page.pk}/blocks/"
|
|
resp = client.get(url)
|
|
assert resp.status_code == status.HTTP_200_OK
|
|
ids = [b["id"] for b in resp.data]
|
|
assert str(b1.pk) in ids
|
|
assert str(b2.pk) in ids
|
|
|
|
def test_create_block(self, auth_client):
|
|
client, _ = auth_client
|
|
page = PageFactory()
|
|
url = f"/api/cms/pages/{page.pk}/blocks/"
|
|
payload = {"block_type": "richtext", "body": "<p>Hello</p>", "order": 0}
|
|
resp = client.post(url, payload, format="json")
|
|
assert resp.status_code == status.HTTP_201_CREATED
|
|
assert ContentBlock.objects.filter(page=page).count() == 1
|
|
|
|
def test_create_block_unknown_page_returns_404(self, auth_client):
|
|
client, _ = auth_client
|
|
url = f"/api/cms/pages/{uuid.uuid4()}/blocks/"
|
|
resp = client.post(url, {"block_type": "richtext", "order": 0}, format="json")
|
|
assert resp.status_code == status.HTTP_404_NOT_FOUND
|
|
|
|
|
|
# ── ContentBlockDetailView ─────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.django_db
|
|
class TestContentBlockDetailView:
|
|
def test_patch_block(self, auth_client):
|
|
client, _ = auth_client
|
|
block = ContentBlockFactory(body="original")
|
|
url = f"/api/cms/blocks/{block.pk}/"
|
|
resp = client.patch(url, {"body": "updated"}, format="json")
|
|
assert resp.status_code == status.HTTP_200_OK
|
|
block.refresh_from_db()
|
|
assert block.body == "updated"
|
|
|
|
def test_delete_block(self, auth_client):
|
|
client, _ = auth_client
|
|
block = ContentBlockFactory()
|
|
url = f"/api/cms/blocks/{block.pk}/"
|
|
resp = client.delete(url)
|
|
assert resp.status_code == status.HTTP_204_NO_CONTENT
|
|
assert not ContentBlock.objects.filter(pk=block.pk).exists()
|
|
|
|
def test_patch_nonexistent_returns_404(self, auth_client):
|
|
client, _ = auth_client
|
|
url = f"/api/cms/blocks/{uuid.uuid4()}/"
|
|
resp = client.patch(url, {"body": "x"}, format="json")
|
|
assert resp.status_code == status.HTTP_404_NOT_FOUND
|
|
|
|
|
|
# ── CourseTheme ────────────────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.django_db
|
|
class TestCourseTheme:
|
|
def test_theme_created_with_defaults(self):
|
|
theme = CourseThemeFactory()
|
|
assert theme.primary_color == "#1a1a1a"
|
|
assert theme.logo is None
|
|
|
|
def test_theme_with_logo(self):
|
|
user = AccountUserFactory()
|
|
asset = MediaAssetFactory(uploaded_by=user)
|
|
theme = CourseThemeFactory(logo=asset)
|
|
assert theme.logo == asset
|
|
|
|
|
|
# ── scan_media_asset_task ──────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.django_db
|
|
class TestScanMediaAssetTask:
|
|
def test_marks_asset_clean(self):
|
|
from cms.tasks import scan_media_asset_task
|
|
|
|
user = AccountUserFactory()
|
|
asset = MediaAssetFactory(uploaded_by=user, scan_status=ScanStatus.PENDING)
|
|
result = scan_media_asset_task(str(asset.pk))
|
|
asset.refresh_from_db()
|
|
assert asset.scan_status == ScanStatus.CLEAN
|
|
assert result["scan_status"] == "clean"
|
|
|
|
def test_missing_asset_returns_error(self):
|
|
from cms.tasks import scan_media_asset_task
|
|
|
|
result = scan_media_asset_task(str(uuid.uuid4()))
|
|
assert result["error"] == "not found"
|