Skip to content

Commit

Permalink
test: [ACI-994] increase test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrii committed May 30, 2024
1 parent 8613e01 commit 61c8082
Show file tree
Hide file tree
Showing 6 changed files with 616 additions and 7 deletions.
193 changes: 189 additions & 4 deletions credentials/apps/badges/tests/test_admin_forms.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import uuid
from unittest.mock import MagicMock, patch

from django import forms
from django.contrib.sites.models import Site
from django.test import TestCase
from django.utils.translation import gettext as _
from django.test import TestCase, override_settings

from credentials.apps.badges.admin_forms import BadgePenaltyForm
from credentials.apps.badges.admin_forms import (
BadgePenaltyForm,
CredlyOrganizationAdminForm,
DataRuleExtensionsMixin,
ParentMixin,
)
from credentials.apps.badges.credly.exceptions import CredlyAPIError
from credentials.apps.badges.models import BadgeRequirement, BadgeTemplate


Expand Down Expand Up @@ -70,4 +76,183 @@ def test_clean_requirements_different_template(self):
with self.assertRaises(forms.ValidationError) as cm:
form.clean()

self.assertEqual(str(cm.exception), "['All requirements must belong to the same template.']")
self.assertEqual(
str(cm.exception), "['All requirements must belong to the same template.']"
)

@override_settings(BADGES_CONFIG={"credly": {"ORGANIZATIONS": {}}})
def test_clean(self):
form = CredlyOrganizationAdminForm()
form.cleaned_data = {
"uuid": "test_uuid",
"api_key": "test_api_key",
}

with patch(
"credentials.apps.badges.models.CredlyOrganization.get_preconfigured_organizations"
) as mock_get_orgs:
mock_get_orgs.return_value = {}

with patch(
"credentials.apps.badges.admin_forms.CredlyAPIClient"
) as mock_client:
mock_client.return_value = MagicMock()

form.clean()

mock_get_orgs.assert_called_once()
mock_client.assert_called_once_with("test_uuid", "test_api_key")

@override_settings(
BADGES_CONFIG={"credly": {"ORGANIZATIONS": {"test_uuid": "test_api_key"}}}
)
def test_clean_with_configured_organization(self):
form = CredlyOrganizationAdminForm()
form.cleaned_data = {
"uuid": "test_uuid",
"api_key": None,
}

with patch(
"credentials.apps.badges.models.CredlyOrganization.get_preconfigured_organizations"
) as mock_get_orgs:
mock_get_orgs.return_value = {"test_uuid": "test_org"}

with patch(
"credentials.apps.badges.admin_forms.CredlyAPIClient"
) as mock_client:
mock_client.return_value = MagicMock()

form.clean()

mock_get_orgs.assert_called_once()
mock_client.assert_called_once_with("test_uuid", "test_api_key")

def test_clean_with_invalid_organization(self):
form = CredlyOrganizationAdminForm()
form.cleaned_data = {
"uuid": "invalid_uuid",
"api_key": "test_api_key",
}

with patch(
"credentials.apps.badges.models.CredlyOrganization.get_preconfigured_organizations"
) as mock_get_orgs:
mock_get_orgs.return_value = {"test_uuid": "test_org"}

with self.assertRaises(forms.ValidationError) as cm:
form.clean()

self.assertIn(
"You specified an invalid authorization token.", str(cm.exception)
)

def test_clean_cannot_provide_api_key_for_configured_organization(self):
form = CredlyOrganizationAdminForm()
form.cleaned_data = {
"uuid": "test_uuid",
"api_key": "test_api_key",
}

with patch(
"credentials.apps.badges.models.CredlyOrganization.get_preconfigured_organizations"
) as mock_get_orgs:
mock_get_orgs.return_value = {"test_uuid": "test_org"}

with self.assertRaises(forms.ValidationError) as cm:
form.clean()

self.assertEqual(
str(cm.exception),
'["You can\'t provide an API key for a configured organization."]',
)

def test_ensure_organization_exists(self):
form = CredlyOrganizationAdminForm()
api_client = MagicMock()
api_client.fetch_organization.return_value = {"data": {"org_id": "test_org_id"}}

form._ensure_organization_exists(api_client)

api_client.fetch_organization.assert_called_once()
self.assertEqual(form.api_data, {"org_id": "test_org_id"})

def test_ensure_organization_exists_with_error(self):
form = CredlyOrganizationAdminForm()
api_client = MagicMock()
api_client.fetch_organization.side_effect = CredlyAPIError("API Error")

with self.assertRaises(forms.ValidationError) as cm:
form._ensure_organization_exists(api_client)

api_client.fetch_organization.assert_called_once()
self.assertEqual(str(cm.exception), "['API Error']")


class TestParentMixin(ParentMixin):
def get_form_kwargs(self, index):
return super().get_form_kwargs(index)

Check warning on line 194 in credentials/apps/badges/tests/test_admin_forms.py

View check run for this annotation

Codecov / codecov/patch

credentials/apps/badges/tests/test_admin_forms.py#L194

Added line #L194 was not covered by tests


class ParentMixinTestCase(TestCase):
def setUp(self):
self.instance = MagicMock()
self.instance.some_attribute = "some_value"

self.mixin = TestParentMixin()
self.mixin.instance = self.instance

def test_get_form_kwargs_passes_parent_instance(self):
with patch.object(
TestParentMixin,
"get_form_kwargs",
return_value={"parent_instance": self.instance},
) as super_method:
result = self.mixin.get_form_kwargs(0)

super_method.assert_called_once_with(0)

self.assertIn("parent_instance", result)
self.assertEqual(result["parent_instance"], self.instance)


class TestForm(DataRuleExtensionsMixin, forms.Form):
data_path = forms.ChoiceField(choices=[])
value = forms.CharField()


class DataRuleExtensionsMixinTestCase(TestCase):
def setUp(self):
self.parent_instance = MagicMock()
self.parent_instance.event_type = COURSE_PASSING_EVENT

def test_init_sets_choices_based_on_event_type(self):
form = TestForm(parent_instance=self.parent_instance)
self.assertEqual(
form.fields["data_path"].choices,
[("is_passing", "is_passing"), ("course.course_key", "course.course_key")],
)

def test_clean_with_valid_boolean_value(self):
form = TestForm(
data={"data_path": "is_passing", "value": "True"},
parent_instance=self.parent_instance,
)
form.is_valid()
self.assertRaises(KeyError, lambda: form.errors["__all__"])

def test_clean_with_invalid_boolean_value(self):
form = TestForm(
data={"data_path": "is_passing", "value": "invalid"},
parent_instance=self.parent_instance,
)
self.assertFalse(form.is_valid())
self.assertEqual(form.errors["__all__"], ["Value must be a boolean."])

def test_clean_with_non_boolean_data_path(self):
form = TestForm(
data={"data_path": "course.course_key", "value": "some_value"},
parent_instance=self.parent_instance,
)
form.is_valid()
self.assertRaises(KeyError, lambda: form.errors["__all__"])
136 changes: 136 additions & 0 deletions credentials/apps/badges/tests/test_api_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
from unittest import mock

from attrs import asdict
from django.test import TestCase
from faker import Faker
from openedx_events.learning.data import BadgeData, BadgeTemplateData, UserData, UserPersonalData
from requests.models import Response

from credentials.apps.badges.credly.api_client import CredlyAPIClient
from credentials.apps.badges.credly.exceptions import CredlyAPIError, CredlyError
from credentials.apps.badges.models import CredlyOrganization


class CredlyApiClientTestCase(TestCase):
def setUp(self):
fake = Faker()
self.api_client = CredlyAPIClient("test_organization_id", "test_api_key")
self.badge_data = BadgeData(
uuid=fake.uuid4(),
user=UserData(
id=1,
is_active=True,
pii=UserPersonalData(
username="test_user", email="test_email@mail.com", name="Test User"
),
),
template=BadgeTemplateData(
uuid=fake.uuid4(),
name="Test Badge",
origin="Credly",
description="Test Badge Description",
image_url="https://test.com/image.png",
),
)

def test_get_organization_nonexistent(self):
with mock.patch(
"credentials.apps.badges.credly.api_client.CredlyOrganization.objects.get"
) as mock_get:
mock_get.side_effect = CredlyOrganization.DoesNotExist
with self.assertRaises(CredlyError) as cm:
self.api_client._get_organization("nonexistent_organization_id")
self.assertEqual(
str(cm.exception),
"CredlyOrganization with the uuid nonexistent_organization_id does not exist!",
)

def test_perform_request(self):
with mock.patch(
"credentials.apps.badges.credly.api_client.requests.request"
) as mock_request:
mock_response = mock.Mock()
mock_response.json.return_value = {"key": "value"}
mock_request.return_value = mock_response
result = self.api_client.perform_request("GET", "/api/endpoint")
mock_request.assert_called_once_with(
"GET",
"https://sandbox-api.credly.com/api/endpoint",
headers=self.api_client._get_headers(),
json=None,
)
self.assertEqual(result, {"key": "value"})

def test_raise_for_error_success(self):
response = mock.Mock(spec=Response)
response.status_code = 200
self.api_client._raise_for_error(response)

def test_raise_for_error_error(self):
response = mock.Mock(spec=Response)
response.status_code = 404
response.text = "Not Found"
response.raise_for_status.side_effect = CredlyAPIError(
f"Credly API: {response.text} ({response.status_code})"
)

with self.assertRaises(CredlyAPIError) as cm:
self.api_client._raise_for_error(response)
self.assertEqual(str(cm.exception), "Credly API: Not Found (404)")

def test_fetch_organization(self):
with mock.patch.object(
CredlyAPIClient, "perform_request"
) as mock_perform_request:
mock_perform_request.return_value = {"organization": "data"}
result = self.api_client.fetch_organization()
mock_perform_request.assert_called_once_with("get", "")
self.assertEqual(result, {"organization": "data"})

def test_fetch_badge_templates(self):
with mock.patch.object(
CredlyAPIClient, "perform_request"
) as mock_perform_request:
mock_perform_request.return_value = {
"badge_templates": ["template1", "template2"]
}
result = self.api_client.fetch_badge_templates()
mock_perform_request.assert_called_once_with(
"get", "badge_templates/?filter=state::active"
)
self.assertEqual(result, {"badge_templates": ["template1", "template2"]})

def test_fetch_event_information(self):
event_id = "event123"
with mock.patch.object(
CredlyAPIClient, "perform_request"
) as mock_perform_request:
mock_perform_request.return_value = {"event": "data"}
result = self.api_client.fetch_event_information(event_id)
mock_perform_request.assert_called_once_with("get", f"events/{event_id}/")
self.assertEqual(result, {"event": "data"})

def test_issue_badge(self):
issue_badge_data = self.badge_data
with mock.patch.object(
CredlyAPIClient, "perform_request"
) as mock_perform_request:
mock_perform_request.return_value = {"badge": "issued"}
result = self.api_client.issue_badge(issue_badge_data)
mock_perform_request.assert_called_once_with(
"post", "badges/", asdict(issue_badge_data)
)
self.assertEqual(result, {"badge": "issued"})

def test_revoke_badge(self):
badge_id = "badge123"
data = {"data": "value"}
with mock.patch.object(
CredlyAPIClient, "perform_request"
) as mock_perform_request:
mock_perform_request.return_value = {"badge": "revoked"}
result = self.api_client.revoke_badge(badge_id, data)
mock_perform_request.assert_called_once_with(
"put", f"badges/{badge_id}/revoke/", data=data
)
self.assertEqual(result, {"badge": "revoked"})
Loading

0 comments on commit 61c8082

Please sign in to comment.