migrations/0003_attestations.sql: one row per Phase, no UPDATE/DELETE
grant for trf_app (a published attestation is permanent).
src/target_revenue/attestation.py: publish_attestation() is idempotent
(existing row returned unchanged, never regenerated) and derives
conversion_timestamp from the ledger itself via _find_conversion_prefix(),
which finds the earliest prefix where the fold reaches Outstanding
Target = 0 rather than trusting entries[-1] or wall-clock publish time -
so a later unrelated entry can never change an already-converged Phase's
recorded conversion moment. Raises NotConvertedError rather than
fabricating a conversion. Reuses conversion.generate_attestation()
unchanged. Guarded the psycopg import behind TYPE_CHECKING so the pure
helper stays testable under plain system Python.
service/app.py adds GET /phases/{id}/attestation (unauthenticated,
publish-on-first-observation).
tests/test_attestation.py (3 tests, no Docker/Postgres) proves the
earliest-crossing behavior. 6 new Docker-gated tests in
test_ledger_hosting.py cover pre-conversion 404, the core
legal-technical property that /metrics facts/calculations/forecasts
are identical whether or not /attestation was ever called, one-time
publication, signature verification, and DB-level UPDATE/DELETE
rejection.
52 lines
2.1 KiB
Python
52 lines
2.1 KiB
Python
"""Pure, offline tests for target_revenue.attestation (WP-0006-T06).
|
|
|
|
`_find_conversion_prefix` needs no database — it is a pure function over
|
|
(initial_target_amount, entries), like fold.py. Full `publish_attestation`
|
|
persistence/idempotency is covered by the Docker-gated tests in
|
|
tests/test_ledger_hosting.py, since it requires a real Connection.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from conftest import golden_entries, golden_manifest
|
|
|
|
from target_revenue import attestation
|
|
|
|
|
|
def test_find_conversion_prefix_matches_golden_phase_full_sequence():
|
|
manifest = golden_manifest()
|
|
entries = golden_entries()
|
|
initial_amount = manifest["phase"]["initial_target"]["amount"]
|
|
|
|
found = attestation._find_conversion_prefix(initial_amount, entries)
|
|
|
|
assert found is not None
|
|
prefix, conversion_timestamp = found
|
|
assert prefix == entries # golden phase converts exactly at the last entry
|
|
assert conversion_timestamp == entries[-1]["recognized_at"]
|
|
|
|
|
|
def test_find_conversion_prefix_returns_none_when_never_converted():
|
|
manifest = golden_manifest()
|
|
partial_entries = golden_entries()[:4] # Outstanding Target still 45000
|
|
initial_amount = manifest["phase"]["initial_target"]["amount"]
|
|
|
|
assert attestation._find_conversion_prefix(initial_amount, partial_entries) is None
|
|
|
|
|
|
def test_find_conversion_prefix_finds_earliest_crossing_not_the_last_entry():
|
|
"""If a later entry (e.g. an unrelated remission-credit added after
|
|
conversion already occurred) exists, the crossing point must still be
|
|
the earliest one — not the last entry in the list."""
|
|
manifest = golden_manifest()
|
|
entries = golden_entries()
|
|
initial_amount = manifest["phase"]["initial_target"]["amount"]
|
|
|
|
extra = {**entries[-1], "id": "trsl:entry:example0010999", "amount": 1}
|
|
extended = entries + [extra]
|
|
|
|
found = attestation._find_conversion_prefix(initial_amount, extended)
|
|
assert found is not None
|
|
prefix, conversion_timestamp = found
|
|
assert prefix == entries # not `extended` — crossing already happened before `extra`
|
|
assert conversion_timestamp == entries[-1]["recognized_at"]
|