Add hosted conformance suite and close WP-0006 (T08)
tests/test_hosted_conformance.py (6 Docker-gated tests) addresses all three requirements from the task description: the golden Phase example (manifest + full ledger + all 4 extensions) replayed through the hosted API reproduces exactly the offline-established outcome (Development Credit 67000, Remission Credit 33000, Outstanding Target 0, MIT) including the hosted Attestation; two Phases under different Licensors operate with interleaved appends and are proven isolated both positively and negatively (cross-Licensor writes rejected); a parametrized regression test across four ledger shapes (credits-only, remission, reversal, admin corrections) confirms hosted-append-then- offline-fold always matches expected totals. WP-0006 is now finished - all 8 tasks (PRD, ADR-0002, registries, ledger API, metrics, attestation, onboarding, conformance) done. T01's flagged gap (no task owns hosting the Breach/Compliance Record component from License V1C1 §7.4) remains open and unassigned.
This commit is contained in:
parent
12e8ae218f
commit
151ed3e97c
3 changed files with 335 additions and 3 deletions
|
|
@ -78,7 +78,7 @@ The concept's §13 now defines a **Global Contingency Share Determination Rule**
|
|||
| [TREV-WP-0003](workplans/TREV-WP-0003-normative-core-extraction.md) | Extract stable normative core docs — **finished**, reviewed and accepted 2026-07-29 |
|
||||
| [TREV-WP-0004](workplans/TREV-WP-0004-global-jurisdiction-research.md) | Global jurisdictional research backing the License/CUA candidates — **finished**, T10 synthesis accepted 2026-07-29 with alpha/beta working defaults (full legal review deferred until out of beta — see `SCOPE.md` §1) |
|
||||
| [TREV-WP-0005](workplans/TREV-WP-0005-enforcement-network-research.md) | Enforcement Network legal feasibility research — **finished**, T10 synthesis accepted 2026-07-29 on the same alpha/beta basis (Japan's Article 12 risk remains explicitly unresolved) |
|
||||
| [TREV-WP-0006](workplans/TREV-WP-0006-trust-service-implementation.md) | Hosted Trust Service reference implementation (PRD Phase 4b) — active; T01 (PRD), T02 (ADR-0002, accepted), T03 (registries), T04 (Ledger append API), T05 (Metrics), T06 (Conversion Attestation), T07 (onboarding mechanism, `specs/TrustServiceOnboarding.md` + `scripts/trf_onboard.py`) done; T08 (hosted conformance suite) next |
|
||||
| [TREV-WP-0006](workplans/TREV-WP-0006-trust-service-implementation.md) | Hosted Trust Service reference implementation (PRD Phase 4b) — **finished**, all 8 tasks done (Postgres-backed registries/ledger/metrics/attestation, ADR-0002 accepted, onboarding CLI, hosted conformance suite). T01's flagged gap — no task owns hosting the Breach/Compliance Record component (License V1C1 §7.4) — remains open, not yet assigned |
|
||||
| [TREV-WP-0007](workplans/TREV-WP-0007-degeneration-policy-and-canonical-profiles.md) | Degeneration policy + canonical monetization profile catalog — active, not yet started |
|
||||
| [TREV-WP-0008](workplans/TREV-WP-0008-governance-and-pilot-rollout.md) | Governance formalization + pilot rollout across `coulomb-loop`/`net-kingdom`/`helix-forge`/`railiance-*` — active, not yet started; real Phase declarations gated behind T05 |
|
||||
|
||||
|
|
|
|||
313
tests/test_hosted_conformance.py
Normal file
313
tests/test_hosted_conformance.py
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
"""Hosted-scale conformance suite (WP-0006-T08).
|
||||
|
||||
Extends WP-0002's offline conformance suite (`tests/test_*.py`, 36 tests)
|
||||
to run against the actual hosted service, per the task's own three
|
||||
requirements:
|
||||
|
||||
1. the golden Phase example (`examples/phase-001/`) replayed through the
|
||||
hosted API end-to-end, not only the offline library;
|
||||
2. multiple concurrent Phases across different Licensors, isolated from
|
||||
one another;
|
||||
3. a standing regression check that hosted and offline folds of the same
|
||||
exported ledger always agree — generalized here across several ledger
|
||||
shapes (development-credit, remission-credit, credit-reversal,
|
||||
administrative corrections), not just the golden fixture's own shape.
|
||||
|
||||
Same ephemeral, disposable Postgres-via-Docker pattern as the other hosted
|
||||
test modules (never the shared state-hub instance).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
psycopg = pytest.importorskip("psycopg")
|
||||
pytest.importorskip("fastapi")
|
||||
from fastapi.testclient import TestClient # noqa: E402
|
||||
|
||||
from conftest import golden_entries, golden_extension, golden_manifest # noqa: E402
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
MIGRATIONS = [
|
||||
REPO_ROOT / "migrations" / "0001_registries.sql",
|
||||
REPO_ROOT / "migrations" / "0002_ledger.sql",
|
||||
REPO_ROOT / "migrations" / "0003_attestations.sql",
|
||||
]
|
||||
EXTENSION_NAMES = [
|
||||
"development-license",
|
||||
"cost-plus-operations",
|
||||
"phase-sponsorship",
|
||||
"service-with-development-allocation",
|
||||
]
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
shutil.which("docker") is None, reason="docker not available"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def pg_container():
|
||||
name = f"trf-test-pg-conformance-{uuid.uuid4().hex[:8]}"
|
||||
subprocess.run(
|
||||
[
|
||||
"docker", "run", "--rm", "-d",
|
||||
"--name", name,
|
||||
"-e", "POSTGRES_PASSWORD=postgres",
|
||||
"-e", "POSTGRES_DB=target_revenue_test",
|
||||
"-p", "127.0.0.1::5432",
|
||||
"postgres:16-alpine",
|
||||
],
|
||||
check=True, capture_output=True,
|
||||
)
|
||||
try:
|
||||
port_out = subprocess.run(
|
||||
["docker", "port", name, "5432/tcp"], check=True, capture_output=True, text=True
|
||||
).stdout.strip()
|
||||
host_port = port_out.split(":")[-1]
|
||||
dsn = f"host=127.0.0.1 port={host_port} dbname=target_revenue_test user=postgres password=postgres"
|
||||
|
||||
for _ in range(60):
|
||||
try:
|
||||
with psycopg.connect(dsn, connect_timeout=1):
|
||||
break
|
||||
except psycopg.OperationalError:
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
raise RuntimeError("postgres container did not become ready in time")
|
||||
|
||||
with psycopg.connect(dsn) as conn:
|
||||
for migration in MIGRATIONS:
|
||||
conn.execute(migration.read_text(encoding="utf-8"))
|
||||
conn.commit()
|
||||
for token, licensor_id in [
|
||||
("test-token-repo-a", "coulomb-loop"),
|
||||
("test-token-repo-b", "net-kingdom"),
|
||||
]:
|
||||
conn.execute(
|
||||
"INSERT INTO licensors (token, licensor_id) VALUES (%s, %s)",
|
||||
(token, licensor_id),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
app_dsn = (
|
||||
f"host=127.0.0.1 port={host_port} dbname=target_revenue_test "
|
||||
f"user=trf_app password=changeme-in-deployment"
|
||||
)
|
||||
yield {"app_dsn": app_dsn, "token_a": "test-token-repo-a", "token_b": "test-token-repo-b"}
|
||||
finally:
|
||||
subprocess.run(["docker", "stop", name], capture_output=True)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client(pg_container, monkeypatch):
|
||||
monkeypatch.setenv("TRF_DATABASE_URL", pg_container["app_dsn"])
|
||||
monkeypatch.setenv("TRF_SIGNING_KEY_HEX", "33" * 32)
|
||||
from target_revenue.service import app as app_module
|
||||
|
||||
if hasattr(app_module.app.state, "pool"):
|
||||
app_module.app.state.pool.close()
|
||||
del app_module.app.state.pool
|
||||
if hasattr(app_module.app.state, "signing_key"):
|
||||
del app_module.app.state.signing_key
|
||||
with TestClient(app_module.app) as c:
|
||||
yield c
|
||||
|
||||
|
||||
def auth(token: str) -> dict[str, str]:
|
||||
return {"Authorization": f"Bearer {token}"}
|
||||
|
||||
|
||||
def test_golden_phase_replayed_through_hosted_api_matches_offline_library(client, pg_container):
|
||||
"""Requirement 1: the golden Phase example, replayed through the
|
||||
hosted API rather than the offline library, must reproduce exactly
|
||||
the outcomes tests/test_conversion.py and tests/test_ledger_fold.py
|
||||
already established offline (Development Credit 67000, Remission
|
||||
Credit 33000, Outstanding Target 0, future_license MIT)."""
|
||||
from target_revenue import conversion, fold
|
||||
|
||||
manifest = golden_manifest()
|
||||
manifest["phase"]["id"] = manifest["phase"]["id"] + "-conformance-golden-" + uuid.uuid4().hex[:6]
|
||||
phase_id = manifest["phase"]["id"]
|
||||
|
||||
resp = client.post("/phases", json=manifest, headers=auth(pg_container["token_a"]))
|
||||
assert resp.status_code == 201, resp.text
|
||||
|
||||
for name in EXTENSION_NAMES:
|
||||
ext = golden_extension(name)
|
||||
ext_resp = client.post("/extensions", json=ext, headers=auth(pg_container["token_a"]))
|
||||
# Extensions are shared/global, not per-Phase — a second replay in
|
||||
# the same test session would collide; accept both outcomes here.
|
||||
assert ext_resp.status_code in (201, 422)
|
||||
|
||||
for entry in golden_entries():
|
||||
submitted = {k: v for k, v in entry.items() if k not in ("previous_entry_hash", "signature")}
|
||||
submitted["phase"] = phase_id
|
||||
entry_resp = client.post(f"/phases/{phase_id}/ledger", json=submitted, headers=auth(pg_container["token_a"]))
|
||||
assert entry_resp.status_code == 201, entry_resp.text
|
||||
|
||||
exported_entries = client.get(f"/phases/{phase_id}/ledger").json()
|
||||
hosted_result = fold.fold_outstanding_target(
|
||||
manifest["phase"]["initial_target"]["amount"], exported_entries
|
||||
)
|
||||
|
||||
offline_manifest = golden_manifest()
|
||||
offline_status = conversion.conversion_status(offline_manifest, golden_entries())
|
||||
|
||||
assert hosted_result.development_credit == offline_status.development_credit == 67000
|
||||
assert hosted_result.remission_credit == offline_status.remission_credit == 33000
|
||||
assert hosted_result.outstanding_target == offline_status.outstanding_target == 0
|
||||
assert hosted_result.is_converted is offline_status.is_converted is True
|
||||
|
||||
attestation_resp = client.get(f"/phases/{phase_id}/attestation")
|
||||
assert attestation_resp.status_code == 200
|
||||
hosted_attestation = attestation_resp.json()
|
||||
assert hosted_attestation["final_development_credit"] == 67000
|
||||
assert hosted_attestation["final_remission_credit"] == 33000
|
||||
assert hosted_attestation["final_outstanding_target"] == 0
|
||||
assert hosted_attestation["future_license"] == "MIT"
|
||||
assert hosted_attestation["ledger_checkpoint"] == golden_entries()[-1]["id"]
|
||||
|
||||
|
||||
def test_multiple_concurrent_phases_across_different_licensors_are_isolated(client, pg_container):
|
||||
"""Requirement 2: two Phases, belonging to two different Licensors
|
||||
(standing in for two different product-line repos, e.g. coulomb-loop
|
||||
and net-kingdom), operate concurrently without cross-contamination —
|
||||
each Licensor's writes and reads only ever affect its own Phase."""
|
||||
manifest_a = golden_manifest()
|
||||
manifest_a["phase"]["id"] = "trsl:phase:conformance-repo-a-" + uuid.uuid4().hex[:6]
|
||||
manifest_a["phase"]["initial_target"]["amount"] = 10000
|
||||
|
||||
manifest_b = golden_manifest()
|
||||
manifest_b["phase"]["id"] = "trsl:phase:conformance-repo-b-" + uuid.uuid4().hex[:6]
|
||||
manifest_b["phase"]["initial_target"]["amount"] = 20000
|
||||
|
||||
assert client.post("/phases", json=manifest_a, headers=auth(pg_container["token_a"])).status_code == 201
|
||||
assert client.post("/phases", json=manifest_b, headers=auth(pg_container["token_b"])).status_code == 201
|
||||
|
||||
def entry(phase_id: str, entry_id: str, amount: float) -> dict:
|
||||
return {
|
||||
"id": entry_id,
|
||||
"phase": phase_id,
|
||||
"type": "development-credit",
|
||||
"amount": amount,
|
||||
"currency": "USD",
|
||||
"recognized_at": "2026-08-01T00:00:00Z",
|
||||
"evidence_reference": f"confidential:evidence:{entry_id}",
|
||||
"extension": {"id": "trsl:extension:development-license", "version": "1.0"},
|
||||
}
|
||||
|
||||
# Interleaved appends across both Phases, simulating concurrent
|
||||
# multi-repo activity rather than strictly sequential per-Phase use.
|
||||
a1 = client.post(
|
||||
f"/phases/{manifest_a['phase']['id']}/ledger",
|
||||
json=entry(manifest_a["phase"]["id"], "trsl:entry:conformrepoa0001", 3000),
|
||||
headers=auth(pg_container["token_a"]),
|
||||
)
|
||||
b1 = client.post(
|
||||
f"/phases/{manifest_b['phase']['id']}/ledger",
|
||||
json=entry(manifest_b["phase"]["id"], "trsl:entry:conformrepob0001", 5000),
|
||||
headers=auth(pg_container["token_b"]),
|
||||
)
|
||||
a2 = client.post(
|
||||
f"/phases/{manifest_a['phase']['id']}/ledger",
|
||||
json=entry(manifest_a["phase"]["id"], "trsl:entry:conformrepoa0002", 2000),
|
||||
headers=auth(pg_container["token_a"]),
|
||||
)
|
||||
assert a1.status_code == b1.status_code == a2.status_code == 201
|
||||
|
||||
# Cross-Licensor writes are rejected (isolation, not just non-interference).
|
||||
cross = client.post(
|
||||
f"/phases/{manifest_a['phase']['id']}/ledger",
|
||||
json=entry(manifest_a["phase"]["id"], "trsl:entry:conformrepoacross", 1),
|
||||
headers=auth(pg_container["token_b"]),
|
||||
)
|
||||
assert cross.status_code == 422
|
||||
|
||||
metrics_a = client.get(f"/phases/{manifest_a['phase']['id']}/metrics").json()
|
||||
metrics_b = client.get(f"/phases/{manifest_b['phase']['id']}/metrics").json()
|
||||
|
||||
assert metrics_a["facts"]["cumulative_development_credit"] == 5000
|
||||
assert metrics_b["facts"]["cumulative_development_credit"] == 5000 # only b1 applied to B
|
||||
assert metrics_a["facts"]["outstanding_target"] == 5000
|
||||
assert metrics_b["facts"]["outstanding_target"] == 15000
|
||||
|
||||
ledger_a = client.get(f"/phases/{manifest_a['phase']['id']}/ledger").json()
|
||||
ledger_b = client.get(f"/phases/{manifest_b['phase']['id']}/ledger").json()
|
||||
assert {e["id"] for e in ledger_a} == {"trsl:entry:conformrepoa0001", "trsl:entry:conformrepoa0002"}
|
||||
assert {e["id"] for e in ledger_b} == {"trsl:entry:conformrepob0001"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"plan",
|
||||
[
|
||||
[("development-credit", 1000), ("development-credit", 500)],
|
||||
[("development-credit", 2000), ("remission-credit", 300)],
|
||||
[("development-credit", 5000), ("credit-reversal", 1000)],
|
||||
[
|
||||
("development-credit", 4000),
|
||||
("administrative-correction-development", 250),
|
||||
("administrative-correction-remission", 100),
|
||||
],
|
||||
],
|
||||
ids=["credits-only", "with-remission", "with-reversal", "with-admin-corrections"],
|
||||
)
|
||||
def test_hosted_and_offline_fold_always_agree(client, pg_container, plan):
|
||||
"""Requirement 3, generalized: for several distinct ledger shapes (not
|
||||
only the golden fixture's own), appending through the hosted API and
|
||||
folding the export offline must reproduce exactly the same
|
||||
Development Credit, Remission Credit, and Outstanding Target the plan
|
||||
implies by construction."""
|
||||
from target_revenue import fold, hashing
|
||||
|
||||
manifest = golden_manifest()
|
||||
manifest["phase"]["id"] = "trsl:phase:conformance-plan-" + uuid.uuid4().hex[:8]
|
||||
manifest["phase"]["initial_target"]["amount"] = 100000
|
||||
phase_id = manifest["phase"]["id"]
|
||||
|
||||
assert client.post("/phases", json=manifest, headers=auth(pg_container["token_a"])).status_code == 201
|
||||
|
||||
expected_dev = 0.0
|
||||
expected_rem = 0.0
|
||||
for i, (entry_type, amount) in enumerate(plan, start=1):
|
||||
e = {
|
||||
"id": f"trsl:entry:conformplan{uuid.uuid4().hex[:6]}{i:02d}",
|
||||
"phase": phase_id,
|
||||
"type": entry_type,
|
||||
"amount": amount,
|
||||
"currency": "USD",
|
||||
"recognized_at": f"2026-08-{i:02d}T00:00:00Z",
|
||||
"evidence_reference": f"confidential:evidence:plan-{i}",
|
||||
}
|
||||
if entry_type in ("development-credit", "remission-credit"):
|
||||
e["extension"] = {"id": "trsl:extension:development-license", "version": "1.0"}
|
||||
if entry_type == "credit-reversal":
|
||||
e["reverses"] = "trsl:entry:conformplanseed"
|
||||
resp = client.post(f"/phases/{phase_id}/ledger", json=e, headers=auth(pg_container["token_a"]))
|
||||
assert resp.status_code == 201, resp.text
|
||||
|
||||
if entry_type == "development-credit":
|
||||
expected_dev += amount
|
||||
elif entry_type == "remission-credit":
|
||||
expected_rem += amount
|
||||
elif entry_type == "credit-reversal":
|
||||
expected_dev -= amount
|
||||
elif entry_type == "administrative-correction-development":
|
||||
expected_dev += amount
|
||||
elif entry_type == "administrative-correction-remission":
|
||||
expected_rem += amount
|
||||
|
||||
exported = client.get(f"/phases/{phase_id}/ledger").json()
|
||||
hashing.verify_chain(exported)
|
||||
result = fold.fold_outstanding_target(manifest["phase"]["initial_target"]["amount"], exported)
|
||||
|
||||
assert result.development_credit == expected_dev
|
||||
assert result.remission_credit == expected_rem
|
||||
assert result.outstanding_target == max(
|
||||
0.0, manifest["phase"]["initial_target"]["amount"] - expected_dev - expected_rem
|
||||
)
|
||||
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "Trust Service reference implementation (PRD Phase 4b)"
|
||||
domain: infotech
|
||||
repo: target-revenue
|
||||
status: active
|
||||
status: finished
|
||||
owner: claude
|
||||
topic_slug: infotech
|
||||
created: "2026-07-29"
|
||||
|
|
@ -317,7 +317,7 @@ offline, 71 passing with Docker; no stray containers left running.
|
|||
|
||||
```task
|
||||
id: TREV-WP-0006-T08
|
||||
status: todo
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "359c9c5e-a49a-40c1-82ea-2bf80e93e7d6"
|
||||
```
|
||||
|
|
@ -327,3 +327,22 @@ multiple concurrent Phases across different repos, the golden Phase
|
|||
example replayed through the hosted API rather than only the offline
|
||||
library, and a specific regression test that hosted and offline folds of
|
||||
the same exported ledger always agree.
|
||||
|
||||
**Result:** `tests/test_hosted_conformance.py` (6 Docker-gated tests)
|
||||
addresses all three named requirements directly: (1) the entire golden
|
||||
Phase (`examples/phase-001/manifest.json` + full 6-entry ledger + all 4
|
||||
extensions) replayed through the hosted API reproduces exactly the
|
||||
36-test-suite-established offline outcome — Development Credit 67000,
|
||||
Remission Credit 33000, Outstanding Target 0, `future_license` MIT — and
|
||||
the hosted Attestation matches it field-for-field; (2) two Phases under
|
||||
two different Licensors (standing in for `coulomb-loop`/`net-kingdom`)
|
||||
operate with interleaved, concurrent-style appends and are proven isolated
|
||||
both positively (each Phase's metrics reflect only its own entries) and
|
||||
negatively (a cross-Licensor write attempt is rejected); (3) a
|
||||
parametrized regression test across four distinct ledger shapes
|
||||
(credits-only, with remission, with a reversal, with both administrative
|
||||
correction types) asserts hosted-append-then-offline-fold always
|
||||
reproduces the exact expected totals, generalizing the single-shape check
|
||||
already added ad hoc in T04. This closes WP-0006 — all 8 tasks done. Full
|
||||
suite: 49 passing offline (plain system Python, no new dependency), 77
|
||||
passing with Docker; no stray containers left running.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue