From f5d19c919934b95ff19ca67da8ae2fc5d7c0f749 Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Tue, 12 Nov 2024 22:53:45 +0000 Subject: [PATCH] feat(models): improve EnrollmentFlow validation when the flow's agency.active=True, validate that templates exist consolidate other EnrollmentFlow.clean() tests --- benefits/core/models.py | 36 +++++++++--- tests/pytest/conftest.py | 20 ------- tests/pytest/core/test_models.py | 97 ++++++++++++++++---------------- 3 files changed, 77 insertions(+), 76 deletions(-) diff --git a/benefits/core/models.py b/benefits/core/models.py index 7c69072490..fc2b4f7dda 100644 --- a/benefits/core/models.py +++ b/benefits/core/models.py @@ -604,13 +604,13 @@ def enrollment_success_template(self): return self.enrollment_success_template_override or f"{prefix}--{self.agency_card_name}.html" def clean(self): - supports_expiration = self.supports_expiration - expiration_days = self.expiration_days - expiration_reenrollment_days = self.expiration_reenrollment_days - reenrollment_error_template = self.reenrollment_error_template + errors = {} + + if self.supports_expiration: + expiration_days = self.expiration_days + expiration_reenrollment_days = self.expiration_reenrollment_days + reenrollment_error_template = self.reenrollment_error_template - if supports_expiration: - errors = {} message = "When support_expiration is True, this value must be greater than 0." if expiration_days is None or expiration_days <= 0: errors.update(expiration_days=ValidationError(message)) @@ -619,8 +619,28 @@ def clean(self): if reenrollment_error_template is None: errors.update(reenrollment_error_template=ValidationError("Required when supports expiration is True.")) - if errors: - raise ValidationError(errors) + if errors: + raise ValidationError(errors) + + if self.transit_agency and self.transit_agency.active: + self.transit_agency.clean() + + templates = [ + self.selection_label_template, + self.eligibility_start_template, + self.eligibility_unverified_template, + self.enrollment_index_template, + self.enrollment_success_template, + ] + if self.supports_expiration: + templates.append(self.reenrollment_error_template) + + # since templates are calculated from the pattern or the override field + # we can't add a field-level validation error + # so just raise directly for a missing template + for t in templates: + if not template_path(t): + raise ValidationError(f"Template not found: {t}") def eligibility_form_instance(self, *args, **kwargs): """Return an instance of this flow's EligibilityForm, or None.""" diff --git a/tests/pytest/conftest.py b/tests/pytest/conftest.py index 97d84c8c13..5bfb7f56c7 100644 --- a/tests/pytest/conftest.py +++ b/tests/pytest/conftest.py @@ -132,26 +132,6 @@ def model_EnrollmentFlow_does_not_support_expiration(model_EnrollmentFlow): return model_EnrollmentFlow -@pytest.fixture -def model_EnrollmentFlow_zero_expiration_days(model_EnrollmentFlow): - model_EnrollmentFlow.supports_expiration = True - model_EnrollmentFlow.expiration_days = 0 - model_EnrollmentFlow.expiration_reenrollment_days = 14 - model_EnrollmentFlow.save() - - return model_EnrollmentFlow - - -@pytest.fixture -def model_EnrollmentFlow_zero_expiration_reenrollment_days(model_EnrollmentFlow): - model_EnrollmentFlow.supports_expiration = True - model_EnrollmentFlow.expiration_days = 14 - model_EnrollmentFlow.expiration_reenrollment_days = 0 - model_EnrollmentFlow.save() - - return model_EnrollmentFlow - - @pytest.fixture def model_EnrollmentFlow_supports_expiration(model_EnrollmentFlow): model_EnrollmentFlow.supports_expiration = True diff --git a/tests/pytest/core/test_models.py b/tests/pytest/core/test_models.py index 8916109339..9d70656f8b 100644 --- a/tests/pytest/core/test_models.py +++ b/tests/pytest/core/test_models.py @@ -162,54 +162,6 @@ def test_EnrollmentFlow_agency_card_name__claims(model_EnrollmentFlow_with_scope assert model_EnrollmentFlow_with_scope_and_claim.agency_card_name == "" -@pytest.mark.django_db -def test_EnrollmentFlow_supports_expiration_False(model_EnrollmentFlow, model_EnrollmentFlow_does_not_support_expiration): - # test will fail if any error is raised - model_EnrollmentFlow_does_not_support_expiration.full_clean() - - -@pytest.mark.django_db -def test_EnrollmentFlow_zero_expiration_days(model_EnrollmentFlow_zero_expiration_days): - with pytest.raises(ValidationError) as exception_info: - model_EnrollmentFlow_zero_expiration_days.full_clean() - - error_dict = exception_info.value.error_dict - assert len(error_dict["expiration_days"]) == 1 - assert error_dict["expiration_days"][0].message == "When support_expiration is True, this value must be greater than 0." - - -@pytest.mark.django_db -def test_EnrollmentFlow_zero_expiration_reenrollment_days(model_EnrollmentFlow_zero_expiration_reenrollment_days): - with pytest.raises(ValidationError) as exception_info: - model_EnrollmentFlow_zero_expiration_reenrollment_days.full_clean() - - error_dict = exception_info.value.error_dict - assert len(error_dict["expiration_reenrollment_days"]) == 1 - assert ( - error_dict["expiration_reenrollment_days"][0].message - == "When support_expiration is True, this value must be greater than 0." - ) - - -@pytest.mark.django_db -def test_EnrollmentFlow_missing_reenrollment_template(model_EnrollmentFlow_supports_expiration): - model_EnrollmentFlow_supports_expiration.reenrollment_error_template = None - model_EnrollmentFlow_supports_expiration.save() - - with pytest.raises(ValidationError) as exception_info: - model_EnrollmentFlow_supports_expiration.full_clean() - - error_dict = exception_info.value.error_dict - assert len(error_dict["reenrollment_error_template"]) == 1 - assert error_dict["reenrollment_error_template"][0].message == "Required when supports expiration is True." - - -@pytest.mark.django_db -def test_EnrollmentFlow_supports_expiration(model_EnrollmentFlow_supports_expiration): - # test will fail if any error is raised - model_EnrollmentFlow_supports_expiration.full_clean() - - @pytest.mark.django_db def test_EnrollmentFlow_enrollment_index_template(model_EnrollmentFlow_with_scope_and_claim): assert model_EnrollmentFlow_with_scope_and_claim.enrollment_index_template == "enrollment/index.html" @@ -422,6 +374,55 @@ def test_EnrollmentFlow_template_overrides_eligibility_api(model_EnrollmentFlow_ ) +@pytest.mark.django_db +def test_EnrollmentFlow_clean_does_not_supports_expiration(model_EnrollmentFlow_does_not_support_expiration): + # test will fail if any error is raised + model_EnrollmentFlow_does_not_support_expiration.clean() + + +@pytest.mark.django_db +def test_EnrollmentFlow_clean_supports_expiration(model_EnrollmentFlow_supports_expiration): + # test will fail if any error is raised + model_EnrollmentFlow_supports_expiration.clean() + + model_EnrollmentFlow_supports_expiration.expiration_days = 0 + model_EnrollmentFlow_supports_expiration.expiration_reenrollment_days = 0 + model_EnrollmentFlow_supports_expiration.reenrollment_error_template = None + + with pytest.raises(ValidationError) as e: + model_EnrollmentFlow_supports_expiration.clean() + + assert "expiration_days" in e.value.error_dict + assert "expiration_reenrollment_days" in e.value.error_dict + assert "reenrollment_error_template" in e.value.error_dict + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "template_attribute", + [ + "selection_label_template_override", + "eligibility_start_template_override", + "eligibility_unverified_template_override", + "enrollment_index_template_override", + "enrollment_success_template_override", + "reenrollment_error_template", + ], +) +def test_EnrollmentFlow_clean_templates( + model_EnrollmentFlow_supports_expiration, model_TransitAgency_inactive, template_attribute +): + model_EnrollmentFlow_supports_expiration.transit_agency = model_TransitAgency_inactive + setattr(model_EnrollmentFlow_supports_expiration, template_attribute, "does/not/exist.html") + # agency is inactive, OK to have bad template fields + model_EnrollmentFlow_supports_expiration.clean() + + # now mark it active and expect failure on clean() + model_TransitAgency_inactive.active = True + with pytest.raises(ValidationError, match="Template not found: does/not/exist.html"): + model_EnrollmentFlow_supports_expiration.clean() + + @pytest.mark.django_db def test_TransitProcessor_str(model_TransitProcessor): assert str(model_TransitProcessor) == model_TransitProcessor.name