From 4d807f5a50ac6d58b05583a55ba72b966f33b23d Mon Sep 17 00:00:00 2001 From: Daniel Vainio Date: Mon, 13 Jan 2025 16:11:31 +0200 Subject: [PATCH] Implement is_unavailable status Add new functions to the Package and PackageVersion models to determine whether a package or package version is unavailable. A package is considered unavailable if: - It is not active (is_effectively_active is False), or - It does not have a related listing in a specific community, or - It has a listing, but the listing is marked as rejected. A package version is considered unavailable if: - The related package is unavailable, or - The package version itself is not active. - (If both conditions apply, the package's status takes precedence.) Additionally, implement unit tests to validate these new functions for both the Package and PackageVersion models. Refs. TS-2776 --- .../thunderstore/repository/models/package.py | 16 ++++ .../repository/models/package_version.py | 6 ++ .../repository/tests/test_package.py | 74 ++++++++++++++++++- .../repository/tests/test_package_version.py | 24 ++++++ 4 files changed, 118 insertions(+), 2 deletions(-) diff --git a/django/thunderstore/repository/models/package.py b/django/thunderstore/repository/models/package.py index 5abe5bf58..3f84c699e 100644 --- a/django/thunderstore/repository/models/package.py +++ b/django/thunderstore/repository/models/package.py @@ -135,6 +135,22 @@ def update_listing(self, has_nsfw_content, categories, community): listing.categories.add(*categories) listing.save(update_fields=("has_nsfw_content",)) + def listing_is_unavailable(self, listing) -> bool: + if listing is None: + return True + + return any([ + listing.is_rejected, + listing.is_waiting_for_approval, + ]) + + def is_unavailable(self, community) -> bool: + if self.is_effectively_active is False: + return True + + listing = self.get_package_listing(community) + return self.listing_is_unavailable(listing) + @cached_property def has_wiki(self) -> bool: try: diff --git a/django/thunderstore/repository/models/package_version.py b/django/thunderstore/repository/models/package_version.py index 23a434172..652aad513 100644 --- a/django/thunderstore/repository/models/package_version.py +++ b/django/thunderstore/repository/models/package_version.py @@ -190,6 +190,12 @@ def get_page_url(self, community_identifier: str) -> str: }, ) + def is_unavailable(self, community) -> bool: + unavailable = self.package.is_unavailable(community) + if unavailable: + return True + return self.is_active is False + @cached_property def display_name(self): return self.name.replace("_", " ") diff --git a/django/thunderstore/repository/tests/test_package.py b/django/thunderstore/repository/tests/test_package.py index 97a20ed97..2eaf775f8 100644 --- a/django/thunderstore/repository/tests/test_package.py +++ b/django/thunderstore/repository/tests/test_package.py @@ -7,10 +7,15 @@ from django.core.exceptions import ValidationError from conftest import TestUserTypes -from thunderstore.community.factories import PackageCategoryFactory, SiteFactory +from thunderstore.community.factories import ( + CommunityFactory, + PackageCategoryFactory, + PackageListingFactory, + SiteFactory, +) from thunderstore.community.models.package_listing import PackageListing from thunderstore.core.types import UserType -from thunderstore.repository.factories import PackageFactory +from thunderstore.repository.factories import PackageFactory, PackageVersionFactory from thunderstore.repository.models import ( Namespace, Package, @@ -19,6 +24,7 @@ TeamMemberRole, ) from thunderstore.wiki.factories import WikiPageFactory +from thunderstore.community.consts import PackageListingReviewStatus User = get_user_model() @@ -224,3 +230,67 @@ def test_package_update_listing( assert listing.categories.count() == 3 for entry in cats: assert entry in listing.categories.all() + + +@pytest.mark.django_db +@pytest.mark.parametrize( + ( + "review_status", + "package_is_active", + "require_package_listing_approval", + "expected_is_unavailable_result", + ), + [ + (PackageListingReviewStatus.approved, True, False, False), + (PackageListingReviewStatus.approved, False, False, True), + (PackageListingReviewStatus.rejected, True, False, True), + (PackageListingReviewStatus.rejected, False, False, True), + (PackageListingReviewStatus.unreviewed, True, True, True), + (PackageListingReviewStatus.unreviewed, True, False, False), + (PackageListingReviewStatus.unreviewed, False, True, True), + (PackageListingReviewStatus.unreviewed, False, False, True), + ], +) +def test_package_is_unavailable( + review_status: PackageListingReviewStatus, + package_is_active: bool, + require_package_listing_approval: bool, + expected_is_unavailable_result: bool, +) -> None: + community = CommunityFactory( + require_package_listing_approval=require_package_listing_approval + ) + + package = PackageFactory(is_active=package_is_active) + if package_is_active: + PackageVersionFactory(package=package, version_number="1.0.0") + + PackageListingFactory( + package_=package, + community_=community, + review_status=review_status, + ) + + assert package.is_unavailable(community) == expected_is_unavailable_result + + +@pytest.mark.django_db +def test_package_is_unavailable_no_listing() -> None: + community = CommunityFactory() + package = PackageFactory(is_active=True) + PackageVersionFactory(package=package, version_number="1.0.0") + + assert package.is_unavailable(community) is True + + +@pytest.mark.django_db +def test_package_is_unavailable_no_version() -> None: + community = CommunityFactory() + package = PackageFactory(is_active=True) + PackageListingFactory( + package_=package, + community_=community, + review_status=PackageListingReviewStatus.approved, + ) + + assert package.is_unavailable(community) is True diff --git a/django/thunderstore/repository/tests/test_package_version.py b/django/thunderstore/repository/tests/test_package_version.py index 3ee0dfaf7..650c07e7c 100644 --- a/django/thunderstore/repository/tests/test_package_version.py +++ b/django/thunderstore/repository/tests/test_package_version.py @@ -8,6 +8,7 @@ from thunderstore.repository.factories import PackageFactory, PackageVersionFactory from thunderstore.repository.models import PackageVersion from thunderstore.repository.package_formats import PackageFormats +from thunderstore.community.factories import CommunityFactory @pytest.mark.django_db @@ -129,3 +130,26 @@ def test_package_version_is_effectively_active( version = PackageVersionFactory(package=package, is_active=version_is_active) assert version.is_effectively_active == (package_is_active and version_is_active) + + +@pytest.mark.django_db +@pytest.mark.parametrize( + ("package_is_unavailable", "version_is_active", "expected_is_unavailable"), + [ + (True, True, True), + (True, False, True), + (False, True, False), + (False, False, True), + ], +) +def test_package_version_is_unavailable( + package_is_unavailable: bool, + version_is_active: bool, + expected_is_unavailable: bool, +) -> None: + community = CommunityFactory() + package = PackageFactory() + package.is_unavailable = lambda _: package_is_unavailable + version = PackageVersionFactory(package=package, is_active=version_is_active) + + assert version.is_unavailable(community) == expected_is_unavailable