target-revenue/tests/test_forgejo_hubs.py
tegwick de308f8947 Complete WP-0015: forgejo_hubs migration + closeout (T05/T07)
migrations/0007_forgejo_hubs.sql: table forgejo_hubs, auto-populated
via a BEFORE INSERT trigger on phase_manifests that reads repo_hub/
repo_hub_uri straight out of the manifest JSONB (no top-level columns
needed). ON CONFLICT DO NOTHING -- a hub already seen is left alone;
correcting a URI is a SECURITY DEFINER governance action
(correct_forgejo_hub_uri), not a plain UPDATE, matching every other
governance-action pattern in this project. Thin Python wrappers added
to registry.py.

tests/test_forgejo_hubs.py (6 Docker-gated tests) and
tests/test_reference_docs.py (13 tests, no Docker needed -- smoke-tests
every real specs/policies/specs/profiles/ file, not just the two
exercised incidentally by T03's Control Plane tests).

All seven WP-0015 tasks done; workplan marked finished. Final suite:
94 passing offline, 183 passing under the service extras venv. No
stray Docker containers left running.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 23:57:46 +02:00

177 lines
6.1 KiB
Python

"""Integration tests for WP-0015-T05 (`forgejo_hubs` registry).
Same ephemeral, disposable Postgres-via-Docker pattern as the other
hosted test modules (never the shared state-hub instance).
"""
from __future__ import annotations
import copy
import shutil
import subprocess
import time
import uuid
from pathlib import Path
import pytest
psycopg = pytest.importorskip("psycopg")
from conftest import 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",
REPO_ROOT / "migrations" / "0004_breach_records.sql",
REPO_ROOT / "migrations" / "0005_licensor_credentials.sql",
REPO_ROOT / "migrations" / "0006_control_plane.sql",
REPO_ROOT / "migrations" / "0007_forgejo_hubs.sql",
]
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-hubs-{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()
conn.execute(
"INSERT INTO licensors (token, licensor_id, credential_label, rights, issued_by) "
"VALUES (%s, %s, %s, %s, %s)",
("founding-token", "binky", "founder", "operator", "bootstrap"),
)
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 {"admin_dsn": dsn, "app_dsn": app_dsn, "founding_token": "founding-token"}
finally:
subprocess.run(["docker", "stop", name], capture_output=True)
@pytest.fixture()
def conn(pg_container):
with psycopg.connect(pg_container["app_dsn"]) as connection:
yield connection
@pytest.fixture()
def licensor(conn, pg_container):
from target_revenue import registry
return registry.authenticate(conn, pg_container["founding_token"])
def _manifest_with_hub(hub_slug: str, hub_uri: str, phase_suffix: str) -> dict:
manifest = copy.deepcopy(golden_manifest())
manifest["phase"]["id"] = manifest["phase"]["id"] + "-hubtest-" + phase_suffix
manifest["phase"]["milestone_release"]["repo_hub"] = hub_slug
manifest["phase"]["milestone_release"]["repo_hub_uri"] = hub_uri
return manifest
def test_registering_phase_auto_creates_hub_row(conn, licensor):
from target_revenue import registry
suffix = uuid.uuid4().hex[:8]
manifest = _manifest_with_hub(f"hub-{suffix}", "https://forgejo.example.invalid", suffix)
registry.register_phase_manifest(conn, licensor, manifest)
conn.commit()
hub = registry.get_forgejo_hub(conn, f"hub-{suffix}")
assert hub is not None
assert hub["service_uri"] == "https://forgejo.example.invalid"
def test_unknown_hub_returns_none(conn):
from target_revenue import registry
assert registry.get_forgejo_hub(conn, "no-such-hub") is None
def test_second_phase_same_hub_does_not_overwrite(conn, licensor):
"""ON CONFLICT DO NOTHING: a hub already seen is left alone by the
auto-populate trigger -- only correct_forgejo_hub_uri() may change it."""
from target_revenue import registry
suffix = uuid.uuid4().hex[:8]
hub_slug = f"hub-{suffix}"
manifest_a = _manifest_with_hub(hub_slug, "https://first-seen.example.invalid", suffix + "a")
registry.register_phase_manifest(conn, licensor, manifest_a)
conn.commit()
manifest_b = _manifest_with_hub(hub_slug, "https://different-uri.example.invalid", suffix + "b")
registry.register_phase_manifest(conn, licensor, manifest_b)
conn.commit()
hub = registry.get_forgejo_hub(conn, hub_slug)
assert hub["service_uri"] == "https://first-seen.example.invalid"
def test_correct_forgejo_hub_uri_updates_existing_hub(conn, licensor):
from target_revenue import registry
suffix = uuid.uuid4().hex[:8]
hub_slug = f"hub-{suffix}"
manifest = _manifest_with_hub(hub_slug, "https://old-domain.example.invalid", suffix)
registry.register_phase_manifest(conn, licensor, manifest)
conn.commit()
registry.correct_forgejo_hub_uri(conn, hub_slug, "https://new-domain.example.invalid")
conn.commit()
hub = registry.get_forgejo_hub(conn, hub_slug)
assert hub["service_uri"] == "https://new-domain.example.invalid"
def test_correct_forgejo_hub_uri_unknown_hub_is_rejected(conn):
from target_revenue import registry
with pytest.raises(registry.RegistrationError, match="unknown forgejo hub"):
registry.correct_forgejo_hub_uri(conn, "never-registered", "https://x.example.invalid")
def test_application_role_cannot_update_forgejo_hubs_directly(pg_container):
with psycopg.connect(pg_container["app_dsn"]) as app_conn:
with pytest.raises(psycopg.errors.InsufficientPrivilege):
app_conn.execute(
"UPDATE forgejo_hubs SET service_uri = 'https://tampered.invalid' "
"WHERE hub_slug = 'anything'"
)
app_conn.rollback()