From a6095d10d2734a3f9bf8210602d7e2b1ff575df4 Mon Sep 17 00:00:00 2001 From: Kyrylo Kholodenko Date: Fri, 20 Dec 2024 13:24:20 +0200 Subject: [PATCH] test: [AXM-1249] add tests and fix existing tests --- .../apps/badges/accredible/api_client.py | 1 + credentials/apps/badges/issuers.py | 5 +- .../tests/accredible/test_api_client.py | 1 - .../apps/badges/tests/test_admin_forms.py | 10 +- credentials/apps/badges/tests/test_issuers.py | 163 +++++++++++++++++- credentials/apps/badges/tests/test_models.py | 50 ++++++ credentials/apps/badges/tests/test_signals.py | 2 + 7 files changed, 216 insertions(+), 16 deletions(-) diff --git a/credentials/apps/badges/accredible/api_client.py b/credentials/apps/badges/accredible/api_client.py index 213bce350..5aae5d03f 100644 --- a/credentials/apps/badges/accredible/api_client.py +++ b/credentials/apps/badges/accredible/api_client.py @@ -118,6 +118,7 @@ def sync_groups(self, site_id: int) -> int: "name": raw_group.get("course_name"), "description": raw_group.get("course_description"), "icon": self.fetch_design_image(raw_group.get("primary_design_id")), + "created": raw_group.get("created_at"), }, ) diff --git a/credentials/apps/badges/issuers.py b/credentials/apps/badges/issuers.py index 2f37d1984..51cd9b69c 100644 --- a/credentials/apps/badges/issuers.py +++ b/credentials/apps/badges/issuers.py @@ -17,7 +17,6 @@ ) from credentials.apps.badges.credly.api_client import CredlyAPIClient from credentials.apps.badges.credly.data import CredlyBadgeData -from credentials.apps.badges.credly.exceptions import CredlyAPIError from credentials.apps.badges.exceptions import BadgeProviderError from credentials.apps.badges.models import ( BadgeTemplate, @@ -246,7 +245,7 @@ def issue_accredible_badge(self, *, user_credential): ) try: - accredible_api = AccredibleAPIClient(group.api_config) + accredible_api = AccredibleAPIClient(group.api_config.id) response = accredible_api.issue_badge(accredible_badge_data) except BadgeProviderError: user_credential.state = "error" @@ -263,7 +262,7 @@ def revoke_accredible_badge(self, credential_id, user_credential): """ credential = self.get_credential(credential_id) - accredible_api_client = AccredibleAPIClient(credential.api_config) + accredible_api_client = AccredibleAPIClient(credential.api_config.id) revoke_badge_data = AccredibleExpireBadgeData( credential=AccredibleExpiredCredential(expired_on=datetime.now().strftime("%Y-%m-%d %H:%M:%S %z")) ) diff --git a/credentials/apps/badges/tests/accredible/test_api_client.py b/credentials/apps/badges/tests/accredible/test_api_client.py index 6e8a27832..7c1044c80 100644 --- a/credentials/apps/badges/tests/accredible/test_api_client.py +++ b/credentials/apps/badges/tests/accredible/test_api_client.py @@ -24,7 +24,6 @@ def setUp(self): recipient=AccredibleRecipient(name="Test name", email="test_name@test.com"), group_id=123, name="Test Badge", - description="Test Badge Description", issued_on="2021-01-01 00:00:00 +0000", complete=True, ) diff --git a/credentials/apps/badges/tests/test_admin_forms.py b/credentials/apps/badges/tests/test_admin_forms.py index 6a5914e93..1c41d4013 100644 --- a/credentials/apps/badges/tests/test_admin_forms.py +++ b/credentials/apps/badges/tests/test_admin_forms.py @@ -11,7 +11,7 @@ DataRuleExtensionsMixin, ParentMixin, ) -from credentials.apps.badges.credly.exceptions import CredlyAPIError +from credentials.apps.badges.exceptions import BadgeProviderError from credentials.apps.badges.models import BadgeRequirement, BadgeTemplate @@ -132,7 +132,7 @@ def test_clean_with_invalid_organization(self): ) as mock_get_orgs: mock_get_orgs.return_value = {"test_uuid": "test_org"} - with self.assertRaises(forms.ValidationError) as cm: + with self.assertRaises(BadgeProviderError) as cm: form.clean() self.assertIn("You specified an invalid authorization token.", str(cm.exception)) @@ -170,13 +170,13 @@ def test_ensure_organization_exists(self): def test_ensure_organization_exists_with_error(self): form = CredlyOrganizationAdminForm() api_client = MagicMock() - api_client.fetch_organization.side_effect = CredlyAPIError("API Error") + api_client.fetch_organization.side_effect = BadgeProviderError("API Error") - with self.assertRaises(forms.ValidationError) as cm: + with self.assertRaises(BadgeProviderError) as cm: form.ensure_organization_exists(api_client) api_client.fetch_organization.assert_called_once() - self.assertEqual(str(cm.exception), "['API Error']") + self.assertEqual(str(cm.exception), "API Error") class TestParentMixin(ParentMixin): diff --git a/credentials/apps/badges/tests/test_issuers.py b/credentials/apps/badges/tests/test_issuers.py index 787f2ba27..76df550bf 100644 --- a/credentials/apps/badges/tests/test_issuers.py +++ b/credentials/apps/badges/tests/test_issuers.py @@ -6,10 +6,18 @@ from django.contrib.contenttypes.models import ContentType from django.test import TestCase +from credentials.apps.badges.exceptions import BadgeProviderError +from credentials.apps.badges.accredible.api_client import AccredibleAPIClient from credentials.apps.badges.credly.api_client import CredlyAPIClient -from credentials.apps.badges.credly.exceptions import CredlyAPIError -from credentials.apps.badges.issuers import CredlyBadgeTemplateIssuer -from credentials.apps.badges.models import CredlyBadge, CredlyBadgeTemplate, CredlyOrganization +from credentials.apps.badges.issuers import CredlyBadgeTemplateIssuer, AccredibleBadgeTemplateIssuer +from credentials.apps.badges.models import ( + CredlyBadge, + CredlyBadgeTemplate, + CredlyOrganization, + AccredibleAPIConfig, + AccredibleBadge, + AccredibleGroup, +) from credentials.apps.credentials.constants import UserCredentialStatus @@ -126,10 +134,10 @@ def test_issue_credly_badge_with_error(self): # Mock the CredlyAPIClient and its issue_badge method to raise CredlyAPIError with mock.patch("credentials.apps.badges.credly.api_client.CredlyAPIClient") as mock_credly_api_client: mock_issue_badge = mock_credly_api_client.return_value.issue_badge - mock_issue_badge.side_effect = CredlyAPIError + mock_issue_badge.side_effect = BadgeProviderError # Call the issue_credly_badge method and expect CredlyAPIError to be raised - with self.assertRaises(CredlyAPIError): + with self.assertRaises(BadgeProviderError): self.issuer().issue_credly_badge(user_credential=user_credential) # Check if the user credential state is updated to "error" @@ -154,7 +162,7 @@ def test_revoke_credly_badge_success(self, mock_revoke_badge): user_credential.refresh_from_db() self.assertEqual(user_credential.state, "revoked") - @patch.object(CredlyAPIClient, "revoke_badge", side_effect=CredlyAPIError("Revocation failed")) + @patch.object(CredlyAPIClient, "revoke_badge", side_effect=BadgeProviderError("Revocation failed")) def test_revoke_credly_badge_failure(self, mock_revoke_badge): # pylint: disable=unused-argument user_credential = self.issued_user_credential_type.objects.create( username="test_user", @@ -165,8 +173,149 @@ def test_revoke_credly_badge_failure(self, mock_revoke_badge): # pylint: disabl external_uuid=self.fake.uuid4(), ) - with self.assertRaises(CredlyAPIError): + with self.assertRaises(BadgeProviderError): self.issuer().revoke_credly_badge(self.badge_template.id, user_credential) user_credential.refresh_from_db() self.assertEqual(user_credential.state, "error") + + +class AccredibleBadgeTemplateIssuerTestCase(TestCase): + issued_credential_type = AccredibleGroup + issued_user_credential_type = AccredibleBadge + issuer = AccredibleBadgeTemplateIssuer + + def setUp(self): + self.fake = faker.Faker() + self.accredible_api_config = AccredibleAPIConfig.objects.create( + api_key=self.fake.uuid4(), name=self.fake.word() + ) + self.group = self.issued_credential_type.objects.create( + origin=self.issued_credential_type.ORIGIN, + site_id=1, + uuid=self.fake.uuid4(), + name=self.fake.word(), + state="active", + api_config=self.accredible_api_config, + ) + User.objects.create_user(username="test_user", email="test_email@example.com", password="test_password") + + def _perform_request(self, method, endpoint, data=None): # pylint: disable=unused-argument + return {"credential": {"id": 123}} + + def test_create_user_credential_awarded(self): + with mock.patch("credentials.apps.badges.issuers.notify_badge_awarded") as mock_notify_badge_awarded: + with mock.patch.object(self.issuer, "issue_accredible_badge") as mock_issue_accredible_badge: + self.issuer().award(credential_id=self.group.id, username="test_user") + + mock_notify_badge_awarded.assert_called_once() + mock_issue_accredible_badge.assert_called_once() + + self.assertTrue( + self.issued_user_credential_type.objects.filter( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.group), + credential_id=self.group.id, + ).exists() + ) + + def test_create_user_credential_revoked(self): + self.issued_user_credential_type.objects.create( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.group), + credential_id=self.group.id, + state=AccredibleBadge.STATES.accepted, + uuid=self.fake.uuid4(), + external_id=123, + ) + + with mock.patch("credentials.apps.badges.issuers.notify_badge_revoked") as mock_notify_badge_revoked: + with mock.patch.object(self.issuer, "revoke_accredible_badge") as mock_revoke_accredible_badge: + self.issuer().revoke(self.group.id, "test_user") + + mock_revoke_accredible_badge.assert_called_once() + mock_notify_badge_revoked.assert_called_once() + + self.assertTrue( + self.issued_user_credential_type.objects.filter( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.group), + credential_id=self.group.id, + status=UserCredentialStatus.REVOKED, + ).exists() + ) + + @patch.object(AccredibleAPIClient, "perform_request", _perform_request) + def test_issue_accredible_badge(self): + user_credential = self.issued_user_credential_type.objects.create( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.group), + credential_id=self.group.id, + state=AccredibleBadge.STATES.accepted, + uuid=self.fake.uuid4(), + external_id=123, + ) + + self.issuer().issue_accredible_badge(user_credential=user_credential) + + self.assertIsNotNone(user_credential.external_id) + self.assertEqual(user_credential.state, AccredibleBadge.STATES.accepted) + + user_credential.refresh_from_db() + self.assertIsNotNone(user_credential.external_id) + self.assertEqual(user_credential.state, AccredibleBadge.STATES.accepted) + + def test_issue_accredible_badge_with_error(self): + user_credential = self.issued_user_credential_type.objects.create( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.group), + credential_id=self.group.id, + state=AccredibleBadge.STATES.accepted, + uuid=self.fake.uuid4(), + external_id=123, + ) + + with mock.patch("credentials.apps.badges.accredible.api_client.AccredibleAPIClient") as mock_accredible_api_client: + mock_issue_badge = mock_accredible_api_client.return_value.issue_badge + mock_issue_badge.side_effect = BadgeProviderError + + with self.assertRaises(BadgeProviderError): + self.issuer().issue_accredible_badge(user_credential=user_credential) + + user_credential.refresh_from_db() + self.assertEqual(user_credential.state, "error") + + @patch.object(AccredibleAPIClient, "revoke_badge") + def test_revoke_accredible_badge_success(self, mock_revoke_badge): + user_credential = self.issued_user_credential_type.objects.create( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.group), + credential_id=self.group.id, + state=AccredibleBadge.STATES.accepted, + uuid=self.fake.uuid4(), + external_id=123, + ) + + mock_revoke_badge.return_value = {"credential": {"id": 123}} + + self.issuer().revoke_accredible_badge(self.group.id, user_credential) + + user_credential.refresh_from_db() + self.assertEqual(user_credential.state, "expired") + + @patch.object(AccredibleAPIClient, "revoke_badge", side_effect=BadgeProviderError("Revocation failed")) + def test_revoke_accredible_badge_failure(self, mock_revoke_badge): # pylint: disable=unused-argument + user_credential = self.issued_user_credential_type.objects.create( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.group), + credential_id=self.group.id, + state=AccredibleBadge.STATES.accepted, + uuid=self.fake.uuid4(), + external_id=123, + ) + + with self.assertRaises(BadgeProviderError): + self.issuer().revoke_accredible_badge(self.group.id, user_credential) + + user_credential.refresh_from_db() + self.assertEqual(user_credential.state, "error") diff --git a/credentials/apps/badges/tests/test_models.py b/credentials/apps/badges/tests/test_models.py index e886eebee..630c675f6 100644 --- a/credentials/apps/badges/tests/test_models.py +++ b/credentials/apps/badges/tests/test_models.py @@ -21,6 +21,7 @@ PenaltyDataRule, AccredibleGroup, AccredibleAPIConfig, + AccredibleBadge, ) from credentials.apps.core.models import User @@ -772,3 +773,52 @@ def setUp(self): def test_get_all_api_config_ids(self): organization_ids = [id for id in AccredibleAPIConfig.get_all_api_config_ids()] self.assertEqual(organization_ids, [self.api_config.id]) + + +class AccredibleBadgeAsBadgeDataTestCase(TestCase): + def setUp(self): + self.user = User.objects.create_user( + username="test_user", + email="test@example.com", + full_name="Test User", + lms_user_id=1, + ) + self.site = Site.objects.create(domain="test_domain", name="test_name") + self.credential = BadgeTemplate.objects.create( + uuid=uuid.uuid4(), + origin="test_origin", + name="test_template", + description="test_description", + icon="test_icon", + site=self.site, + ) + self.badge = AccredibleBadge.objects.create( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.credential), + credential_id=self.credential.id, + state=AccredibleBadge.STATES.created, + uuid=uuid.uuid4(), + ) + + def test_as_badge_data(self): + expected_badge_data = BadgeData( + uuid=str(self.badge.uuid), + user=UserData( + pii=UserPersonalData( + username=self.user.username, + email=self.user.email, + name=self.user.get_full_name(), + ), + id=self.user.lms_user_id, + is_active=self.user.is_active, + ), + template=BadgeTemplateData( + uuid=str(self.credential.uuid), + origin=self.credential.origin, + name=self.credential.name, + description=self.credential.description, + image_url=str(self.credential.icon), + ), + ) + actual_badge_data = self.badge.as_badge_data() + self.assertEqual(actual_badge_data, expected_badge_data) diff --git a/credentials/apps/badges/tests/test_signals.py b/credentials/apps/badges/tests/test_signals.py index 0a79663b4..ed3d2b3d8 100644 --- a/credentials/apps/badges/tests/test_signals.py +++ b/credentials/apps/badges/tests/test_signals.py @@ -28,6 +28,7 @@ def test_progression_signal_emission_and_receiver_execution(self): sender=self, username="test_user", badge_template_id=self.badge_template.id, + origin=self.badge_template.origin, ) # UserCredential object @@ -51,6 +52,7 @@ def test_regression_signal_emission_and_receiver_execution(self): sender=self, username="test_user", badge_template_id=self.badge_template.id, + origin=self.badge_template.origin, ) # UserCredential object