351 lines
14 KiB
Python
351 lines
14 KiB
Python
import os
|
|
|
|
from django.conf import settings
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from django.test import TestCase, override_settings
|
|
from django.urls import reverse
|
|
|
|
from apps.products.models import Article, ArticleSection, MainProduct, SubProduct, SubProductVersion
|
|
|
|
|
|
class ProductViewsSetup(TestCase):
|
|
def setUp(self):
|
|
self.main_product = MainProduct.objects.create(
|
|
name="Tecvico",
|
|
slug="tecvico",
|
|
short_description="Radiomics software",
|
|
description="Full description.",
|
|
)
|
|
self.sub_product = SubProduct.objects.create(
|
|
main_product=self.main_product,
|
|
name="Image Processing",
|
|
slug="image-processing",
|
|
short_description="Filtering and registration",
|
|
description="Sub description.",
|
|
)
|
|
self.article = Article.objects.create(
|
|
sub_product=self.sub_product,
|
|
title="Filtering",
|
|
description="Article body.",
|
|
)
|
|
ArticleSection.objects.create(
|
|
article=self.article,
|
|
title="Standard",
|
|
value="IBSI 2.0",
|
|
)
|
|
|
|
|
|
class ProductOverviewViewTest(ProductViewsSetup):
|
|
def test_overview_returns_200(self):
|
|
response = self.client.get(reverse("products:overview"))
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_overview_uses_correct_template(self):
|
|
response = self.client.get(reverse("products:overview"))
|
|
self.assertTemplateUsed(response, "products/overview.html")
|
|
|
|
def test_overview_contains_main_product(self):
|
|
response = self.client.get(reverse("products:overview"))
|
|
self.assertIn(self.main_product, response.context["main_products"])
|
|
|
|
def test_inactive_product_excluded(self):
|
|
inactive = MainProduct.objects.create(
|
|
name="Inactive",
|
|
short_description="s",
|
|
description="d",
|
|
is_active=False,
|
|
)
|
|
response = self.client.get(reverse("products:overview"))
|
|
self.assertNotIn(inactive, response.context["main_products"])
|
|
|
|
|
|
class MainProductDetailViewTest(ProductViewsSetup):
|
|
def test_detail_returns_200(self):
|
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "tecvico"})
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_detail_uses_correct_template(self):
|
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "tecvico"})
|
|
response = self.client.get(url)
|
|
self.assertTemplateUsed(response, "products/main_detail.html")
|
|
|
|
def test_detail_contains_product_in_context(self):
|
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "tecvico"})
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.context["main_product"], self.main_product)
|
|
|
|
def test_nonexistent_slug_returns_404(self):
|
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "nonexistent"})
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_inactive_product_returns_404(self):
|
|
self.main_product.is_active = False
|
|
self.main_product.save()
|
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "tecvico"})
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_detail_articles_in_context(self):
|
|
main_article = Article.objects.create(
|
|
main_product=self.main_product,
|
|
title="Overview",
|
|
description="Main product article.",
|
|
)
|
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "tecvico"})
|
|
response = self.client.get(url)
|
|
self.assertIn("articles", response.context)
|
|
self.assertIn(main_article, list(response.context["articles"]))
|
|
self.assertNotIn(self.article, list(response.context["articles"]))
|
|
|
|
|
|
class SubProductDetailViewTest(ProductViewsSetup):
|
|
def test_sub_detail_returns_200(self):
|
|
url = reverse(
|
|
"products:sub_product_detail",
|
|
kwargs={"main_slug": "tecvico", "sub_slug": "image-processing"},
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_sub_detail_uses_correct_template(self):
|
|
url = reverse(
|
|
"products:sub_product_detail",
|
|
kwargs={"main_slug": "tecvico", "sub_slug": "image-processing"},
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertTemplateUsed(response, "products/sub_detail.html")
|
|
|
|
def test_sub_detail_context_keys(self):
|
|
url = reverse(
|
|
"products:sub_product_detail",
|
|
kwargs={"main_slug": "tecvico", "sub_slug": "image-processing"},
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertIn("main_product", response.context)
|
|
self.assertIn("sub_product", response.context)
|
|
self.assertIn("articles", response.context)
|
|
self.assertIn("siblings", response.context)
|
|
|
|
def test_sub_detail_articles_in_context(self):
|
|
url = reverse(
|
|
"products:sub_product_detail",
|
|
kwargs={"main_slug": "tecvico", "sub_slug": "image-processing"},
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertIn(self.article, list(response.context["articles"]))
|
|
|
|
def test_nonexistent_sub_slug_returns_404(self):
|
|
url = reverse(
|
|
"products:sub_product_detail",
|
|
kwargs={"main_slug": "tecvico", "sub_slug": "does-not-exist"},
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_mismatched_main_slug_returns_404(self):
|
|
url = reverse(
|
|
"products:sub_product_detail",
|
|
kwargs={"main_slug": "wrong-product", "sub_slug": "image-processing"},
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
|
|
class SubProductOlderVersionsViewTest(ProductViewsSetup):
|
|
def test_versions_returns_200(self):
|
|
from apps.products.models import SubProductVersion
|
|
|
|
SubProductVersion.objects.create(
|
|
sub_product=self.sub_product,
|
|
version="9.9",
|
|
is_featured=True,
|
|
release_channel="stable",
|
|
windows_download_url="https://example.com/w",
|
|
is_active=True,
|
|
)
|
|
SubProductVersion.objects.create(
|
|
sub_product=self.sub_product,
|
|
version="9.8",
|
|
is_featured=False,
|
|
windows_download_url="https://example.com/w2",
|
|
is_active=True,
|
|
)
|
|
url = reverse(
|
|
"products:sub_product_versions",
|
|
kwargs={"main_slug": "tecvico", "sub_slug": "image-processing"},
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, "products/versions_archive.html")
|
|
|
|
def test_main_versions_returns_200(self):
|
|
from apps.products.models import SubProductVersion
|
|
|
|
SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="9.9",
|
|
is_featured=True,
|
|
release_channel="stable",
|
|
windows_download_url="https://example.com/win.exe",
|
|
is_active=True,
|
|
)
|
|
SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="9.8",
|
|
is_active=True,
|
|
)
|
|
url = reverse(
|
|
"products:main_product_versions",
|
|
kwargs={"main_slug": "tecvico"},
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, "products/versions_archive.html")
|
|
|
|
def test_main_detail_releases_in_context(self):
|
|
from apps.products.models import SubProductVersion
|
|
|
|
SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="1.0",
|
|
windows_download_url="https://example.com/win.exe",
|
|
is_active=True,
|
|
)
|
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "tecvico"})
|
|
response = self.client.get(url)
|
|
self.assertTrue(response.context["show_releases_section"])
|
|
|
|
def test_installable_shows_release_channel_badges(self):
|
|
from apps.products.models import RELEASE_CHANNEL_BETA, RELEASE_CHANNEL_PREVIOUS, SubProductVersion
|
|
|
|
SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="2.0",
|
|
release_channel=RELEASE_CHANNEL_BETA,
|
|
is_featured=True,
|
|
windows_download_url="https://example.com/beta.exe",
|
|
is_active=True,
|
|
)
|
|
SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="1.9",
|
|
release_channel=RELEASE_CHANNEL_PREVIOUS,
|
|
show_on_product_page=True,
|
|
windows_download_url="https://example.com/prev.exe",
|
|
is_active=True,
|
|
)
|
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "tecvico"})
|
|
response = self.client.get(url)
|
|
self.assertContains(response, "Beta")
|
|
self.assertContains(response, "Previous")
|
|
self.assertEqual(len(response.context["inline_download_channels"]), 1)
|
|
|
|
def test_custom_channel_label_overrides_preset(self):
|
|
from apps.products.models import RELEASE_CHANNEL_STABLE, SubProductVersion
|
|
|
|
SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="3.0",
|
|
release_channel=RELEASE_CHANNEL_STABLE,
|
|
channel_label="Early Access",
|
|
is_featured=True,
|
|
windows_download_url="https://example.com/ea.exe",
|
|
is_active=True,
|
|
)
|
|
url = reverse("products:main_product_detail", kwargs={"main_slug": "tecvico"})
|
|
response = self.client.get(url)
|
|
self.assertContains(response, "Early Access")
|
|
self.assertNotContains(response, ">Stable<")
|
|
|
|
|
|
@override_settings(MEDIA_ROOT=settings.BASE_DIR / "test_media_releases")
|
|
class ReleaseAssetDownloadViewTest(ProductViewsSetup):
|
|
def test_uploaded_file_download(self):
|
|
release_file = SimpleUploadedFile("setup.exe", b"binary-payload")
|
|
version = SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="2.0",
|
|
windows_download_file=release_file,
|
|
is_active=True,
|
|
)
|
|
url = reverse(
|
|
"products:release_asset_download",
|
|
kwargs={"version_id": version.pk, "asset": "windows"},
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(b"".join(response.streaming_content), b"binary-payload")
|
|
self.assertIn("attachment", response["Content-Disposition"])
|
|
self.assertIn("setup.exe", response["Content-Disposition"])
|
|
|
|
def test_external_url_blocks_file_download(self):
|
|
version = SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="2.1",
|
|
windows_download_url="https://example.com/win.exe",
|
|
windows_download_file=SimpleUploadedFile("local.exe", b"x"),
|
|
is_active=True,
|
|
)
|
|
url = reverse(
|
|
"products:release_asset_download",
|
|
kwargs={"version_id": version.pk, "asset": "windows"},
|
|
)
|
|
self.assertEqual(self.client.get(url).status_code, 404)
|
|
|
|
def test_product_page_uses_file_when_no_url(self):
|
|
version = SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="3.0",
|
|
windows_download_file=SimpleUploadedFile("win.tar.gz", b"gz"),
|
|
is_active=True,
|
|
)
|
|
detail_url = reverse("products:main_product_detail", kwargs={"main_slug": "tecvico"})
|
|
response = self.client.get(detail_url)
|
|
download_url = reverse(
|
|
"products:release_asset_download",
|
|
kwargs={"version_id": version.pk, "asset": "windows"},
|
|
)
|
|
self.assertContains(response, download_url)
|
|
|
|
def test_inactive_version_returns_404(self):
|
|
version = SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="0.1",
|
|
windows_download_file=SimpleUploadedFile("a.exe", b"a"),
|
|
is_active=False,
|
|
)
|
|
url = reverse(
|
|
"products:release_asset_download",
|
|
kwargs={"version_id": version.pk, "asset": "windows"},
|
|
)
|
|
self.assertEqual(self.client.get(url).status_code, 404)
|
|
|
|
def test_clearing_file_removes_from_disk(self):
|
|
version = SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="4.0",
|
|
windows_download_file=SimpleUploadedFile("remove-me.exe", b"data"),
|
|
is_active=True,
|
|
)
|
|
stored_path = version.windows_download_file.path
|
|
self.assertTrue(stored_path)
|
|
version.windows_download_file = ""
|
|
version.save()
|
|
version.refresh_from_db()
|
|
self.assertFalse(version.windows_download_file)
|
|
self.assertEqual(version.windows_download_filename, "")
|
|
self.assertFalse(os.path.exists(stored_path))
|
|
|
|
def test_deleting_version_removes_files_from_disk(self):
|
|
version = SubProductVersion.objects.create(
|
|
main_product=self.main_product,
|
|
version="5.0",
|
|
windows_download_file=SimpleUploadedFile("gone.exe", b"data"),
|
|
is_active=True,
|
|
)
|
|
stored_path = version.windows_download_file.path
|
|
version.delete()
|
|
self.assertFalse(os.path.exists(stored_path))
|