Go-live T05 + WP-0013/0014: first Phase and Control Plane completion
Accept WP-0008-T05 for trsl:phase:info-tech-canon-service-surface (history/260805-T05-GoLive-info-tech-canon.md). Finish WP-0013 remission automation and WP-0014 extension/breach/attestation Control Plane UI. Update SCOPE, README, and pilot-candidate notes for pilot Stage 1.
This commit is contained in:
parent
f56d82f09a
commit
3064c0fe0c
18 changed files with 1676 additions and 72 deletions
|
|
@ -381,3 +381,202 @@ def test_phase_new_form_has_no_ledger_input(client, credentials):
|
|||
assert 'name="repo_hub_uri"' in resp.text
|
||||
assert 'name="repo_id"' in resp.text
|
||||
assert 'name="repo_name"' in resp.text
|
||||
|
||||
|
||||
# --- WP-0014: extensions / breach / attestation UI -------------------------
|
||||
|
||||
|
||||
def _register_phase(client, phase_id, amount="1000"):
|
||||
return client.post(
|
||||
"/phases/new",
|
||||
data={
|
||||
"phase_id": phase_id,
|
||||
"milestone_release_name": "WP-0014 test release",
|
||||
"source_revision": "abc123",
|
||||
"repo_hub": "forgejo-coulomb",
|
||||
"repo_hub_uri": "https://forgejo.coulomb.social",
|
||||
"repo_id": "103",
|
||||
"repo_name": "coulomb/target-revenue",
|
||||
"initial_target_amount": amount,
|
||||
"currency": "USD",
|
||||
"future_license": "MIT",
|
||||
"degeneration_policy": "trsl:policy:linear-longstop-v0@1.0",
|
||||
"longstop_at": "2027-01-01T00:00:00Z",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
|
||||
|
||||
def test_viewer_can_list_extensions_operator_registers_admin_promotes(
|
||||
client, credentials
|
||||
):
|
||||
ext_id = "trsl:extension:wp0014-test-" + uuid.uuid4().hex[:6]
|
||||
version = "1.0"
|
||||
|
||||
_login(client, credentials["viewer"].token)
|
||||
resp = client.get("/extensions")
|
||||
assert resp.status_code == 200
|
||||
assert "Monetization Extension Registry" in resp.text
|
||||
|
||||
# Viewer cannot register
|
||||
denied = client.post(
|
||||
"/extensions",
|
||||
data={
|
||||
"extension_id": ext_id,
|
||||
"version": version,
|
||||
"value_description": "Test extension for WP-0014",
|
||||
"pricing_method": "fixed-fee",
|
||||
"allocation_rule": "100% to Development Credit",
|
||||
"default_rate": "1.0",
|
||||
"recognition_event": "payment-settled",
|
||||
"reversal_rule": "Refunds reverse the credit",
|
||||
"evidence_requirement": "Settled payment reference",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert denied.status_code == 303
|
||||
|
||||
_login(client, credentials["operator"].token)
|
||||
reg = client.post(
|
||||
"/extensions",
|
||||
data={
|
||||
"extension_id": ext_id,
|
||||
"version": version,
|
||||
"value_description": "Test extension for WP-0014",
|
||||
"pricing_method": "fixed-fee",
|
||||
"allocation_rule": "100% to Development Credit",
|
||||
"default_rate": "1.0",
|
||||
"recognition_event": "payment-settled",
|
||||
"reversal_rule": "Refunds reverse the credit",
|
||||
"evidence_requirement": "Settled payment reference",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert reg.status_code == 303
|
||||
assert reg.headers["location"] == "/extensions"
|
||||
|
||||
listed = client.get("/extensions")
|
||||
assert ext_id in listed.text
|
||||
assert "registered" in listed.text
|
||||
|
||||
# Operator cannot promote
|
||||
op_promote = client.post(
|
||||
"/extensions/promote",
|
||||
data={"extension_id": ext_id, "version": version},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert op_promote.status_code == 303
|
||||
|
||||
_login(client, credentials["admin"].token)
|
||||
promote = client.post(
|
||||
"/extensions/promote",
|
||||
data={"extension_id": ext_id, "version": version},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert promote.status_code == 303
|
||||
after = client.get("/extensions")
|
||||
assert "canonical" in after.text
|
||||
|
||||
|
||||
def test_operator_publishes_anonymized_breach(client, credentials):
|
||||
_login(client, credentials["operator"].token)
|
||||
phase_id = "trsl:phase:wp0014-breach-" + uuid.uuid4().hex[:8]
|
||||
assert _register_phase(client, phase_id).status_code == 303
|
||||
|
||||
record_id = "trsl:breach:wp0014-" + uuid.uuid4().hex[:8]
|
||||
resp = client.post(
|
||||
f"/phases/{phase_id}/breach",
|
||||
data={
|
||||
"record_id": record_id,
|
||||
"case_id": "case-001",
|
||||
"event_type": "alleged",
|
||||
"category": "unauthorized-commercial-use",
|
||||
"event_at": "2026-08-01T12:00:00Z",
|
||||
"evidence_reference": "confidential:evidence:case-001",
|
||||
"anonymized": "true",
|
||||
"named_entitlement_holder": "",
|
||||
"named_disclosure_authorized": "false",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert resp.status_code == 303, resp.headers
|
||||
assert resp.headers["location"] == f"/phases/{phase_id}"
|
||||
|
||||
detail = client.get(f"/phases/{phase_id}")
|
||||
assert detail.status_code == 200
|
||||
assert record_id in detail.text
|
||||
assert "unauthorized-commercial-use" in detail.text
|
||||
assert "anonymized" in detail.text
|
||||
|
||||
|
||||
def test_named_breach_requires_cua_authorization(client, credentials):
|
||||
_login(client, credentials["operator"].token)
|
||||
phase_id = "trsl:phase:wp0014-named-" + uuid.uuid4().hex[:8]
|
||||
assert _register_phase(client, phase_id).status_code == 303
|
||||
|
||||
# Missing CUA authorization → rejected (flash danger, still redirect)
|
||||
resp = client.post(
|
||||
f"/phases/{phase_id}/breach",
|
||||
data={
|
||||
"record_id": "trsl:breach:named-fail-" + uuid.uuid4().hex[:6],
|
||||
"case_id": "case-named",
|
||||
"event_type": "determined",
|
||||
"category": "payment-default",
|
||||
"event_at": "2026-08-02T00:00:00Z",
|
||||
"anonymized": "false",
|
||||
"named_entitlement_holder": "Acme Corp",
|
||||
"named_disclosure_authorized": "false",
|
||||
},
|
||||
follow_redirects=True,
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
# Should not show a published named record
|
||||
assert "Acme Corp" not in resp.text or "named_disclosure_authorized" in resp.text.lower() or "rejected" in resp.text.lower() or "must be true" in resp.text.lower() or "danger" in resp.text.lower() or "flash" in resp.text.lower()
|
||||
|
||||
# With authorization → accepted
|
||||
ok = client.post(
|
||||
f"/phases/{phase_id}/breach",
|
||||
data={
|
||||
"record_id": "trsl:breach:named-ok-" + uuid.uuid4().hex[:6],
|
||||
"case_id": "case-named",
|
||||
"event_type": "determined",
|
||||
"category": "payment-default",
|
||||
"event_at": "2026-08-02T00:00:00Z",
|
||||
"anonymized": "false",
|
||||
"named_entitlement_holder": "Acme Corp",
|
||||
"named_disclosure_authorized": "true",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert ok.status_code == 303
|
||||
detail = client.get(f"/phases/{phase_id}")
|
||||
assert "Acme Corp" in detail.text
|
||||
|
||||
|
||||
def test_attestation_shown_after_conversion(client, credentials):
|
||||
_login(client, credentials["operator"].token)
|
||||
phase_id = "trsl:phase:wp0014-attest-" + uuid.uuid4().hex[:8]
|
||||
assert _register_phase(client, phase_id, amount="100").status_code == 303
|
||||
|
||||
before = client.get(f"/phases/{phase_id}")
|
||||
assert "Not converted" in before.text or "no attestation" in before.text.lower()
|
||||
|
||||
client.post(
|
||||
f"/phases/{phase_id}/ledger",
|
||||
data={
|
||||
"entry_id": "trsl:entry:wp0014full" + uuid.uuid4().hex[:6],
|
||||
"amount": "100",
|
||||
"currency": "USD",
|
||||
"recognized_at": "2026-08-01T00:00:00Z",
|
||||
"evidence_reference": "confidential:evidence:full",
|
||||
"extension_id": "trsl:extension:development-license",
|
||||
"extension_version": "1.0",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
|
||||
after = client.get(f"/phases/{phase_id}")
|
||||
assert after.status_code == 200
|
||||
assert "Conversion Attestation" in after.text
|
||||
assert "MIT" in after.text # future_license
|
||||
assert "ledger_checkpoint" in after.text or "Ledger checkpoint" in after.text
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue