From aa4e142c572a3dda3f163333cb5b458f5ed8b443 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Thu, 16 Jan 2025 15:22:44 +0000 Subject: [PATCH 1/8] Display all destination flags on case detail page So that we have parity with the flags displayed on queue view vs case detail page. --- api/applications/tests/factories.py | 13 +++- api/cases/libraries/get_flags.py | 12 ++-- api/cases/tests/conftest.py | 26 +++++-- api/cases/tests/test_case_flags.py | 107 ++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 api/cases/tests/test_case_flags.py diff --git a/api/applications/tests/factories.py b/api/applications/tests/factories.py index acfc2d0f86..0a08a3329c 100644 --- a/api/applications/tests/factories.py +++ b/api/applications/tests/factories.py @@ -20,7 +20,13 @@ from api.documents.tests.factories import DocumentFactory from api.goods.tests.factories import GoodFactory from api.organisations.tests.factories import OrganisationFactory, SiteFactory, ExternalLocationFactory -from api.parties.tests.factories import ConsigneeFactory, EndUserFactory, PartyFactory, ThirdPartyFactory +from api.parties.tests.factories import ( + ConsigneeFactory, + EndUserFactory, + PartyFactory, + PartyDocumentFactory, + ThirdPartyFactory, +) from api.users.tests.factories import ExporterUserFactory, GovUserFactory from api.staticdata.units.enums import Units from api.staticdata.control_list_entries.helpers import get_control_list_entry @@ -228,6 +234,11 @@ def _create(cls, model_class, *args, **kwargs): GoodOnApplicationFactory(application=obj, good=GoodFactory(organisation=obj.organisation)) PartyOnApplicationFactory(application=obj, party=EndUserFactory(organisation=obj.organisation)) + PartyDocumentFactory( + party=obj.end_user.party, + s3_key="party-document", + safe=True, + ) if kwargs["goods_recipients"] in [ StandardApplication.VIA_CONSIGNEE, diff --git a/api/cases/libraries/get_flags.py b/api/cases/libraries/get_flags.py index ccdfe7ac65..833a31a53d 100644 --- a/api/cases/libraries/get_flags.py +++ b/api/cases/libraries/get_flags.py @@ -1,4 +1,4 @@ -from django.db.models import QuerySet, When, Case as DB_Case, IntegerField, BinaryField +from django.db.models import Q, QuerySet, When, Case as DB_Case, IntegerField, BinaryField from api.cases.enums import CaseTypeSubTypeEnum from api.cases.models import Case @@ -27,10 +27,14 @@ def get_destination_flags(case, case_type): if case_type == CaseTypeSubTypeEnum.EUA: return Flag.objects.filter(parties__parties_on_application__application_id=case.id) elif case_type == CaseTypeSubTypeEnum.STANDARD: - return Flag.objects.filter( - parties__parties_on_application__application_id=case.id, - parties__parties_on_application__deleted_at__isnull=True, + flags = Flag.objects.filter( + ( + Q(parties__parties_on_application__application_id=case.id) + & Q(parties__parties_on_application__deleted_at__isnull=True) + ) + | (Q(parties_on_application__application__pk=case.id) & Q(parties_on_application__deleted_at__isnull=True)) ) + return flags return Flag.objects.none() diff --git a/api/cases/tests/conftest.py b/api/cases/tests/conftest.py index 70b1362335..fcff81de9d 100644 --- a/api/cases/tests/conftest.py +++ b/api/cases/tests/conftest.py @@ -4,13 +4,14 @@ from rest_framework.test import APIClient from api.applications.tests.factories import DraftStandardApplicationFactory -from api.core.constants import ExporterPermissions, GovPermissions +from api.core.constants import ExporterPermissions, GovPermissions, Roles from api.organisations.tests.factories import OrganisationFactory from api.parties.tests.factories import PartyDocumentFactory from api.users.libraries.user_to_token import user_to_token -from api.users.models import Permission -from api.users.enums import UserType +from api.users.models import BaseUser, Permission, Role +from api.users.enums import SystemUser, UserType from api.users.tests.factories import ( + BaseUserFactory, ExporterUserFactory, GovUserFactory, RoleFactory, @@ -52,6 +53,14 @@ def exporter_headers(exporter_user, organisation): } +@pytest.fixture(autouse=True) +def system_user(): + if BaseUser.objects.filter(id=SystemUser.id).exists(): + return BaseUser.objects.get(id=SystemUser.id) + else: + return BaseUserFactory(id=SystemUser.id) + + @pytest.fixture() def gov_headers(gov_user): return {"HTTP_GOV_USER_TOKEN": user_to_token(gov_user.baseuser_ptr)} @@ -59,7 +68,16 @@ def gov_headers(gov_user): @pytest.fixture() def gov_user(): - return GovUserFactory() + gov_user = GovUserFactory() + if Role.objects.filter(id=Roles.INTERNAL_DEFAULT_ROLE_ID, type=UserType.INTERNAL.value).exists(): + return gov_user + + gov_user.role = RoleFactory( + id=Roles.INTERNAL_DEFAULT_ROLE_ID, type=UserType.INTERNAL.value, name=Roles.INTERNAL_DEFAULT_ROLE_NAME + ) + gov_user.save() + + return gov_user @pytest.fixture() diff --git a/api/cases/tests/test_case_flags.py b/api/cases/tests/test_case_flags.py new file mode 100644 index 0000000000..b09d8775f6 --- /dev/null +++ b/api/cases/tests/test_case_flags.py @@ -0,0 +1,107 @@ +import pytest + +from django.urls import reverse +from urllib import parse + +from api.applications.models import StandardApplication +from api.applications.tests.factories import DraftStandardApplicationFactory +from api.cases.models import Case +from api.flags.enums import FlagLevels +from api.queues.constants import ALL_CASES_QUEUE_ID + +from lite_routing.routing_rules_internal.enums import FlagsEnum + +pytestmark = pytest.mark.django_db + + +@pytest.fixture +def all_cases_queue_url(): + query_params = {"queue_id": ALL_CASES_QUEUE_ID} + return f"{reverse('cases:search')}?{parse.urlencode(query_params, doseq=True)}" + + +@pytest.fixture +def standard_case(organisation, submit_application): + draft = DraftStandardApplicationFactory(organisation=organisation) + application = submit_application(draft) + return Case.objects.get(id=application.id) + + +@pytest.fixture +def case_with_flags(standard_case): + def _case_with_flags(flags_data): + case_flags = flags_data.get(FlagLevels.CASE, []) + standard_case.flags.add(*case_flags) + + application = StandardApplication.objects.get(id=standard_case.id) + good_flags = flags_data.get(FlagLevels.GOOD, []) + for good_on_application in application.goods.all(): + good_on_application.good.flags.add(*good_flags) + + destination_flags = flags_data.get(FlagLevels.DESTINATION, []) + party_on_application_flags = flags_data.get(FlagLevels.PARTY_ON_APPLICATION, []) + for party_on_application in application.parties.all(): + party_on_application.party.flags.add(*destination_flags) + party_on_application.flags.add(*party_on_application_flags) + + return standard_case + + return _case_with_flags + + +@pytest.mark.parametrize( + "flags_data", + ( + {FlagLevels.CASE: [FlagsEnum.UNSCR_OFSI_SANCTIONS]}, + {FlagLevels.GOOD: [FlagsEnum.SMALL_ARMS, FlagsEnum.UK_DUAL_USE_SCH3]}, + {FlagLevels.DESTINATION: [FlagsEnum.OIL_AND_GAS_ID]}, + {FlagLevels.PARTY_ON_APPLICATION: [FlagsEnum.SANCTION_UK_MATCH, FlagsEnum.SANCTION_OFSI_MATCH]}, + ), +) +def test_queue_view_case_flags( + api_client, + all_cases_queue_url, + case_with_flags, + gov_headers, + flags_data, +): + case = case_with_flags(flags_data) + + response = api_client.get(all_cases_queue_url, **gov_headers) + assert response.status_code == 200 + + for case in response.json()["results"]["cases"]: + all_flags = [ + item["id"] for flag_level in ["flags", "goods_flags", "destinations_flags"] for item in case[flag_level] + ] + + for flags in flags_data.values(): + assert set(flags).issubset(set(all_flags)) + + +@pytest.mark.parametrize( + "flags_data", + ( + {FlagLevels.CASE: [FlagsEnum.UNSCR_OFSI_SANCTIONS]}, + {FlagLevels.GOOD: [FlagsEnum.SMALL_ARMS, FlagsEnum.UK_DUAL_USE_SCH3]}, + {FlagLevels.DESTINATION: [FlagsEnum.OIL_AND_GAS_ID]}, + {FlagLevels.PARTY_ON_APPLICATION: [FlagsEnum.SANCTION_UK_MATCH, FlagsEnum.SANCTION_OFSI_MATCH]}, + ), +) +def test_case_detail_flags( + api_client, + case_with_flags, + gov_headers, + flags_data, +): + case = case_with_flags(flags_data) + + url = reverse("cases:case", kwargs={"pk": case.id}) + response = api_client.get(url, **gov_headers) + assert response.status_code == 200 + + response = response.json() + all_flags = [item["id"] for item in response["case"]["all_flags"]] + + for flags in flags_data.values(): + assert set(flags).issubset(set(all_flags)) From 9f8179b14c8b03ee38bf382858a572c738cdf986 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Fri, 17 Jan 2025 15:30:40 +0000 Subject: [PATCH 2/8] Use factory to add necessary flags to case --- api/applications/tests/factories.py | 30 +++++++++++++++--- api/cases/tests/test_case_flags.py | 49 +++++++---------------------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/api/applications/tests/factories.py b/api/applications/tests/factories.py index 0a08a3329c..6e816f6b62 100644 --- a/api/applications/tests/factories.py +++ b/api/applications/tests/factories.py @@ -2,6 +2,9 @@ import factory.fuzzy from faker import Faker +from django.utils import timezone + + from api.applications.enums import ApplicationExportType, ApplicationExportLicenceOfficialType from api.applications.models import ( ApplicationDocument, @@ -18,6 +21,7 @@ from api.cases.models import Advice from api.external_data.models import Denial, DenialEntity, SanctionMatch from api.documents.tests.factories import DocumentFactory +from api.flags.enums import FlagLevels from api.goods.tests.factories import GoodFactory from api.organisations.tests.factories import OrganisationFactory, SiteFactory, ExternalLocationFactory from api.parties.tests.factories import ( @@ -65,11 +69,29 @@ class Meta: @classmethod def _create(cls, model_class, *args, **kwargs): - obj = model_class(*args, **kwargs) + flags = kwargs.pop("flags", {}) + application = model_class(*args, **kwargs) if "status" not in kwargs: - obj.status = get_case_status_by_status(CaseStatusEnum.SUBMITTED) - obj.save() - return obj + application.status = get_case_status_by_status(CaseStatusEnum.SUBMITTED) + + application.submitted_at = timezone.now() + application.save() + + if flags: + case_flags = flags.get(FlagLevels.CASE, []) + application.case_ptr.flags.add(*case_flags) + + good_flags = flags.get(FlagLevels.GOOD, []) + for good_on_application in application.goods.all(): + good_on_application.good.flags.add(*good_flags) + + destination_flags = flags.get(FlagLevels.DESTINATION, []) + party_on_application_flags = flags.get(FlagLevels.PARTY_ON_APPLICATION, []) + for party_on_application in application.parties.all(): + party_on_application.party.flags.add(*destination_flags) + party_on_application.flags.add(*party_on_application_flags) + + return application class PartyOnApplicationFactory(factory.django.DjangoModelFactory): diff --git a/api/cases/tests/test_case_flags.py b/api/cases/tests/test_case_flags.py index b09d8775f6..911fce362f 100644 --- a/api/cases/tests/test_case_flags.py +++ b/api/cases/tests/test_case_flags.py @@ -3,9 +3,7 @@ from django.urls import reverse from urllib import parse -from api.applications.models import StandardApplication -from api.applications.tests.factories import DraftStandardApplicationFactory -from api.cases.models import Case +from api.applications.tests.factories import StandardApplicationFactory from api.flags.enums import FlagLevels from api.queues.constants import ALL_CASES_QUEUE_ID @@ -20,35 +18,6 @@ def all_cases_queue_url(): return f"{reverse('cases:search')}?{parse.urlencode(query_params, doseq=True)}" -@pytest.fixture -def standard_case(organisation, submit_application): - draft = DraftStandardApplicationFactory(organisation=organisation) - application = submit_application(draft) - return Case.objects.get(id=application.id) - - -@pytest.fixture -def case_with_flags(standard_case): - def _case_with_flags(flags_data): - case_flags = flags_data.get(FlagLevels.CASE, []) - standard_case.flags.add(*case_flags) - - application = StandardApplication.objects.get(id=standard_case.id) - good_flags = flags_data.get(FlagLevels.GOOD, []) - for good_on_application in application.goods.all(): - good_on_application.good.flags.add(*good_flags) - - destination_flags = flags_data.get(FlagLevels.DESTINATION, []) - party_on_application_flags = flags_data.get(FlagLevels.PARTY_ON_APPLICATION, []) - for party_on_application in application.parties.all(): - party_on_application.party.flags.add(*destination_flags) - party_on_application.flags.add(*party_on_application_flags) - - return standard_case - - return _case_with_flags - - @pytest.mark.parametrize( "flags_data", ( @@ -61,11 +30,10 @@ def _case_with_flags(flags_data): def test_queue_view_case_flags( api_client, all_cases_queue_url, - case_with_flags, gov_headers, flags_data, ): - case = case_with_flags(flags_data) + StandardApplicationFactory(flags=flags_data) response = api_client.get(all_cases_queue_url, **gov_headers) assert response.status_code == 200 @@ -75,8 +43,11 @@ def test_queue_view_case_flags( item["id"] for flag_level in ["flags", "goods_flags", "destinations_flags"] for item in case[flag_level] ] + expected_flags = [] for flags in flags_data.values(): - assert set(flags).issubset(set(all_flags)) + expected_flags.extend(flags) + + assert set(all_flags).difference(set(expected_flags)) == set() @pytest.mark.parametrize( @@ -90,11 +61,10 @@ def test_queue_view_case_flags( ) def test_case_detail_flags( api_client, - case_with_flags, gov_headers, flags_data, ): - case = case_with_flags(flags_data) + case = StandardApplicationFactory(flags=flags_data) url = reverse("cases:case", kwargs={"pk": case.id}) response = api_client.get(url, **gov_headers) @@ -103,5 +73,8 @@ def test_case_detail_flags( response = response.json() all_flags = [item["id"] for item in response["case"]["all_flags"]] + expected_flags = [] for flags in flags_data.values(): - assert set(flags).issubset(set(all_flags)) + expected_flags.extend(flags) + + assert set(all_flags).difference(set(expected_flags)) == set() From b5a0f8d16b44f65726c00e21fcfa6124945d5d9c Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Fri, 17 Jan 2025 16:24:28 +0000 Subject: [PATCH 3/8] Add comment explaining that flags can be attached to PartyOnApplication --- api/cases/libraries/get_flags.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/cases/libraries/get_flags.py b/api/cases/libraries/get_flags.py index 833a31a53d..a1169daf24 100644 --- a/api/cases/libraries/get_flags.py +++ b/api/cases/libraries/get_flags.py @@ -27,6 +27,8 @@ def get_destination_flags(case, case_type): if case_type == CaseTypeSubTypeEnum.EUA: return Flag.objects.filter(parties__parties_on_application__application_id=case.id) elif case_type == CaseTypeSubTypeEnum.STANDARD: + # Usually destination level flags are attached to `Party` but some of the flags + # are attached to `PartyOnApplication` so gather all of them flags = Flag.objects.filter( ( Q(parties__parties_on_application__application_id=case.id) From a6dd9da1b4dd503df048e940e7c15e955cb28e68 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Fri, 17 Jan 2025 16:34:09 +0000 Subject: [PATCH 4/8] Create StandardSubmittedApplicationFactory for flags tests For these tests we want an application to be in submitted state without actually submitting (via API call) as that would trigger flagging rules and alter the flags applied. We cannot use DraftStandardApplicationFactory() as its purpose is to give a draft application. StandardApplicationFactory() can be updated but no products attached at this stage. A new factory is created for this purpose which has products and provided flags attached. When changes are saved then post_save signal can still trigger flagging rules hence this is mocked in the test. --- api/applications/tests/factories.py | 54 +++++++++++++++++------------ api/cases/tests/test_case_flags.py | 24 +++++++++---- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/api/applications/tests/factories.py b/api/applications/tests/factories.py index 6e816f6b62..ca9c76d042 100644 --- a/api/applications/tests/factories.py +++ b/api/applications/tests/factories.py @@ -1,10 +1,10 @@ import factory import factory.fuzzy + from faker import Faker from django.utils import timezone - from api.applications.enums import ApplicationExportType, ApplicationExportLicenceOfficialType from api.applications.models import ( ApplicationDocument, @@ -69,29 +69,12 @@ class Meta: @classmethod def _create(cls, model_class, *args, **kwargs): - flags = kwargs.pop("flags", {}) - application = model_class(*args, **kwargs) + obj = model_class(*args, **kwargs) if "status" not in kwargs: - application.status = get_case_status_by_status(CaseStatusEnum.SUBMITTED) - - application.submitted_at = timezone.now() - application.save() - - if flags: - case_flags = flags.get(FlagLevels.CASE, []) - application.case_ptr.flags.add(*case_flags) - - good_flags = flags.get(FlagLevels.GOOD, []) - for good_on_application in application.goods.all(): - good_on_application.good.flags.add(*good_flags) - - destination_flags = flags.get(FlagLevels.DESTINATION, []) - party_on_application_flags = flags.get(FlagLevels.PARTY_ON_APPLICATION, []) - for party_on_application in application.parties.all(): - party_on_application.party.flags.add(*destination_flags) - party_on_application.flags.add(*party_on_application_flags) + obj.status = get_case_status_by_status(CaseStatusEnum.SUBMITTED) - return application + obj.save() + return obj class PartyOnApplicationFactory(factory.django.DjangoModelFactory): @@ -295,3 +278,30 @@ def _create(cls, model_class, *args, **kwargs): AdviceFactory(case=obj, level=AdviceLevel.FINAL) return obj + + +class StandardSubmittedApplicationFactory(DraftStandardApplicationFactory): + + @classmethod + def _create(cls, model_class, *args, **kwargs): + flags = kwargs.pop("flags", {}) + application = super()._create(model_class, *args, **kwargs) + application.status = get_case_status_by_status(CaseStatusEnum.SUBMITTED) + application.submitted_at = timezone.now() + application.save() + + if flags: + case_flags = flags.get(FlagLevels.CASE, []) + application.case_ptr.flags.add(*case_flags) + + good_flags = flags.get(FlagLevels.GOOD, []) + for good_on_application in application.goods.all(): + good_on_application.good.flags.add(*good_flags) + + destination_flags = flags.get(FlagLevels.DESTINATION, []) + party_on_application_flags = flags.get(FlagLevels.PARTY_ON_APPLICATION, []) + for party_on_application in application.parties.all(): + party_on_application.party.flags.add(*destination_flags) + party_on_application.flags.add(*party_on_application_flags) + + return application diff --git a/api/cases/tests/test_case_flags.py b/api/cases/tests/test_case_flags.py index 911fce362f..c7ba725b2d 100644 --- a/api/cases/tests/test_case_flags.py +++ b/api/cases/tests/test_case_flags.py @@ -3,7 +3,7 @@ from django.urls import reverse from urllib import parse -from api.applications.tests.factories import StandardApplicationFactory +from api.applications.tests.factories import StandardSubmittedApplicationFactory from api.flags.enums import FlagLevels from api.queues.constants import ALL_CASES_QUEUE_ID @@ -31,9 +31,13 @@ def test_queue_view_case_flags( api_client, all_cases_queue_url, gov_headers, + mocker, flags_data, ): - StandardApplicationFactory(flags=flags_data) + # When changes are saved in factory then post_save() signal can trigger flagging rules + # and alter flags applied which is not desirable in these tests hence mock that function. + mocker.patch("api.cases.signals.apply_flagging_rules_to_case", return_value=None) + StandardSubmittedApplicationFactory(flags=flags_data) response = api_client.get(all_cases_queue_url, **gov_headers) assert response.status_code == 200 @@ -53,18 +57,26 @@ def test_queue_view_case_flags( @pytest.mark.parametrize( "flags_data", ( - {FlagLevels.CASE: [FlagsEnum.UNSCR_OFSI_SANCTIONS]}, + { + FlagLevels.CASE: [FlagsEnum.UNSCR_OFSI_SANCTIONS], + FlagLevels.GOOD: [FlagsEnum.DUAL_USE_ANNEX_1], + }, {FlagLevels.GOOD: [FlagsEnum.SMALL_ARMS, FlagsEnum.UK_DUAL_USE_SCH3]}, {FlagLevels.DESTINATION: [FlagsEnum.OIL_AND_GAS_ID]}, - {FlagLevels.PARTY_ON_APPLICATION: [FlagsEnum.SANCTION_UK_MATCH, FlagsEnum.SANCTION_OFSI_MATCH]}, + { + FlagLevels.CASE: [FlagsEnum.GOODS_NOT_LISTED], + FlagLevels.PARTY_ON_APPLICATION: [FlagsEnum.SANCTION_UK_MATCH, FlagsEnum.SANCTION_OFSI_MATCH], + }, ), ) def test_case_detail_flags( api_client, gov_headers, + mocker, flags_data, ): - case = StandardApplicationFactory(flags=flags_data) + mocker.patch("api.cases.signals.apply_flagging_rules_to_case", return_value=None) + case = StandardSubmittedApplicationFactory(flags=flags_data) url = reverse("cases:case", kwargs={"pk": case.id}) response = api_client.get(url, **gov_headers) @@ -77,4 +89,4 @@ def test_case_detail_flags( for flags in flags_data.values(): expected_flags.extend(flags) - assert set(all_flags).difference(set(expected_flags)) == set() + assert sorted(all_flags) == sorted(expected_flags) From cc2255eb0d9e1b3966e91c8704f044b9551ffaac Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Sun, 19 Jan 2025 21:21:01 +0000 Subject: [PATCH 5/8] Ensure SystemUser is only created once --- api/cases/tests/conftest.py | 6 ++---- api/users/tests/factories.py | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/api/cases/tests/conftest.py b/api/cases/tests/conftest.py index fcff81de9d..3cf94149f1 100644 --- a/api/cases/tests/conftest.py +++ b/api/cases/tests/conftest.py @@ -15,6 +15,7 @@ ExporterUserFactory, GovUserFactory, RoleFactory, + SystemUserFactory, UserOrganisationRelationshipFactory, ) @@ -55,10 +56,7 @@ def exporter_headers(exporter_user, organisation): @pytest.fixture(autouse=True) def system_user(): - if BaseUser.objects.filter(id=SystemUser.id).exists(): - return BaseUser.objects.get(id=SystemUser.id) - else: - return BaseUserFactory(id=SystemUser.id) + return SystemUserFactory() @pytest.fixture() diff --git a/api/users/tests/factories.py b/api/users/tests/factories.py index 05d319ff1a..589c6ef1ee 100644 --- a/api/users/tests/factories.py +++ b/api/users/tests/factories.py @@ -3,7 +3,7 @@ from api.organisations.tests.factories import OrganisationFactory from api.users import models -from api.users.enums import UserType, UserStatuses +from api.users.enums import SystemUser, UserType, UserStatuses from api.users.models import Role, UserOrganisationRelationship from api.teams.tests.factories import TeamFactory @@ -19,6 +19,22 @@ class Meta: model = models.BaseUser +class SystemUserFactory(factory.django.DjangoModelFactory): + first_name = factory.Faker("first_name") + last_name = factory.Faker("last_name") + email = factory.LazyAttribute(lambda n: faker.unique.email()) + + class Meta: + model = models.BaseUser + django_get_or_create = ( + "id", + "type", + ) + + id = SystemUser.id + type = UserType.SYSTEM + + class GovUserFactory(factory.django.DjangoModelFactory): baseuser_ptr = factory.SubFactory(BaseUserFactory, type=UserType.INTERNAL) team = factory.SubFactory(TeamFactory) From c93458a622688c0ea77599f4ffde73c05166f656 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Sun, 19 Jan 2025 21:38:05 +0000 Subject: [PATCH 6/8] Update assertion in the test --- api/cases/tests/test_case_flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/cases/tests/test_case_flags.py b/api/cases/tests/test_case_flags.py index c7ba725b2d..718d79a9b9 100644 --- a/api/cases/tests/test_case_flags.py +++ b/api/cases/tests/test_case_flags.py @@ -51,7 +51,7 @@ def test_queue_view_case_flags( for flags in flags_data.values(): expected_flags.extend(flags) - assert set(all_flags).difference(set(expected_flags)) == set() + assert sorted(all_flags) == sorted(expected_flags) @pytest.mark.parametrize( From 8b192985d04633b14e8cb93708765210263a7513 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Mon, 20 Jan 2025 07:04:16 +0000 Subject: [PATCH 7/8] Add missing pytestmark --- api/cases/tests/conftest.py | 5 ++--- api/cases/tests/test_helpers.py | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/api/cases/tests/conftest.py b/api/cases/tests/conftest.py index 3cf94149f1..ae2fe655ad 100644 --- a/api/cases/tests/conftest.py +++ b/api/cases/tests/conftest.py @@ -8,10 +8,9 @@ from api.organisations.tests.factories import OrganisationFactory from api.parties.tests.factories import PartyDocumentFactory from api.users.libraries.user_to_token import user_to_token -from api.users.models import BaseUser, Permission, Role -from api.users.enums import SystemUser, UserType +from api.users.models import Permission, Role +from api.users.enums import UserType from api.users.tests.factories import ( - BaseUserFactory, ExporterUserFactory, GovUserFactory, RoleFactory, diff --git a/api/cases/tests/test_helpers.py b/api/cases/tests/test_helpers.py index f85b5e651a..f28b824952 100644 --- a/api/cases/tests/test_helpers.py +++ b/api/cases/tests/test_helpers.py @@ -1,10 +1,14 @@ # TODO; test notify_ecju_query in total isolation import datetime +import pytest + from parameterized import parameterized from api.cases.helpers import working_days_in_range +pytestmark = pytest.mark.django_db + @parameterized.expand( [ From 3949c261286d7b146e64c41a11ad3f37ec1769ae Mon Sep 17 00:00:00 2001 From: Tomos Williams Date: Tue, 21 Jan 2025 16:05:21 +0000 Subject: [PATCH 8/8] updated denial_reason descritpion, removing Military End Use Control. from the end of the text. --- .../0007_criterion_1_description_update.py | 19 +++++++++++ ...est_0007_criterion_1_description_update.py | 32 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 api/staticdata/denial_reasons/migrations/0007_criterion_1_description_update.py create mode 100644 api/staticdata/denial_reasons/migrations/tests/test_0007_criterion_1_description_update.py diff --git a/api/staticdata/denial_reasons/migrations/0007_criterion_1_description_update.py b/api/staticdata/denial_reasons/migrations/0007_criterion_1_description_update.py new file mode 100644 index 0000000000..b317a3f709 --- /dev/null +++ b/api/staticdata/denial_reasons/migrations/0007_criterion_1_description_update.py @@ -0,0 +1,19 @@ +from django.db import migrations + + +def update_denial_reason(apps, schema_editor): + DenialReason = apps.get_model("denial_reasons", "DenialReason") + denial_reason = DenialReason.objects.get(id=1) + if denial_reason: + denial_reason.description = "Respect for the UK's international obligations and commitments, in particular sanctions adopted by the UN Security Council, agreements on non-proliferation and other subjects, as well as other international obligations." + denial_reason.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("denial_reasons", "0006_populate_uuid_field"), + ] + + operations = [ + migrations.RunPython(update_denial_reason, migrations.RunPython.noop), + ] diff --git a/api/staticdata/denial_reasons/migrations/tests/test_0007_criterion_1_description_update.py b/api/staticdata/denial_reasons/migrations/tests/test_0007_criterion_1_description_update.py new file mode 100644 index 0000000000..319b2848d4 --- /dev/null +++ b/api/staticdata/denial_reasons/migrations/tests/test_0007_criterion_1_description_update.py @@ -0,0 +1,32 @@ +import pytest + +from django_test_migrations.migrator import Migrator + +from api.staticdata.denial_reasons.constants import DENIAL_REASON_ID_TO_UUID_MAP + + +@pytest.mark.django_db() +def test_populate_uuid_field(): + migrator = Migrator(database="default") + + old_state = migrator.apply_initial_migration(("denial_reasons", "0006_populate_uuid_field")) + DenialReason = old_state.apps.get_model("denial_reasons", "DenialReason") + denial_reason = DenialReason.objects.get(id=1) + assert ( + denial_reason.description + == """Respect for the UK's international obligations and commitments, in particular sanctions adopted by the UN Security Council, agreements on non-proliferation and other subjects, as well as other international obligations. + +Military End Use Control.""" + ) + + new_state = migrator.apply_tested_migration(("denial_reasons", "0007_criterion_1_description_update")) + DenialReason = new_state.apps.get_model("denial_reasons", "DenialReason") + denial_reason = DenialReason.objects.get(id=1) + assert ( + denial_reason.description + == "Respect for the UK's international obligations and commitments, in particular sanctions adopted by the UN Security Council, agreements on non-proliferation and other subjects, as well as other international obligations." + ) + + expected_uuids = set(DENIAL_REASON_ID_TO_UUID_MAP.values()) + actual_uuids = set([str(denial_reason.uuid) for denial_reason in DenialReason.objects.all()]) + assert expected_uuids == actual_uuids