Skip to content

Commit

Permalink
Merge pull request #606 from openedx/ammar/add-subsidy-uuid-validation
Browse files Browse the repository at this point in the history
feat: add validation on subsidy uuid
  • Loading branch information
muhammad-ammar authored Dec 11, 2024
2 parents 078919c + 5c71d0f commit 4ca1895
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
SubsidyAccessPolicySetLateRedemptionView
)

from .forms import ForcedPolicyRedemptionForm
from .forms import ForcedPolicyRedemptionForm, SubsidyAccessPolicyForm

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -199,6 +199,8 @@ class PerLearnerEnrollmentCreditAccessPolicy(DjangoQLSearchMixin, BaseSubsidyAcc
"""
Admin configuration for PerLearnerEnrollmentCreditAccessPolicy.
"""
form = SubsidyAccessPolicyForm

list_display = BaseSubsidyAccessPolicyMixin.list_display + (
'per_learner_enrollment_limit',
)
Expand Down Expand Up @@ -250,6 +252,8 @@ class PerLearnerSpendCreditAccessPolicy(DjangoQLSearchMixin, BaseSubsidyAccessPo
"""
Admin configuration for PerLearnerSpendCreditAccessPolicy.
"""
form = SubsidyAccessPolicyForm

list_display = BaseSubsidyAccessPolicyMixin.list_display + (
'per_learner_spend_limit_dollars',
)
Expand Down Expand Up @@ -313,6 +317,8 @@ class LearnerContentAssignmentAccessPolicy(DjangoQLSearchMixin, BaseSubsidyAcces
"""
Admin configuration for AssignedLearnerCreditAccessPolicy.
"""
form = SubsidyAccessPolicyForm

search_fields = (
'uuid',
'display_name',
Expand Down
33 changes: 32 additions & 1 deletion enterprise_access/apps/subsidy_access_policy/admin/forms.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
"""
Forms to be used for subsidy_access_policy django admin.
"""
import requests
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _

from ..models import ForcedPolicyRedemption
from enterprise_access.apps.subsidy_access_policy.utils import get_versioned_subsidy_client

from ..models import ForcedPolicyRedemption, SubsidyAccessPolicy


class LateRedemptionDaysFromNowChoices:
Expand Down Expand Up @@ -92,3 +96,30 @@ class ForcedPolicyRedemptionForm(forms.ModelForm):
class Meta:
model = ForcedPolicyRedemption
fields = '__all__'


class SubsidyAccessPolicyForm(forms.ModelForm):
"""
Admin form for the SubsidyAccessPolicy model.
"""
def clean_subsidy_uuid(self):
"""
Validate that the subsidy exists and is assigned to the same enterprise customer as the budget.
"""
# 1. check if the subsidy_uuid actually exists
# 2. subsidy is assigned to the same enterprise customer as the budget
# if any of these checks fail, raise a ValidationError
client = get_versioned_subsidy_client(version=1)
try:
subsidy = client.retrieve_subsidy(self.cleaned_data["subsidy_uuid"])
except requests.exceptions.HTTPError as exc:
raise ValidationError("Subsidy does not exist") from exc

if str(subsidy["enterprise_customer_uuid"]) != str(self.cleaned_data["enterprise_customer_uuid"]):
raise ValidationError("Subsidy is not assigned to the same enterprise customer as the budget")

return self.cleaned_data["subsidy_uuid"]

class Meta:
model = SubsidyAccessPolicy
fields = '__all__'
77 changes: 77 additions & 0 deletions enterprise_access/apps/subsidy_access_policy/tests/test_forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
Unittests for forms.
"""
import uuid
from unittest import mock

import requests
from django.test import TestCase

from enterprise_access.apps.subsidy_access_policy.admin.forms import SubsidyAccessPolicyForm
from enterprise_access.apps.subsidy_access_policy.constants import AccessMethods


class TestSubsidyAccessPolicyForm(TestCase):
"""
Tests for SubsidyAccessPolicyForm.
"""
def setUp(self):
super().setUp()
self.enterprise_customer_uuid = uuid.uuid4()
self.subsidy_uuid = uuid.uuid4()
self.catalog_uuid = uuid.uuid4()
self.form_data = {
'enterprise_customer_uuid': self.enterprise_customer_uuid,
'subsidy_uuid': self.subsidy_uuid,
'catalog_uuid': self.catalog_uuid,
'access_method': AccessMethods.DIRECT,
}

@mock.patch('enterprise_access.apps.subsidy_access_policy.admin.forms.get_versioned_subsidy_client')
def test_clean_subsidy_uuid_success(self, mock_get_client):
"""
Test successful validation when subsidy exists and belongs to enterprise customer.
"""
mock_client = mock.MagicMock()
mock_client.retrieve_subsidy.return_value = {
'enterprise_customer_uuid': self.enterprise_customer_uuid
}
mock_get_client.return_value = mock_client

form = SubsidyAccessPolicyForm(data=self.form_data)
self.assertTrue(form.is_valid())
self.assertEqual(form.cleaned_data['subsidy_uuid'], self.subsidy_uuid)

@mock.patch('enterprise_access.apps.subsidy_access_policy.admin.forms.get_versioned_subsidy_client')
def test_clean_subsidy_uuid_not_found(self, mock_get_client):
"""
Verify that a validation error is raised when the subsidy does not exist.
"""
mock_client = mock.MagicMock()
mock_client.retrieve_subsidy.side_effect = requests.exceptions.HTTPError()
mock_get_client.return_value = mock_client

form = SubsidyAccessPolicyForm(data=self.form_data)
self.assertFalse(form.is_valid())
self.assertIn('subsidy_uuid', form.errors)
self.assertEqual(form.errors['subsidy_uuid'], ['Subsidy does not exist'])

@mock.patch('enterprise_access.apps.subsidy_access_policy.admin.forms.get_versioned_subsidy_client')
def test_clean_subsidy_uuid_wrong_enterprise(self, mock_get_client):
"""
Verify that a validation error is raised when the subsidy belongs to a different enterprise customer.
"""
different_enterprise_uuid = uuid.uuid4()
mock_client = mock.MagicMock()
mock_client.retrieve_subsidy.return_value = {
'enterprise_customer_uuid': different_enterprise_uuid
}
mock_get_client.return_value = mock_client

form = SubsidyAccessPolicyForm(data=self.form_data)
self.assertFalse(form.is_valid())
self.assertIn('subsidy_uuid', form.errors)
self.assertEqual(
form.errors['subsidy_uuid'],
['Subsidy is not assigned to the same enterprise customer as the budget']
)

0 comments on commit 4ca1895

Please sign in to comment.