Close HUB-WP-0009 conformance gaps (C2, C7, C9, C10); mark blocked workplans
Implements the four residual conformance checks left open by the T04
minimal vertical:
- C2: GET /ports/registry/registrations/{hub_slug} resolves missing (404),
ambiguous (shared reuse_surface_id across hub_slugs), and stale
(deprecated/retired descriptor) registrations; a new .../audit route
exposes queryable registration history from the existing in-memory
history and the PostgreSQL runtime_audit_ledger.
- C7: harness proof that disabled compatibility groups deny access
(404) with no fixture credentials involved, matching the existing
fail-closed compat router behavior.
- C9: harness proof plus a dedicated test that /readyz degrades only on
an unavailable configured dependency while unrelated disabled
projections stay non-blocking.
- C10: ContractValidator now negotiates contract_version_min/max against
the runtime's contract version and rejects incompatible or inverted
ranges with an explicit 422 instead of silently accepting them.
HUB-WP-0009 is now finished. HUB-WP-0006 is marked blocked: its only open
task (T06) has no remaining hub-core code path and waits on an external
Forgejo identity/production deployment gate. HUB-WP-0011 is marked
blocked: T02/T03 already waited on external credential/deployment
review, and T01 needs a source/destination ownership and retention
decision against live message data before it can be implemented safely.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 310936@bnt-lap001
Assistant-Session: 00cd9abe-09a0-416b-88e0-f907b9101629
This commit is contained in:
parent
b0e89592c6
commit
e89d621f18
11 changed files with 496 additions and 20 deletions
|
|
@ -18,19 +18,23 @@ def test_implemented_tier_2_and_3_profile_passes_reference_runtime() -> None:
|
|||
report = ConformanceHarness(isolated_target()).run()
|
||||
|
||||
assert report.passed
|
||||
assert report.passed_count == 8
|
||||
assert report.passed_count == 12
|
||||
assert {check.check_id for check in report.checks} == {
|
||||
"C1",
|
||||
"C2",
|
||||
"C3",
|
||||
"C4",
|
||||
"C5",
|
||||
"C6",
|
||||
"C7",
|
||||
"C8",
|
||||
"C9",
|
||||
"C10",
|
||||
"F2",
|
||||
"F3",
|
||||
}
|
||||
assert all(check.status == "pass" for check in report.checks)
|
||||
assert report.to_dict()["summary"] == {"passed": 8, "total": 8}
|
||||
assert report.to_dict()["summary"] == {"passed": 12, "total": 12}
|
||||
|
||||
|
||||
def test_projection_rebuild_scenario_leaves_separate_provenance_bearing_views() -> None:
|
||||
|
|
|
|||
|
|
@ -113,6 +113,108 @@ def test_registry_rejects_contract_mismatch() -> None:
|
|||
assert "reuse_surface_id must match" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_registry_rejects_incompatible_contract_version_range() -> None:
|
||||
runtime = client()
|
||||
package = ops_hub_package()
|
||||
package["descriptor"]["contract_version_min"] = "9.9.9"
|
||||
package["descriptor"]["contract_version_max"] = "9.9.9"
|
||||
|
||||
response = runtime.post(
|
||||
"/ports/registry/registrations",
|
||||
headers={"X-Correlation-ID": str(uuid4())},
|
||||
json=package,
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert "incompatible with runtime contract version" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_registry_rejects_inverted_contract_version_range() -> None:
|
||||
runtime = client()
|
||||
package = ops_hub_package()
|
||||
package["descriptor"]["contract_version_min"] = "0.2.0"
|
||||
package["descriptor"]["contract_version_max"] = "0.1.0"
|
||||
|
||||
response = runtime.post(
|
||||
"/ports/registry/registrations",
|
||||
headers={"X-Correlation-ID": str(uuid4())},
|
||||
json=package,
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert "must not exceed" in response.json()["detail"]
|
||||
|
||||
|
||||
def test_registry_resolution_reports_missing_ambiguous_and_audit_history() -> None:
|
||||
runtime = client()
|
||||
package = ops_hub_package()
|
||||
runtime.post(
|
||||
"/ports/registry/registrations",
|
||||
headers={"X-Correlation-ID": str(uuid4())},
|
||||
json=package,
|
||||
)
|
||||
|
||||
missing = runtime.get("/ports/registry/registrations/never-registered")
|
||||
ok_resolution = runtime.get("/ports/registry/registrations/ops-hub")
|
||||
ok_audit = runtime.get("/ports/registry/registrations/ops-hub/audit")
|
||||
|
||||
assert missing.status_code == 404
|
||||
assert ok_resolution.status_code == 200
|
||||
assert ok_resolution.json()["data"]["resolution"] == "ok"
|
||||
assert ok_audit.status_code == 200
|
||||
assert len(ok_audit.json()["items"]) == 1
|
||||
assert ok_audit.json()["items"][0]["data"]["action"] == "registry.accepted"
|
||||
|
||||
duplicate_package = {**package, "descriptor": {**package["descriptor"], "hub_slug": "ops-hub-2"}}
|
||||
runtime.post(
|
||||
"/ports/registry/registrations",
|
||||
headers={"X-Correlation-ID": str(uuid4())},
|
||||
json=duplicate_package,
|
||||
)
|
||||
|
||||
ambiguous_resolution = runtime.get("/ports/registry/registrations/ops-hub")
|
||||
|
||||
assert ambiguous_resolution.json()["data"]["resolution"] == "ambiguous"
|
||||
assert ambiguous_resolution.json()["data"]["ambiguous_with"] == ["ops-hub-2"]
|
||||
|
||||
|
||||
def test_registry_resolution_reports_stale_for_deprecated_descriptor() -> None:
|
||||
runtime = client()
|
||||
package = ops_hub_package()
|
||||
package["descriptor"]["status"] = "deprecated"
|
||||
runtime.post(
|
||||
"/ports/registry/registrations",
|
||||
headers={"X-Correlation-ID": str(uuid4())},
|
||||
json=package,
|
||||
)
|
||||
|
||||
resolution = runtime.get("/ports/registry/registrations/ops-hub")
|
||||
|
||||
assert resolution.json()["data"]["resolution"] == "stale"
|
||||
|
||||
|
||||
def test_readiness_blocks_on_unavailable_dependency_but_not_disabled_ones() -> None:
|
||||
class RejectingProjectionClient:
|
||||
async def fetch_classification_page(self, cursor: str | None):
|
||||
raise RuntimeError("upstream unavailable")
|
||||
|
||||
settings = RuntimeSettings(environment="test", backend="memory", allow_ephemeral=True)
|
||||
runtime = TestClient(
|
||||
create_app(
|
||||
settings=settings,
|
||||
port_store=InMemoryPortStore(),
|
||||
repo_projection_client=RejectingProjectionClient(),
|
||||
)
|
||||
)
|
||||
|
||||
response = runtime.get("/readyz")
|
||||
|
||||
assert response.status_code == 503
|
||||
assert response.json()["status"] == "degraded"
|
||||
assert response.json()["checks"]["repo_manager_projection"] == "unavailable"
|
||||
assert response.json()["checks"]["workload_projection"] == "not_applicable"
|
||||
|
||||
|
||||
def test_messaging_port_writes_and_reads_conversation() -> None:
|
||||
runtime = client()
|
||||
conversation_id = uuid4()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue