feat: prepare ARR for controlled public deployment

This commit is contained in:
Wyndham ARR
2026-07-29 16:38:05 +08:00
commit a701de9f0e
271 changed files with 48472 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
from __future__ import annotations
import hashlib
import json
import tempfile
import unittest
from dataclasses import replace
from datetime import date
from pathlib import Path
from arr_ingestion.contracts import DeliveryEnvelope, IngestionError
from arr_ingestion.repository import (
InMemoryIngestionRepository,
JobRegistration,
)
from arr_ingestion.service import IngestionService
from arr_ingestion.validation import DeliveryValidator
from tests.test_arr_ingestion_validation import build_delivery, policy
from tests.test_arr_opera_daily_ingest import reservation, xml_document
def register(repository: InMemoryIngestionRepository, raw: bytes) -> DeliveryEnvelope:
envelope = DeliveryEnvelope.from_dict(json.loads(raw))
source = envelope.artifacts["source_xml"]
assert source is not None
repository.register_job(
JobRegistration(
job_id=envelope.job_id,
source=source,
processor_version=envelope.processor_version,
rule_set_sha256=envelope.rule_set_sha256,
attempt_no=envelope.attempt_no,
idempotency_key=hashlib.sha256(
f"{envelope.job_id}:{envelope.attempt_no}".encode("utf-8")
).hexdigest(),
)
)
return envelope
class IngestionServiceTests(unittest.TestCase):
def success_xml(self, room: str = "SYN-ROOM-1") -> str:
return xml_document(
reservation(1, room=room, res_comment="SYN-GROUP", departure="2026-07-30")
)
def test_success_commits_all_records_and_same_callback_is_idempotent(self):
with tempfile.TemporaryDirectory() as temp_dir:
raw, store, _values = build_delivery(self.success_xml(), Path(temp_dir))
repository = InMemoryIngestionRepository()
register(repository, raw)
service = IngestionService(DeliveryValidator(store, policy()), repository)
first = service.ingest(raw)
second = service.ingest(raw)
self.assertEqual(first.status, "committed")
self.assertEqual(second, first)
self.assertEqual(first.version_no, 1)
self.assertEqual(repository.current_version_id(date(2026, 7, 27)), first.daily_version_id)
self.assertEqual(len(repository.version_records(first.daily_version_id or 0)), 1)
def test_same_delivery_id_with_different_envelope_bytes_conflicts(self):
with tempfile.TemporaryDirectory() as temp_dir:
raw, store, _values = build_delivery(self.success_xml(), Path(temp_dir))
repository = InMemoryIngestionRepository()
register(repository, raw)
service = IngestionService(DeliveryValidator(store, policy()), repository)
service.ingest(raw)
rewritten = json.dumps(json.loads(raw), ensure_ascii=False, indent=2).encode("utf-8")
with self.assertRaisesRegex(IngestionError, "identifier conflicts") as raised:
service.ingest(rewritten)
self.assertEqual(raised.exception.code, "CALLBACK_CONFLICT")
def test_same_source_and_rule_from_new_job_reuses_committed_version(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
raw, store, _values = build_delivery(self.success_xml(), root / "first")
repository = InMemoryIngestionRepository()
register(repository, raw)
first = IngestionService(DeliveryValidator(store, policy()), repository).ingest(raw)
envelope = json.loads(raw)
envelope["delivery_id"] = "delivery-002"
envelope["job_id"] = "job-002"
second_raw = json.dumps(envelope, ensure_ascii=False).encode("utf-8")
register(repository, second_raw)
second = IngestionService(DeliveryValidator(store, policy()), repository).ingest(second_raw)
self.assertEqual(second.status, "already_committed")
self.assertEqual(second.daily_version_id, first.daily_version_id)
self.assertEqual(second.version_no, first.version_no)
def test_repository_identity_allows_same_source_under_new_rule(self):
"""A future allowlisted rule creates a new immutable version, not a no-op."""
with tempfile.TemporaryDirectory() as temp_dir:
raw, store, _values = build_delivery(self.success_xml(), Path(temp_dir))
repository = InMemoryIngestionRepository()
register(repository, raw)
validator = DeliveryValidator(store, policy())
verified = validator.validate(raw)
first = repository.commit_delivery(verified)
new_rule = "1" * 64
new_envelope = replace(
verified.envelope,
delivery_id="delivery-new-rule",
job_id="job-new-rule",
rule_set_sha256=new_rule,
)
source = new_envelope.artifacts["source_xml"]
assert source is not None
repository.register_job(
JobRegistration(
job_id=new_envelope.job_id,
source=source,
processor_version=new_envelope.processor_version,
rule_set_sha256=new_rule,
attempt_no=1,
idempotency_key=hashlib.sha256(b"job-new-rule:1").hexdigest(),
)
)
new_verified = replace(
verified,
envelope=new_envelope,
envelope_sha256=hashlib.sha256(b"synthetic-new-rule-envelope").hexdigest(),
)
second = repository.commit_delivery(new_verified)
self.assertEqual(second.status, "committed")
self.assertEqual(second.version_no, 2)
self.assertNotEqual(second.daily_version_id, first.daily_version_id)
self.assertEqual(repository.version_status(first.daily_version_id or 0), "superseded")
def test_same_business_date_new_source_supersedes_only_after_success(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
raw_one, store_one, _ = build_delivery(
self.success_xml("SYN-ROOM-1"), root / "one"
)
repository = InMemoryIngestionRepository()
register(repository, raw_one)
first = IngestionService(
DeliveryValidator(store_one, policy()), repository
).ingest(raw_one)
raw_two, store_two, _ = build_delivery(
self.success_xml("SYN-ROOM-2"), root / "two"
)
envelope_two = json.loads(raw_two)
envelope_two["delivery_id"] = "delivery-002"
envelope_two["job_id"] = "job-002"
raw_two = json.dumps(envelope_two, ensure_ascii=False).encode("utf-8")
register(repository, raw_two)
second = IngestionService(
DeliveryValidator(store_two, policy()), repository
).ingest(raw_two)
self.assertEqual(second.status, "committed")
self.assertEqual(second.version_no, 2)
self.assertEqual(repository.version_status(first.daily_version_id or 0), "superseded")
self.assertEqual(repository.version_status(second.daily_version_id or 0), "active")
self.assertEqual(
repository.current_version_id(date(2026, 7, 27)),
second.daily_version_id,
)
def test_business_failure_is_recorded_without_switching_current(self):
failure_xml = xml_document(
reservation(1, rate_code="GRPA4", rate_amount="1800", res_comment="SYN-FAIL")
)
with tempfile.TemporaryDirectory() as temp_dir:
raw, store, _values = build_delivery(failure_xml, Path(temp_dir))
repository = InMemoryIngestionRepository()
register(repository, raw)
outcome = IngestionService(
DeliveryValidator(store, policy()), repository
).ingest(raw)
self.assertEqual(outcome.status, "recorded_failure")
self.assertIsNone(outcome.daily_version_id)
self.assertIsNone(repository.current_version_id(date(2026, 7, 27)))
def test_unknown_job_is_rejected_after_artifact_validation(self):
with tempfile.TemporaryDirectory() as temp_dir:
raw, store, _values = build_delivery(self.success_xml(), Path(temp_dir))
service = IngestionService(
DeliveryValidator(store, policy()), InMemoryIngestionRepository()
)
with self.assertRaisesRegex(IngestionError, "not registered") as raised:
service.ingest(raw)
self.assertEqual(raised.exception.code, "JOB_NOT_FOUND")
if __name__ == "__main__":
unittest.main(verbosity=2)