Skip to content

Commit

Permalink
Merge pull request #2386 from uktrade/uat
Browse files Browse the repository at this point in the history
Production release
  • Loading branch information
currycoder authored Jan 23, 2025
2 parents 7690f3d + 71923a8 commit 9f43d7d
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 9 deletions.
45 changes: 44 additions & 1 deletion api/applications/tests/factories.py
Original file line number Diff line number Diff line change
@@ -1,7 +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,
Expand All @@ -18,9 +21,16 @@
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 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
Expand Down Expand Up @@ -62,6 +72,7 @@ def _create(cls, model_class, *args, **kwargs):
obj = model_class(*args, **kwargs)
if "status" not in kwargs:
obj.status = get_case_status_by_status(CaseStatusEnum.SUBMITTED)

obj.save()
return obj

Expand Down Expand Up @@ -228,6 +239,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,
Expand Down Expand Up @@ -262,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
14 changes: 10 additions & 4 deletions api/cases/libraries/get_flags.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -27,10 +27,16 @@ 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,
# 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)
& 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()

Expand Down
21 changes: 18 additions & 3 deletions api/cases/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
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.models import Permission, Role
from api.users.enums import UserType
from api.users.tests.factories import (
ExporterUserFactory,
GovUserFactory,
RoleFactory,
SystemUserFactory,
UserOrganisationRelationshipFactory,
)

Expand Down Expand Up @@ -52,14 +53,28 @@ def exporter_headers(exporter_user, organisation):
}


@pytest.fixture(autouse=True)
def system_user():
return SystemUserFactory()


@pytest.fixture()
def gov_headers(gov_user):
return {"HTTP_GOV_USER_TOKEN": user_to_token(gov_user.baseuser_ptr)}


@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()
Expand Down
92 changes: 92 additions & 0 deletions api/cases/tests/test_case_flags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import pytest

from django.urls import reverse
from urllib import parse

from api.applications.tests.factories import StandardSubmittedApplicationFactory
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.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,
gov_headers,
mocker,
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

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]
]

expected_flags = []
for flags in flags_data.values():
expected_flags.extend(flags)

assert sorted(all_flags) == sorted(expected_flags)


@pytest.mark.parametrize(
"flags_data",
(
{
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.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,
):
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)
assert response.status_code == 200

response = response.json()
all_flags = [item["id"] for item in response["case"]["all_flags"]]

expected_flags = []
for flags in flags_data.values():
expected_flags.extend(flags)

assert sorted(all_flags) == sorted(expected_flags)
4 changes: 4 additions & 0 deletions api/cases/tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -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(
[
Expand Down
Original file line number Diff line number Diff line change
@@ -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),
]
Original file line number Diff line number Diff line change
@@ -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
18 changes: 17 additions & 1 deletion api/users/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down

0 comments on commit 9f43d7d

Please sign in to comment.