"""Control-plane MCP server runtime registry (#949). The registry is the half of the fleet evidence a caller cannot supply: each server writes exactly one row about itself at native transport bind. These tests pin the storage contract — additive migration, deterministic ordering, and PID-reuse/retention pruning that cannot hide a live duplicate. """ import os import tempfile import unittest from datetime import datetime, timedelta, timezone from unittest import mock import control_plane_db import mcp_fleet_inventory as mfi def _stamp(delta_seconds=0): moment = datetime.now(timezone.utc) + timedelta(seconds=delta_seconds) return moment.replace(microsecond=0).strftime("%Y-%m-%dT%H:%M:%SZ") class _DBCase(unittest.TestCase): def setUp(self): self._tmp = tempfile.TemporaryDirectory() self.addCleanup(self._tmp.cleanup) self.db_path = os.path.join(self._tmp.name, "control_plane.sqlite3") self.db = control_plane_db.ControlPlaneDB(db_path=self.db_path) def record(self, namespace, profile, role, pid, **overrides): base = mfi.build_process_runtime_record( namespace=namespace, profile=profile, role=role, remote="prgs", org="Scaled-Tech-Consulting", repo="Gitea-Tools", repository_root="/checkout/Gitea-Tools", pid=pid, startup_head="82d71b77028a7abd4f8ab4a4e4d89658a187f73d", transport="stdio", client_provenance="client_managed", env={mfi.COHORT_ID_ENV: "ppid:40990"}, boot_id=f"boot{pid}", ) base.update(overrides) return base class TestSchema(_DBCase): def test_schema_version_is_bumped(self): self.assertGreaterEqual(control_plane_db.SCHEMA_VERSION, 6) def test_runtime_table_exists_on_a_fresh_database(self): self.assertEqual(self.db.list_mcp_server_runtimes(), []) def test_migration_is_additive_and_idempotent(self): """Re-opening an existing DB must not disturb the other tables.""" self.db.upsert_session(session_id="s-a", role="author", profile="prgs-author") self.db.register_mcp_server_runtime( self.record("gitea-author", "prgs-author", "author", 41000) ) reopened = control_plane_db.ControlPlaneDB(db_path=self.db_path) self.assertEqual(len(reopened.list_sessions()), 1) self.assertEqual(len(reopened.list_mcp_server_runtimes()), 1) class TestRegistration(_DBCase): def test_registration_round_trips_every_field(self): record = self.record("gitea-controller", "prgs-controller", "controller", 41001) stored = self.db.register_mcp_server_runtime(record) for key in ( "runtime_id", "namespace", "profile", "role", "remote", "org", "repo", "repository_root", "pid", "cohort_id", "cohort_source", "client_provenance", "boot_id", "startup_head", "transport", "status", ): self.assertEqual(stored[key], record[key], key) def test_registration_requires_a_runtime_id(self): record = self.record("gitea-author", "prgs-author", "author", 41002) record["runtime_id"] = "" with self.assertRaises(ValueError): self.db.register_mcp_server_runtime(record) def test_registration_requires_a_namespace(self): record = self.record("gitea-author", "prgs-author", "author", 41003) record["namespace"] = " " with self.assertRaises(ValueError): self.db.register_mcp_server_runtime(record) def test_registration_requires_a_pid(self): record = self.record("gitea-author", "prgs-author", "author", 41004) record["pid"] = None with self.assertRaises(ValueError): self.db.register_mcp_server_runtime(record) def test_five_members_register_independently(self): for index, entry in enumerate(mfi.EXPECTED_PRGS_FLEET): self.db.register_mcp_server_runtime( self.record( entry["namespace"], entry["profile"], entry["role"], 41100 + index ) ) rows = self.db.list_mcp_server_runtimes() self.assertEqual(len(rows), 5) self.assertEqual( sorted(r["profile"] for r in rows), [ "prgs-author", "prgs-controller", "prgs-merger", "prgs-reconciler", "prgs-reviewer", ], ) class TestPruning(_DBCase): def test_reusing_a_pid_replaces_the_stale_row(self): first = self.record("gitea-author", "prgs-author", "author", 41200) self.db.register_mcp_server_runtime(first) second = self.record("gitea-reviewer", "prgs-reviewer", "reviewer", 41200) self.db.register_mcp_server_runtime(second) rows = self.db.list_mcp_server_runtimes() self.assertEqual(len(rows), 1) self.assertEqual(rows[0]["runtime_id"], second["runtime_id"]) def test_pruning_never_removes_a_live_duplicate_on_another_pid(self): """The defect this must not have: hiding a second running server.""" first = self.record("gitea-author", "prgs-author", "author", 41300) self.db.register_mcp_server_runtime(first) second = self.record("gitea-author", "prgs-author", "author", 41301) self.db.register_mcp_server_runtime( second, retention_seconds=mfi.RUNTIME_RETENTION_SECONDS ) rows = self.db.list_mcp_server_runtimes() self.assertEqual(len(rows), 2) self.assertEqual(sorted(r["pid"] for r in rows), [41300, 41301]) def test_retention_drops_rows_older_than_the_window(self): old = self.record( "gitea-merger", "prgs-merger", "merger", 41400, registered_at=_stamp(-(mfi.RUNTIME_RETENTION_SECONDS + 3600)), ) self.db.register_mcp_server_runtime(old) fresh = self.record("gitea-author", "prgs-author", "author", 41401) self.db.register_mcp_server_runtime( fresh, retention_seconds=mfi.RUNTIME_RETENTION_SECONDS ) rows = self.db.list_mcp_server_runtimes() self.assertEqual([r["pid"] for r in rows], [41401]) def test_retention_is_skipped_when_not_requested(self): old = self.record( "gitea-merger", "prgs-merger", "merger", 41500, registered_at=_stamp(-(mfi.RUNTIME_RETENTION_SECONDS + 3600)), ) self.db.register_mcp_server_runtime(old) self.db.register_mcp_server_runtime( self.record("gitea-author", "prgs-author", "author", 41501) ) self.assertEqual(len(self.db.list_mcp_server_runtimes()), 2) class TestListing(_DBCase): def test_listing_is_deterministically_ordered(self): for index in range(5): self.db.register_mcp_server_runtime( self.record( "gitea-author", "prgs-author", "author", 41600 + index, registered_at="2026-07-28T02:00:00Z", ) ) first = [r["runtime_id"] for r in self.db.list_mcp_server_runtimes()] second = [r["runtime_id"] for r in self.db.list_mcp_server_runtimes()] self.assertEqual(first, second) self.assertEqual(first, sorted(first)) def test_status_filter_selects_running_rows(self): running = self.record("gitea-author", "prgs-author", "author", 41700) self.db.register_mcp_server_runtime(running) stopped = self.record("gitea-merger", "prgs-merger", "merger", 41701) self.db.register_mcp_server_runtime(stopped) self.db.mark_mcp_server_runtime_stopped(stopped["runtime_id"]) rows = self.db.list_mcp_server_runtimes(statuses=("running",)) self.assertEqual([r["pid"] for r in rows], [41700]) def test_listing_without_a_filter_returns_stopped_rows_too(self): stopped = self.record("gitea-merger", "prgs-merger", "merger", 41800) self.db.register_mcp_server_runtime(stopped) self.db.mark_mcp_server_runtime_stopped(stopped["runtime_id"]) rows = self.db.list_mcp_server_runtimes() self.assertEqual(len(rows), 1) self.assertEqual(rows[0]["status"], "stopped") class TestHeartbeat(_DBCase): def test_heartbeat_advances_only_the_timestamp(self): record = self.record( "gitea-author", "prgs-author", "author", 41900, last_heartbeat_at="2026-07-28T02:00:00Z", ) self.db.register_mcp_server_runtime(record) self.db.heartbeat_mcp_server_runtime(record["runtime_id"]) row = self.db.list_mcp_server_runtimes()[0] self.assertNotEqual(row["last_heartbeat_at"], "2026-07-28T02:00:00Z") self.assertEqual(row["status"], "running") self.assertEqual(row["pid"], 41900) def test_heartbeat_for_an_unknown_runtime_is_a_no_op(self): self.db.heartbeat_mcp_server_runtime("does-not-exist") self.assertEqual(self.db.list_mcp_server_runtimes(), []) class TestEndToEndSnapshot(_DBCase): """Registry rows feed the classifier without reshaping.""" def test_registered_fleet_classifies_as_healthy(self): pids = [] for index, entry in enumerate(mfi.EXPECTED_PRGS_FLEET): pid = 42000 + index pids.append(pid) self.db.register_mcp_server_runtime( self.record( entry["namespace"], entry["profile"], entry["role"], pid, registered_at="2026-07-28T02:00:00Z", ) ) rows = self.db.list_mcp_server_runtimes(statuses=("running",)) scan = { "available": True, "processes": [ {"pid": pid, "started_at": "2026-07-28T01:00:00Z"} for pid in pids ], "reason": None, } with mock.patch.object(mfi, "probe_pid_alive", return_value=True): result = mfi.classify_fleet_inventory( runtime_rows=rows, process_scan=scan, expected_binding={ "remote": "prgs", "org": "Scaled-Tech-Consulting", "repo": "Gitea-Tools", }, ) self.assertTrue(result["inventory_complete"]) self.assertTrue(result["mutation_gate_satisfied"]) self.assertEqual(result["running_member_count"], 5) if __name__ == "__main__": unittest.main()