feat(fit-connect): govern inbound acknowledgement plans
Module Package Release / publish-packages (push) Successful in 10s
Module Package Release / publish-packages (push) Successful in 10s
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import replace
|
||||
from datetime import UTC, datetime
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from govoplan_fit_connect.backend.inbound import (
|
||||
FIT_CONNECT_ACCEPT_EVENT,
|
||||
FIT_CONNECT_REJECT_EVENT,
|
||||
FitConnectAttachmentEvidence,
|
||||
FitConnectInboundError,
|
||||
FitConnectProfile,
|
||||
FitConnectSubmission,
|
||||
FitConnectSubmissionEvidence,
|
||||
FitConnectVerification,
|
||||
build_acknowledgement_plan,
|
||||
create_ingress_receipt,
|
||||
)
|
||||
from govoplan_fit_connect.backend.manifest import get_manifest
|
||||
|
||||
|
||||
DESTINATION_ID = "736c4581-da80-4710-9384-d19ebe1ff2bc"
|
||||
SUBMISSION_ID = "c82585d8-e49d-4e27-abd6-be3dbf61a76f"
|
||||
|
||||
|
||||
def _profile() -> FitConnectProfile:
|
||||
return FitConnectProfile(
|
||||
profile_id="fit-connect-test-binding",
|
||||
destination_id=DESTINATION_ID,
|
||||
submission_api_version="configured-exact-version",
|
||||
metadata_schema_version="configured-exact-version",
|
||||
profile_revision="revision-4",
|
||||
connection_ref="core-credential:fit-connect-client",
|
||||
decryption_key_ref="core-key:fit-connect-decryption",
|
||||
event_signing_key_ref="core-key:fit-connect-event-signing",
|
||||
)
|
||||
|
||||
|
||||
def _submission() -> FitConnectSubmission:
|
||||
return FitConnectSubmission(
|
||||
destination_id=DESTINATION_ID,
|
||||
submission_id=SUBMISSION_ID,
|
||||
transaction_reference="case:transaction-7",
|
||||
public_service_identifier="urn:de:fim:leika:leistung:99102013104000",
|
||||
region="DE094750156156",
|
||||
received_at=datetime(2026, 8, 23, 12, tzinfo=UTC),
|
||||
evidence=FitConnectSubmissionEvidence(
|
||||
metadata_sha256="a" * 64,
|
||||
data_sha256="b" * 64,
|
||||
metadata_authentication_tag="metadata-tag",
|
||||
data_authentication_tag="data-tag",
|
||||
attachments=(
|
||||
FitConnectAttachmentEvidence(
|
||||
attachment_id="19ffb5ed-0a9d-02b2-9bfb-e271a8474c61",
|
||||
content_sha256="c" * 64,
|
||||
authentication_tag="attachment-tag",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _verification(**changes) -> FitConnectVerification:
|
||||
values = {
|
||||
"downloaded_complete": True,
|
||||
"decryption_succeeded": True,
|
||||
"metadata_schema_valid": True,
|
||||
"data_schema_valid": True,
|
||||
"authentication_tags_verified": True,
|
||||
"verified_at": datetime(2026, 8, 23, 12, 5, tzinfo=UTC),
|
||||
"durable_handoff_reference": "cases:case-4:submission-1",
|
||||
"durable_handoff_sha256": "d" * 64,
|
||||
}
|
||||
values.update(changes)
|
||||
return FitConnectVerification(**values)
|
||||
|
||||
|
||||
def test_ingress_receipt_is_exact_and_claims_no_acknowledgement() -> None:
|
||||
receipt = create_ingress_receipt(_profile(), _submission())
|
||||
|
||||
assert receipt.acknowledged is False
|
||||
assert receipt.business_accepted is False
|
||||
assert len(receipt.receipt_sha256) == 64
|
||||
|
||||
|
||||
def test_complete_durable_verification_builds_effect_free_accept_plan() -> None:
|
||||
profile = _profile()
|
||||
submission = _submission()
|
||||
receipt = create_ingress_receipt(profile, submission)
|
||||
|
||||
plan = build_acknowledgement_plan(
|
||||
profile,
|
||||
submission,
|
||||
receipt,
|
||||
_verification(),
|
||||
)
|
||||
|
||||
assert plan.disposition == "accept"
|
||||
assert plan.event_type == FIT_CONNECT_ACCEPT_EVENT
|
||||
assert plan.dispatch_allowed is False
|
||||
assert plan.technical_receipt_only is True
|
||||
assert plan.business_accepted is False
|
||||
payload = json.loads(plan.event_request_json)
|
||||
assert payload["authentication_tags"]["metadata"] == "metadata-tag"
|
||||
assert payload["durable_handoff_sha256"] == "d" * 64
|
||||
|
||||
|
||||
def test_complete_verification_without_durable_handoff_defers() -> None:
|
||||
profile = _profile()
|
||||
submission = _submission()
|
||||
receipt = create_ingress_receipt(profile, submission)
|
||||
verification = _verification(
|
||||
durable_handoff_reference=None,
|
||||
durable_handoff_sha256=None,
|
||||
)
|
||||
|
||||
plan = build_acknowledgement_plan(profile, submission, receipt, verification)
|
||||
|
||||
assert plan.disposition == "defer"
|
||||
assert plan.event_type is None
|
||||
assert plan.event_request_json is None
|
||||
|
||||
|
||||
def test_incomplete_download_cannot_be_turned_into_rejection() -> None:
|
||||
profile = _profile()
|
||||
submission = _submission()
|
||||
receipt = create_ingress_receipt(profile, submission)
|
||||
verification = _verification(
|
||||
downloaded_complete=False,
|
||||
durable_handoff_reference=None,
|
||||
durable_handoff_sha256=None,
|
||||
failure_disposition="reject",
|
||||
problem_codes=("download-incomplete",),
|
||||
)
|
||||
|
||||
plan = build_acknowledgement_plan(profile, submission, receipt, verification)
|
||||
|
||||
assert plan.disposition == "defer"
|
||||
|
||||
|
||||
def test_explicit_permanent_technical_failure_builds_reject_plan() -> None:
|
||||
profile = _profile()
|
||||
submission = _submission()
|
||||
receipt = create_ingress_receipt(profile, submission)
|
||||
verification = _verification(
|
||||
authentication_tags_verified=False,
|
||||
durable_handoff_reference=None,
|
||||
durable_handoff_sha256=None,
|
||||
failure_disposition="reject",
|
||||
problem_codes=("authentication-tags-invalid",),
|
||||
)
|
||||
|
||||
plan = build_acknowledgement_plan(profile, submission, receipt, verification)
|
||||
|
||||
assert plan.disposition == "reject"
|
||||
assert plan.event_type == FIT_CONNECT_REJECT_EVENT
|
||||
assert json.loads(plan.event_request_json)["problem_codes"] == [
|
||||
"authentication-tags-invalid"
|
||||
]
|
||||
|
||||
|
||||
def test_receipt_from_another_submission_is_rejected() -> None:
|
||||
profile = _profile()
|
||||
submission = _submission()
|
||||
receipt = create_ingress_receipt(profile, submission)
|
||||
wrong_receipt = replace(
|
||||
receipt,
|
||||
submission_id="02bf1d9f-282d-4abf-810a-c4104baf0afe",
|
||||
)
|
||||
|
||||
with pytest.raises(FitConnectInboundError, match="receipt submission"):
|
||||
build_acknowledgement_plan(profile, submission, wrong_receipt, _verification())
|
||||
|
||||
|
||||
def test_manifest_declares_no_active_target_and_has_german_documentation() -> None:
|
||||
manifest = get_manifest()
|
||||
assert manifest.version == "0.1.19"
|
||||
assert manifest.external_providers[0].id == "fit_connect.submission_api"
|
||||
assert manifest.architecture is not None
|
||||
assert manifest.architecture.target_tested_providers == ()
|
||||
assert manifest.documentation[0].translations["de"]["title"]
|
||||
Reference in New Issue
Block a user