diff --git a/enterprise_access/apps/api/serializers/subsidy_access_policy.py b/enterprise_access/apps/api/serializers/subsidy_access_policy.py index 59ed8376..39463560 100644 --- a/enterprise_access/apps/api/serializers/subsidy_access_policy.py +++ b/enterprise_access/apps/api/serializers/subsidy_access_policy.py @@ -533,6 +533,9 @@ class SubsidyAccessPolicyRedeemableResponseSerializer(serializers.ModelSerialize """ policy_redemption_url = serializers.SerializerMethodField() + is_late_redemption_allowed = serializers.BooleanField( + help_text="True if late redemption is currently allowed (i.e. late_redemption_allowed_until is in the future)." + ) class Meta: model = SubsidyAccessPolicy diff --git a/enterprise_access/apps/api/tests/test_serializers.py b/enterprise_access/apps/api/tests/test_serializers.py index 4f5b793f..b07d9e98 100644 --- a/enterprise_access/apps/api/tests/test_serializers.py +++ b/enterprise_access/apps/api/tests/test_serializers.py @@ -1,14 +1,14 @@ """ Tests for the serializers in the API. """ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from unittest import mock from uuid import uuid4 import ddt from django.conf import settings from django.test import TestCase -from django.urls import reverse +from freezegun import freeze_time from enterprise_access.apps.api.serializers.subsidy_access_policy import ( SubsidyAccessPolicyAggregatesSerializer, @@ -121,31 +121,55 @@ def test_aggregates( assert data["spend_available_usd_cents"] == available +@ddt.ddt class TestSubsidyAccessPolicyRedeemableResponseSerializer(TestCase): """ Tests for the SubsidyAccessPolicyRedeemableResponseSerializer. """ + NOW = datetime(2017, 1, 1, 0, 0, 0, tzinfo=timezone.utc) + def setUp(self): - self.non_redeemable_policy = PerLearnerEnrollmentCapLearnerCreditAccessPolicyFactory() - self.subsidy_access_policy_redeem_endpoint = reverse( - 'api:v1:policy-redemption-redeem', - kwargs={'policy_uuid': self.non_redeemable_policy.uuid} - ) + self.redeemable_policy = PerLearnerEnrollmentCapLearnerCreditAccessPolicyFactory() def test_get_policy_redemption_url(self): """ Test that the get_policy_redemption_url method returns the correct URL for the policy redemption. """ - - serializer = SubsidyAccessPolicyRedeemableResponseSerializer(self.non_redeemable_policy) + serializer = SubsidyAccessPolicyRedeemableResponseSerializer(self.redeemable_policy) data = serializer.data self.assertIn("policy_redemption_url", data) expected_url = f"{settings.ENTERPRISE_ACCESS_URL}/api/v1/policy-redemption/" \ - f"{self.non_redeemable_policy.uuid}/redeem/" + f"{self.redeemable_policy.uuid}/redeem/" self.assertEqual(data["policy_redemption_url"], expected_url) + @ddt.data( + { + 'late_redemption_allowed_until': None, + 'expected_is_late_redemption_allowed': False, + }, + { + 'late_redemption_allowed_until': NOW - timedelta(days=1), + 'expected_is_late_redemption_allowed': False, + }, + { + 'late_redemption_allowed_until': NOW + timedelta(days=1), + 'expected_is_late_redemption_allowed': True, + }, + ) + @ddt.unpack + @freeze_time(NOW) + def test_is_late_enrollment_allowed(self, late_redemption_allowed_until, expected_is_late_redemption_allowed): + """ + Test that the `is_late_enrollment_allowed` computed field is present and correct. + """ + policy = PerLearnerEnrollmentCapLearnerCreditAccessPolicyFactory( + late_redemption_allowed_until=late_redemption_allowed_until + ) + serializer = SubsidyAccessPolicyRedeemableResponseSerializer(policy) + assert serializer.data["is_late_redemption_allowed"] == expected_is_late_redemption_allowed + class TestSubsidyAccessPolicyCreditsAvailableResponseSerializer(TestCase): """ diff --git a/requirements/base.txt b/requirements/base.txt index f91fe86e..a14d66c9 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -109,7 +109,7 @@ django-crum==0.7.9 # edx-rbac django-extensions==3.2.3 # via -r requirements/base.in -django-filter==24.1 +django-filter==24.2 # via -r requirements/base.in django-log-request-id==2.1.0 # via -r requirements/base.in @@ -204,7 +204,7 @@ jsonschema==4.21.1 # via drf-spectacular jsonschema-specifications==2023.12.1 # via jsonschema -kombu==5.3.5 +kombu==5.3.6 # via celery markupsafe==2.1.5 # via jinja2 @@ -212,7 +212,7 @@ monotonic==1.6 # via analytics-python mysqlclient==2.2.4 # via -r requirements/base.in -newrelic==9.7.1 +newrelic==9.8.0 # via edx-django-utils oauthlib==3.2.2 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index ac74bebc..f6e24559 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -45,7 +45,7 @@ billiard==4.2.0 # via # -r requirements/validation.txt # celery -build==1.1.1 +build==1.2.1 # via # -r requirements/pip-tools.txt # pip-tools @@ -193,7 +193,7 @@ django-dynamic-fixture==4.0.1 # via -r requirements/validation.txt django-extensions==3.2.3 # via -r requirements/validation.txt -django-filter==24.1 +django-filter==24.2 # via -r requirements/validation.txt django-log-request-id==2.1.0 # via -r requirements/validation.txt @@ -294,6 +294,8 @@ filelock==3.13.3 # -r requirements/validation.txt # tox # virtualenv +freezegun==1.4.0 + # via -r requirements/validation.txt idna==3.6 # via # -r requirements/validation.txt @@ -364,7 +366,7 @@ keyring==25.0.0 # via # -r requirements/validation.txt # twine -kombu==5.3.5 +kombu==5.3.6 # via # -r requirements/validation.txt # celery @@ -397,7 +399,7 @@ more-itertools==10.2.0 # jaraco-functools mysqlclient==2.2.4 # via -r requirements/validation.txt -newrelic==9.7.1 +newrelic==9.8.0 # via # -r requirements/validation.txt # edx-django-utils @@ -543,6 +545,7 @@ python-dateutil==2.9.0.post0 # analytics-python # celery # faker + # freezegun python-slugify==8.0.4 # via # -r requirements/validation.txt diff --git a/requirements/doc.txt b/requirements/doc.txt index 52aa18bc..e62373e7 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -191,7 +191,7 @@ django-dynamic-fixture==4.0.1 # via -r requirements/test.txt django-extensions==3.2.3 # via -r requirements/test.txt -django-filter==24.1 +django-filter==24.2 # via -r requirements/test.txt django-log-request-id==2.1.0 # via -r requirements/test.txt @@ -296,6 +296,8 @@ filelock==3.13.3 # -r requirements/test.txt # tox # virtualenv +freezegun==1.4.0 + # via -r requirements/test.txt idna==3.6 # via # -r requirements/test.txt @@ -348,7 +350,7 @@ jsonschema-specifications==2023.12.1 # via # -r requirements/test.txt # jsonschema -kombu==5.3.5 +kombu==5.3.6 # via # -r requirements/test.txt # celery @@ -366,7 +368,7 @@ monotonic==1.6 # analytics-python mysqlclient==2.2.4 # via -r requirements/test.txt -newrelic==9.7.1 +newrelic==9.8.0 # via # -r requirements/test.txt # edx-django-utils @@ -494,6 +496,7 @@ python-dateutil==2.9.0.post0 # analytics-python # celery # faker + # freezegun python-slugify==8.0.4 # via # -r requirements/test.txt diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt index b1ac4e54..6401f544 100644 --- a/requirements/pip-tools.txt +++ b/requirements/pip-tools.txt @@ -4,7 +4,7 @@ # # make upgrade # -build==1.1.1 +build==1.2.1 # via pip-tools click==8.1.7 # via pip-tools diff --git a/requirements/production.txt b/requirements/production.txt index a086e375..35e5164f 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -136,7 +136,7 @@ django-crum==0.7.9 # edx-rbac django-extensions==3.2.3 # via -r requirements/base.txt -django-filter==24.1 +django-filter==24.2 # via -r requirements/base.txt django-log-request-id==2.1.0 # via -r requirements/base.txt @@ -256,7 +256,7 @@ jsonschema-specifications==2023.12.1 # via # -r requirements/base.txt # jsonschema -kombu==5.3.5 +kombu==5.3.6 # via # -r requirements/base.txt # celery @@ -272,7 +272,7 @@ mysqlclient==2.2.4 # via # -r requirements/base.txt # -r requirements/production.in -newrelic==9.7.1 +newrelic==9.8.0 # via # -r requirements/base.txt # edx-django-utils diff --git a/requirements/quality.txt b/requirements/quality.txt index f41c19a5..25a777de 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -181,7 +181,7 @@ django-dynamic-fixture==4.0.1 # via -r requirements/test.txt django-extensions==3.2.3 # via -r requirements/test.txt -django-filter==24.1 +django-filter==24.2 # via -r requirements/test.txt django-log-request-id==2.1.0 # via -r requirements/test.txt @@ -281,6 +281,8 @@ filelock==3.13.3 # -r requirements/test.txt # tox # virtualenv +freezegun==1.4.0 + # via -r requirements/test.txt idna==3.6 # via # -r requirements/test.txt @@ -341,7 +343,7 @@ jsonschema-specifications==2023.12.1 # jsonschema keyring==25.0.0 # via twine -kombu==5.3.5 +kombu==5.3.6 # via # -r requirements/test.txt # celery @@ -367,7 +369,7 @@ more-itertools==10.2.0 # jaraco-functools mysqlclient==2.2.4 # via -r requirements/test.txt -newrelic==9.7.1 +newrelic==9.8.0 # via # -r requirements/test.txt # edx-django-utils @@ -494,6 +496,7 @@ python-dateutil==2.9.0.post0 # analytics-python # celery # faker + # freezegun python-slugify==8.0.4 # via # -r requirements/test.txt diff --git a/requirements/test.in b/requirements/test.in index a4fee947..95d01861 100644 --- a/requirements/test.in +++ b/requirements/test.in @@ -9,6 +9,7 @@ ddt django-dynamic-fixture # library to create dynamic model instances for testing purposes edx-lint factory-boy +freezegun pytest-cov pytest-django tox diff --git a/requirements/test.txt b/requirements/test.txt index 4977a924..8a96fd7b 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -167,7 +167,7 @@ django-dynamic-fixture==4.0.1 # via -r requirements/test.in django-extensions==3.2.3 # via -r requirements/base.txt -django-filter==24.1 +django-filter==24.2 # via -r requirements/base.txt django-log-request-id==2.1.0 # via -r requirements/base.txt @@ -258,6 +258,8 @@ filelock==3.13.3 # via # tox # virtualenv +freezegun==1.4.0 + # via -r requirements/test.in idna==3.6 # via # -r requirements/base.txt @@ -299,7 +301,7 @@ jsonschema-specifications==2023.12.1 # via # -r requirements/base.txt # jsonschema -kombu==5.3.5 +kombu==5.3.6 # via # -r requirements/base.txt # celery @@ -315,7 +317,7 @@ monotonic==1.6 # analytics-python mysqlclient==2.2.4 # via -r requirements/base.txt -newrelic==9.7.1 +newrelic==9.8.0 # via # -r requirements/base.txt # edx-django-utils @@ -420,6 +422,7 @@ python-dateutil==2.9.0.post0 # analytics-python # celery # faker + # freezegun python-slugify==8.0.4 # via code-annotations python3-openid==3.2.0 diff --git a/requirements/validation.txt b/requirements/validation.txt index c18a6f7b..4f7ed9d5 100644 --- a/requirements/validation.txt +++ b/requirements/validation.txt @@ -224,7 +224,7 @@ django-extensions==3.2.3 # via # -r requirements/quality.txt # -r requirements/test.txt -django-filter==24.1 +django-filter==24.2 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -368,6 +368,10 @@ filelock==3.13.3 # -r requirements/test.txt # tox # virtualenv +freezegun==1.4.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt idna==3.6 # via # -r requirements/quality.txt @@ -447,7 +451,7 @@ keyring==25.0.0 # via # -r requirements/quality.txt # twine -kombu==5.3.5 +kombu==5.3.6 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -484,7 +488,7 @@ mysqlclient==2.2.4 # via # -r requirements/quality.txt # -r requirements/test.txt -newrelic==9.7.1 +newrelic==9.8.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -646,6 +650,7 @@ python-dateutil==2.9.0.post0 # analytics-python # celery # faker + # freezegun python-slugify==8.0.4 # via # -r requirements/quality.txt