All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 3m55s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 3m43s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 1m45s
148 lines
5.2 KiB
Python
148 lines
5.2 KiB
Python
"""Published contract and runtime DTOs agree, including generated-client identities."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from django.urls import reverse
|
|
from organizations.registry_payload_serializers import (
|
|
BudgetRegistryRecordDetailPayloadSerializer,
|
|
BudgetRegistryRecordListPayloadSerializer,
|
|
SmeSupportRecordDetailPayloadSerializer,
|
|
SmeSupportRecordListPayloadSerializer,
|
|
)
|
|
|
|
from tests.apps.organizations import test_registry_query_contract as query_contract
|
|
|
|
registry_query = query_contract.registry_query
|
|
|
|
|
|
@pytest.fixture
|
|
def contract():
|
|
from core import schema
|
|
|
|
return json.loads(Path(schema.__file__).with_name("openapi.json").read_text())
|
|
|
|
|
|
def test_canonical_schema_served_without_authentication(client, contract):
|
|
response = client.get(reverse("canonical-openapi"))
|
|
assert response.status_code == 200
|
|
assert response["Content-Type"] == "application/json"
|
|
assert json.loads(b"".join(response.streaming_content)) == contract
|
|
assert contract["openapi"].startswith("3.")
|
|
|
|
|
|
def test_discriminated_records_keep_named_ordering_and_old_operations(contract):
|
|
schemas = contract["components"]["schemas"]
|
|
for name in ("OrganizationSourceRecord", "OrganizationSourceRecordList"):
|
|
record = schemas[name]
|
|
assert len(record["oneOf"]) == 4
|
|
assert record["discriminator"]["propertyName"] == "source_group"
|
|
for group, record_type in (
|
|
("budget_process_registry", "budget_registry_organization"),
|
|
("government_support", "sme_support_measure"),
|
|
("sro_membership", "sro_membership"),
|
|
):
|
|
target = record["discriminator"]["mapping"][group].rsplit("/", 1)[1]
|
|
variant = schemas[target]
|
|
assert variant["properties"]["record_type"]["enum"] == [record_type]
|
|
assert {
|
|
"uid",
|
|
"organization",
|
|
"source_group",
|
|
"record_type",
|
|
"payload",
|
|
} <= set(variant["required"])
|
|
payload = variant["properties"]["payload"]["$ref"].rsplit("/", 1)[1]
|
|
assert set(schemas[payload]["properties"]) == set(
|
|
schemas[payload]["required"]
|
|
)
|
|
assert (
|
|
"-payload__united_states"
|
|
in schemas["V2OrganizationSourceRecordsListOrdering"]["enum"]
|
|
)
|
|
paths = contract["paths"]
|
|
detail = paths["/api/v2/organization-source-records/{uid}/"]["get"]
|
|
assert detail["operationId"] == "api_v2_organization_source_records_read"
|
|
assert detail["tags"] == ["api"]
|
|
assert (
|
|
paths["/api/v1/parsers/dashboard/"]["get"]["operationId"]
|
|
== "api_v1_parsers_dashboard_list"
|
|
)
|
|
assert (
|
|
schemas["PublishedSourceRecordOrganization"]["properties"]["inn"]["nullable"]
|
|
is True
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("source", "list_serializer", "detail_serializer"),
|
|
[
|
|
(
|
|
"budget_ubpandnubp",
|
|
BudgetRegistryRecordListPayloadSerializer,
|
|
BudgetRegistryRecordDetailPayloadSerializer,
|
|
),
|
|
(
|
|
"fns_sme_support_recipients",
|
|
SmeSupportRecordListPayloadSerializer,
|
|
SmeSupportRecordDetailPayloadSerializer,
|
|
),
|
|
],
|
|
)
|
|
def test_list_detail_runtime_payloads_validate_with_named_contract(
|
|
registry_query, source, list_serializer, detail_serializer, contract
|
|
):
|
|
client, _ = registry_query
|
|
response = client.get(
|
|
reverse("api_v2:organizations:organization-source-records-list"),
|
|
{"source": source},
|
|
)
|
|
assert response.status_code == 200
|
|
assert response.data["errors"] is None
|
|
assert set(response.data["meta"]["pagination"]) == set(
|
|
contract["components"]["schemas"]["SourceRecordPagination"]["required"]
|
|
)
|
|
for row in response.data["data"]:
|
|
serializer = list_serializer(data=row["payload"])
|
|
assert serializer.is_valid(), serializer.errors
|
|
detail = client.get(
|
|
reverse(
|
|
"api_v2:organizations:organization-source-records-detail",
|
|
kwargs={"uid": row["uid"]},
|
|
)
|
|
)
|
|
assert detail.status_code == 200
|
|
serializer = detail_serializer(data=detail.data["payload"])
|
|
assert serializer.is_valid(), serializer.errors
|
|
assert detail.data["organization"] == row["organization"]
|
|
if source == "fns_sme_support_recipients":
|
|
assert row["payload"] == {
|
|
key: detail.data["payload"][key] for key in row["payload"]
|
|
}
|
|
else:
|
|
assert (
|
|
row["payload"]["registry"]["code"]
|
|
== detail.data["payload"]["registry"]["code"]
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"uid,expected", [("broken", 400), ("00000000-0000-0000-0000-000000000099", 404)]
|
|
)
|
|
def test_invalid_or_absent_detail_is_typed_error(registry_query, uid, expected):
|
|
client, _ = registry_query
|
|
response = client.get(
|
|
reverse(
|
|
"api_v2:organizations:organization-source-records-detail",
|
|
kwargs={"uid": uid},
|
|
)
|
|
)
|
|
assert response.status_code == expected
|
|
assert response.data["success"] is False
|
|
assert response.data["data"] is None
|
|
assert response.data["errors"][0]["code"] in {
|
|
"invalid_uid",
|
|
"source_record_not_found",
|
|
}
|