← back to Unclaimed Property Platform
tests/test_cycle9_claims_persistence.py
109 lines
"""Cycle 8 test — claim lifecycle against a REAL SQLite ClaimRepository.
Run: python -m tests.test_cycle9_claims_persistence
Proves the claims subsystem is runnable end-to-end on real persistence (not a mock):
1. full lifecycle DRAFT -> ... -> SUBMITTED_TO_STATE persists across get_for_update reads;
2. queue+complete write the outbox event and both status events WITHOUT colliding on the
real UNIQUE(claim_id, idempotency_key) constraint (C3 fix proven against the DB);
3. the DB itself enforces idempotency (duplicate key -> IntegrityError);
4. the state-only guard blocks claimant self-approval on a persisted claim;
5. illegal transitions raise.
"""
from __future__ import annotations
import sqlite3
import sys
import tempfile
from pathlib import Path
from uuid import uuid4
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from services.claims.claim_workflow import (
ClaimStatus, complete_state_submission, queue_state_submission, transition_claim,
)
from services.claims.sqlite_claim_repo import SqliteClaimRepository
from services.common.sqlite_repo import SqliteRepository
class FakeStateAdapter:
def submit_claim(self, claim, idempotency_key) -> str:
return "STATE-CASE-1"
def ok(msg: str) -> None:
print(f" ✓ {msg}")
def main() -> int:
tmp = Path(tempfile.mkdtemp(prefix="upp-cycle9-"))
base = SqliteRepository(str(tmp / "p.db")) # creates full schema
repo = SqliteClaimRepository(base.conn) # shares the one system-of-record DB
print("1) create + walk the lifecycle (persisted)")
claim = repo.create_claim("SAMPLE", public_property_reference="ref-1", claimant_id=uuid4())
assert repo.get_for_update(claim.claim_id).status == ClaimStatus.DRAFT
steps = [
(ClaimStatus.IDENTITY_PENDING, "k1"),
(ClaimStatus.EVIDENCE_PENDING, "k2"),
(ClaimStatus.READY_FOR_SUBMISSION, "k3"),
]
for target, key in steps:
transition_claim(repo, claim.claim_id, target, actor_id="worker", idempotency_key=key)
assert repo.get_for_update(claim.claim_id).status == ClaimStatus.READY_FOR_SUBMISSION
ok("DRAFT -> IDENTITY -> EVIDENCE -> READY_FOR_SUBMISSION persisted")
print("2) queue + complete submission (real UNIQUE constraint, no collision)")
queue_state_submission(repo, claim.claim_id, actor_id="worker", idempotency_key="sub")
assert repo.get_for_update(claim.claim_id).status == ClaimStatus.SUBMITTING
complete_state_submission(repo, FakeStateAdapter(), claim.claim_id, idempotency_key="sub")
final = repo.get_for_update(claim.claim_id)
assert final.status == ClaimStatus.SUBMITTED_TO_STATE, final.status
assert final.state_case_id == "STATE-CASE-1", final.state_case_id
outbox = repo.pending_outbox(claim.claim_id)
assert any(e["event_type"] == "state_claim_submission_requested" for e in outbox), outbox
ok(f"SUBMITTED_TO_STATE persisted; state_case_id set; outbox has request; "
f"{len(repo.events(claim.claim_id))} events, keys all unique")
print("3) DB itself enforces idempotency (duplicate key -> IntegrityError)")
repo.append_event(claim.claim_id, "probe", {"n": 1}, idempotency_key="dupe")
raised = False
try:
repo.append_event(claim.claim_id, "probe", {"n": 2}, idempotency_key="dupe")
except sqlite3.IntegrityError:
raised = True
assert raised, "duplicate (claim_id, idempotency_key) must raise IntegrityError"
ok("duplicate idempotency key rejected by the real UNIQUE constraint")
print("4) state-only guard on a persisted claim")
raised = False
try:
transition_claim(repo, claim.claim_id, ClaimStatus.APPROVED,
actor_id="claimant:self", idempotency_key="appr")
except PermissionError:
raised = True
assert raised, "claimant must not self-approve"
# a state actor can
transition_claim(repo, claim.claim_id, ClaimStatus.APPROVED,
actor_id="state:CA", idempotency_key="appr2")
assert repo.get_for_update(claim.claim_id).status == ClaimStatus.APPROVED
ok("claimant approval blocked; state:CA approval persisted")
print("5) illegal transition raises")
raised = False
try:
transition_claim(repo, claim.claim_id, ClaimStatus.DRAFT,
actor_id="worker", idempotency_key="bad")
except ValueError:
raised = True
assert raised, "APPROVED -> DRAFT is illegal"
ok("illegal transition (APPROVED -> DRAFT) rejected")
print("\nALL CYCLE-8 CLAIM-PERSISTENCE ASSERTIONS PASSED ✅")
return 0
if __name__ == "__main__":
raise SystemExit(main())