Schema: phase.milestone_release gains required repo_hub/repo_hub_uri/
repo_id/repo_name; phase gains optional base_phase_id. Backfilled the
golden phase-001 fixture (placeholder values, it's synthetic) and the
three real pilot-candidate manifests with actual Forgejo repo ids
confirmed live (net-kingdom=67, vergabe-teilnahme=62,
info-tech-canon=47, all coulomb/*).
Discovered along the way: the schema change alone would have broken
the existing Control Plane registration form, since nothing collected
the four new fields. Fixed inline rather than leaving it broken
between tasks -- this also completes T04's ledger UI change (drop the
hand-typed ledger input, auto-compute /phases/{id}/ledger, add a
drill-down reference link on phase_detail.html) since both changes
touch the same form/route.
Full suite: 94 passing offline (was 84), 158 passing with Docker
(was 146).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
90 lines
3.1 KiB
Python
90 lines
3.1 KiB
Python
import copy
|
|
|
|
import pytest
|
|
from conftest import golden_manifest
|
|
|
|
from target_revenue import validation
|
|
|
|
|
|
def test_golden_manifest_is_conformant():
|
|
validation.validate_phase_manifest(golden_manifest())
|
|
|
|
|
|
def test_manifest_missing_longstop_is_rejected():
|
|
manifest = copy.deepcopy(golden_manifest())
|
|
del manifest["phase"]["longstop_at"]
|
|
with pytest.raises(validation.ConformanceError):
|
|
validation.validate_phase_manifest(manifest)
|
|
|
|
|
|
def test_manifest_bad_future_license_is_rejected():
|
|
manifest = copy.deepcopy(golden_manifest())
|
|
manifest["phase"]["future_license"] = "GPL-3.0"
|
|
with pytest.raises(validation.ConformanceError):
|
|
validation.validate_phase_manifest(manifest)
|
|
|
|
|
|
def test_manifest_missing_required_field_is_rejected():
|
|
manifest = copy.deepcopy(golden_manifest())
|
|
del manifest["phase"]["initial_target"]
|
|
with pytest.raises(validation.ConformanceError):
|
|
validation.validate_phase_manifest(manifest)
|
|
|
|
|
|
def test_manifest_rejects_unknown_top_level_field():
|
|
manifest = copy.deepcopy(golden_manifest())
|
|
manifest["unexpected_field"] = True
|
|
with pytest.raises(validation.ConformanceError):
|
|
validation.validate_phase_manifest(manifest)
|
|
|
|
|
|
def test_initial_target_amount_immutable_across_versions():
|
|
published = golden_manifest()
|
|
candidate = copy.deepcopy(published)
|
|
candidate["phase"]["initial_target"]["amount"] = 70000
|
|
errors = validation.check_manifest_immutability(published, candidate)
|
|
assert errors, "reducing initial_target.amount must be flagged"
|
|
assert "initial_target.amount" in errors[0]
|
|
|
|
|
|
def test_phase_id_immutable_across_versions():
|
|
published = golden_manifest()
|
|
candidate = copy.deepcopy(published)
|
|
candidate["phase"]["id"] = "trsl:phase:example-002"
|
|
errors = validation.check_manifest_immutability(published, candidate)
|
|
assert errors
|
|
assert "phase.id" in errors[0]
|
|
|
|
|
|
def test_unchanged_manifest_has_no_immutability_violations():
|
|
manifest = golden_manifest()
|
|
assert validation.check_manifest_immutability(manifest, manifest) == []
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"field", ["repo_hub", "repo_hub_uri", "repo_id", "repo_name"]
|
|
)
|
|
def test_manifest_missing_repo_provenance_field_is_rejected(field):
|
|
manifest = copy.deepcopy(golden_manifest())
|
|
del manifest["phase"]["milestone_release"][field]
|
|
with pytest.raises(validation.ConformanceError):
|
|
validation.validate_phase_manifest(manifest)
|
|
|
|
|
|
def test_manifest_with_absent_base_phase_id_is_conformant():
|
|
manifest = copy.deepcopy(golden_manifest())
|
|
assert "base_phase_id" not in manifest["phase"]
|
|
validation.validate_phase_manifest(manifest)
|
|
|
|
|
|
def test_manifest_with_present_base_phase_id_is_conformant():
|
|
manifest = copy.deepcopy(golden_manifest())
|
|
manifest["phase"]["base_phase_id"] = "trsl:phase:example-000"
|
|
validation.validate_phase_manifest(manifest)
|
|
|
|
|
|
def test_manifest_with_malformed_base_phase_id_is_rejected():
|
|
manifest = copy.deepcopy(golden_manifest())
|
|
manifest["phase"]["base_phase_id"] = "not-a-phase-id"
|
|
with pytest.raises(validation.ConformanceError):
|
|
validation.validate_phase_manifest(manifest)
|