from types import SimpleNamespace from backend.app.services.mcp_hub_deployment_reconciliation import ( McpHubDeploymentReconciliation, ) class FakeResponse: def __init__(self, payload): self.payload = payload def json(self): return self.payload class FakePortainer: def __init__(self, exit_code=0): self.exit_code = exit_code self.calls = [] def get_container_status(self, name): assert name == "gitea-deploy-control" return { "status": "ok", "container": { "id": "controller-id", "name": "gitea-deploy-control", "state": "running", }, } def _config(self): return SimpleNamespace(endpoint_id="3") def _request(self, method, path, **kwargs): self.calls.append((method, path, kwargs)) if path.endswith("/containers/controller-id/exec"): return FakeResponse({"Id": "exec-1"}) if path.endswith("/exec/exec-1/start"): return FakeResponse({}) if path.endswith("/exec/exec-1/json"): return FakeResponse({"Running": False, "ExitCode": self.exit_code}) raise AssertionError(path) def test_reconciliation_is_fixed_to_mcp_hub_and_reconciles_only_verified_failed_forward_state(): fake = FakePortainer() result = McpHubDeploymentReconciliation(fake).run() assert result == { "status": "reconciled", "repository": "jens/itworx_mcp_hub", "composeProject": "itworx-mcp-hub", } create = fake.calls[0] command = create[2]["json"]["Cmd"] assert command[:2] == ["python3", "-c"] assert command[3:] == ["jens/itworx_mcp_hub", "itworx-mcp-hub"] assert create[2]["json"]["AttachStdout"] is False assert create[2]["json"]["AttachStderr"] is False script = command[2] assert "/controller-config/projects.json" in script assert "/controller-hub-releases/controller-config/projects.json" in script assert "os.path.samefile(registry_path, host_registry_path)" in script assert "/mnt/cache/appdata/gitea-deploy-runners/state" in script assert "/srv/example/appdata/gitea-deploy-runners/deploy.lock" not in script assert "/mnt/cache/appdata/gitea-deploy-runners/deploy.lock" in script assert 'project.get("branch") != "main"' in script assert 'project.get("compose_project") != expected_compose_project' in script assert 'recorded_status != "failed"' in script assert 'recorded_live_status not in {"healthy", "unknown"}' in script assert 'attempt_status != "failed"' in script assert 'attempt_revision == recorded_live' in script assert 'git_run("merge-base", "--is-ancestor", recorded_live, attempt_revision)' in script assert 'git_run("merge-base", "--is-ancestor", attempt_revision, main_revision)' in script def test_unknown_live_status_is_accepted_only_for_a_proven_snapshot_stage_failure_without_rollback_error(): fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] assert 'last_attempt_message = str(last_attempt.get("message") or "")' in script assert "snapshot_failure_prefixes = (" in script assert 'recorded_live_status == "unknown"' in script assert 'last_attempt_message.startswith(snapshot_failure_prefixes)' in script assert '"; rollback error:" in last_attempt_message' in script assert '"; source rollback error:" in last_attempt_message' in script assert "raise SystemExit(55)" in script def test_reconciliation_proves_complete_live_compose_topology_and_preserves_actual_image_ids(): fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] assert 'expected_containers = project.get("containers")' in script assert "set(project_names) != set(expected_containers)" in script assert 'labels.get("com.docker.compose.project") != expected_compose_project' in script assert 'not runtime_state.get("Running")' in script assert 'health in {"starting", "unhealthy"}' in script assert 'image_id = str(container.get("Image") or "")' in script assert '"containers": container_artifacts' in script assert '"compose_sha256": ""' in script assert '"topology_contract": topology_contract' in script assert '"live_legacy_provenance": True' in script assert '"live_artifacts": artifacts' in script assert '"live_revision": recorded_live' in script def test_reconciliation_recognizes_only_the_fixed_mcp_hub_oci_source_as_owned(): fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] assert 'owned_image_source = "https://gitea.example.com/jens/itworx-mcp-hub"' in script assert "if image_source == owned_image_source:" in script assert 'git_run("merge-base", "--is-ancestor", recorded_live, image_revision)' in script assert 'git_run("merge-base", "--is-ancestor", image_revision, main_revision)' in script assert 'image_source.startswith("https://gitea.example.com/")' not in script assert 'image_source == repository' not in script assert '"live_revision": main_revision' not in script assert '"live_revision": attempt_revision' not in script def test_reconciliation_requires_the_controller_17_service_topology_migration(): result = McpHubDeploymentReconciliation(FakePortainer(exit_code=56)).run() assert result == { "status": "topology_migration_required", "repository": "jens/itworx_mcp_hub", "composeProject": "itworx-mcp-hub", "reason": "MCP Hub 15-to-17 topology migration requires its guarded protected-main deployment", } fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] assert 'topology_contract = "itworx.mcp-hub-topology/17-v1"' in script assert 'recorded_artifacts.get("topology_contract") == topology_contract' in script assert '"topology_contract": topology_contract' in script def test_owned_image_ancestry_can_fail_closed_to_the_canonical_gitea_store(): fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] assert 'canonical_repository_root = Path("/mnt/cache/appdata/gitea/git/repositories")' in script assert 'canonical_bare_repo = canonical_repository_root / owner / f"{name}.git"' in script assert "def canonical_git_run(*args):" in script assert "def canonical_image_ancestry_is_proven(" in script assert 'resolved_recorded = canonical_git_run("rev-parse", f"{recorded_revision}^{{commit}}")' in script assert 'resolved_image = canonical_git_run("rev-parse", f"{image_revision}^{{commit}}")' in script assert 'resolved_main = canonical_git_run("rev-parse", "refs/heads/main")' in script assert 'resolved_main.stdout.strip() == expected_main' in script assert '"merge-base", "--is-ancestor", recorded_revision, image_revision' in script assert '"merge-base", "--is-ancestor", image_revision, expected_main' in script assert "if not mounted_ancestry_proven and not canonical_image_ancestry_is_proven(" in script assert "canonical_bare_repo.is_symlink()" in script def test_unchanged_recorded_image_ids_do_not_reinterpret_historical_oci_revision_labels(): fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] change_probe = "image_changed = image_id != recorded_image_id" provenance_gate = "if image_changed:" compat_probe = "contextforge_compat_image = main_declares_contextforge_compat_image(name, image_ref)" dataplane_probe = "main_declares_sessionless_dataplane_image(name, image_ref)" owned_probe = "elif image_source == owned_image_source:" external_probe = "elif not main_declares_external_image(image_ref):" assert change_probe in script assert provenance_gate in script assert compat_probe in script assert dataplane_probe in script assert "not already_reconciled" in script assert owned_probe in script assert external_probe in script assert script.index(change_probe) < script.index(provenance_gate) assert script.index(provenance_gate) < script.index(compat_probe) assert script.index(compat_probe) < script.index(dataplane_probe) assert script.index(dataplane_probe) < script.index(owned_probe) assert script.index(owned_probe) < script.index(external_probe) assert "if image_id != recorded_image_id or container_id != recorded_container_id:" in script def test_external_image_drift_requires_an_immutable_digest_declared_in_reviewed_main_compose(): fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] assert "def main_declares_external_image(image_ref):" in script assert "@sha256:" in script assert 'git_run("show", f"{main_revision}:{compose_path}")' in script assert '"infrastructure/compose/docker-compose.yml"' in script assert '"infrastructure/compose/docker-compose.prod.yml"' in script assert 'line.strip() in {f"image: {image_ref}", f\'image: "{image_ref}"\', f"image: \'{image_ref}\'"}' in script assert "main_declares_external_image(image_ref)" in script assert "elif not main_declares_external_image(image_ref):" in script def test_contextforge_compat_image_drift_requires_exact_reviewed_main_contract(): fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] assert 'container_name != "itworx-mcp-hub-contextforge-1"' in script assert 'f"{main_revision}:infrastructure/images/contextforge/compat-image.env"' in script assert 'f"{main_revision}:scripts/build_contextforge_compat_image.sh"' in script assert '"CONTEXTFORGE_UPSTREAM_COMMIT"' in script assert '"CONTEXTFORGE_ALEMBIC_HEAD"' in script assert '"CONTEXTFORGE_MCP_SDK"' in script assert '"CONTEXTFORGE_LOCAL_IMAGE"' in script assert 're.fullmatch(r"[0-9a-f]{40}", upstream_commit)' in script assert 're.fullmatch(r"[0-9a-f]{12}", alembic_head)' in script assert 'local_image != f"itworx-mcp-hub/contextforge-schema:{alembic_head}"' in script assert 'git -C "$SRC" fetch --depth 1 origin "$CONTEXTFORGE_UPSTREAM_COMMIT"' in script assert 'git -C "$SRC" checkout --detach FETCH_HEAD' in script assert 'MCP_REQUIREMENT="mcp==${CONTEXTFORGE_MCP_SDK}"' in script assert 'alembic -c mcpgateway/alembic.ini heads' in script assert 'docker tag "$STAGED_IMAGE" "$CONTEXTFORGE_LOCAL_IMAGE"' in script assert 'image_ref.startswith("itworx-mcp-hub/contextforge-schema:")' not in script compat_probe = "contextforge_compat_image = main_declares_contextforge_compat_image(name, image_ref)" owned_probe = "elif image_source == owned_image_source:" assert compat_probe in script assert "if contextforge_compat_image or sessionless_dataplane_image:" in script assert owned_probe in script assert script.index(compat_probe) < script.index(owned_probe) def test_sessionless_dataplane_baseline_is_one_time_and_commit_bound(): fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] assert 'container_name != "itworx-mcp-hub-dataplane-router-1"' in script assert 'image_ref != "itworx-mcp-hub/dataplane:sessionless-final"' in script assert 'sessionless_commit = "b519a8c4cf17cd9df00a575acfe096838aab7789"' in script assert 'f"{sessionless_commit}:services/dataplane_router/router.py"' in script assert 'f"{sessionless_commit}:tests/test_dataplane_session_headers.py"' in script assert 'f"{main_revision}:evidence/mcp-session-boundary-20260916/SUMMARY.md"' in script assert 'MCP_TRANSPORT_STATE_HEADERS = frozenset({b"mcp-session-id"})' in script assert 'del response.headers["mcp-session-id"]' in script assert '"2025-11-25"' in script assert '"2026-07-28"' in script assert '"permanently enforce stateless public transport"' in script assert ( 'git_run("merge-base", "--is-ancestor", sessionless_commit, main_revision)' in script ) assert "not already_reconciled" in script def test_already_reconciled_state_is_reverified_before_idempotent_success(): fake = FakePortainer() McpHubDeploymentReconciliation(fake).run() script = fake.calls[0][2]["json"]["Cmd"][2] assert "already_reconciled = (" in script assert script.index("already_reconciled = (") < script.index("project_names =") assert script.index("container_artifacts = {}") < script.index("if already_reconciled:") assert 'print("already-reconciled", flush=True)' in script assert "if drift_found:" in script def test_reconciliation_refuses_unknown_runtime_topology(): result = McpHubDeploymentReconciliation(FakePortainer(exit_code=47)).run() assert result == { "status": "validation_failed", "reason": "live MCP Hub Compose project differs from the controller target", } def test_reconciliation_refuses_unproven_runtime_revision_drift(): result = McpHubDeploymentReconciliation(FakePortainer(exit_code=51)).run() assert result == { "status": "validation_failed", "reason": "live MCP Hub image revision or external immutable image is not proven by the recorded-live to main chain", } def test_reconciliation_refuses_state_that_is_not_the_known_failed_forward_shape(): result = McpHubDeploymentReconciliation(FakePortainer(exit_code=52)).run() assert result == { "status": "validation_failed", "reason": "controller state drift is not the exact known recoverable MCP Hub state", } def test_reconciliation_refuses_unknown_state_without_snapshot_stage_proof(): result = McpHubDeploymentReconciliation(FakePortainer(exit_code=55)).run() assert result == { "status": "validation_failed", "reason": "unknown MCP Hub live status was not produced by a proven recoverable snapshot-stage failure", }