Files
mostovik-backend/tests/apps/organizations/test_canonical_openapi.py
Aleksandr Meshchryakov 18971d33ec
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
feat: complete published registry contracts and gated SRO ingestion
2026-09-14 17:01:02 +02:00

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",
}