Files
Unraid-AppOps-Gateway-Public/backend/tests/test_mcp_hub_deployment_reconciliation.py
T

706 lines
31 KiB
Python

import ast
import hashlib
import json
import os
import re
import subprocess
import tempfile
from datetime import datetime, timezone
from pathlib import Path
from types import SimpleNamespace
import pytest
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_and_read_only_for_mcp_hub():
fake = FakePortainer()
result = McpHubDeploymentReconciliation(fake).run()
assert result == {
"status": "reconciled",
"repository": "jens/itworx_mcp_hub",
"composeProject": "itworx-mcp-hub",
}
command = fake.calls[0][2]["json"]["Cmd"]
assert command[:2] == ["python3", "-c"]
assert command[3:] == ["jens/itworx_mcp_hub", "itworx-mcp-hub"]
script = command[2]
assert "/controller-config/projects.json" in script
assert "/controller-hub-releases/controller-config/projects.json" in script
assert "/mnt/cache/appdata/gitea-deploy-runners/state" in script
assert "os.path.samefile(registry_path, host_registry_path)" in script
assert 'os.open(project_lock_path, os.O_RDONLY' in script
assert 'os.open(global_lock_path, os.O_RDONLY' in script
assert 'state_root.mkdir' not in script
assert 'project_lock_path.parent.mkdir' not in script
assert 'os.replace(' not in script
assert 'shutil.copy2(' not in script
assert 'state_path.write_text(' not in script
assert '"status": "success"' not in script
assert '"live_legacy_provenance": True' not in script
def test_unknown_or_legacy_state_requires_the_guarded_deployment_path():
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 legacy, unknown, mixed, or unresolved migration state requires guarded deployment or data-aware recovery",
}
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'set(registered_live_containers) == expected_fixed' in script
assert 'raise SystemExit(56 if recorded_live_status in {"unknown", "healthy"} else 52)' in script
assert 'recorded_status == "success"' in script
assert 'recorded_live_status == "healthy"' in script
assert 'state.get("live_legacy_provenance") is False' in script
assert 'recorded_artifacts.get("topology_contract") == topology_contract' in script
assert 'receipt.get("schema") == "gitea-deploy-receipt.v1"' in script
assert 'receipt.get("live") is None' in script
def test_modern_success_requires_a_complete_exact_receipt_and_protected_ancestry():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'set(receipt) == {"schema", "repository", "revision", "tree", "treeDigest", "build", "containers", "live"}' in script
assert 'receipt_identity_valid' in script
assert 'receipt.get("revision") == recorded_live' in script
assert 'git_run("merge-base", "--is-ancestor", recorded_live, main_revision)' in script
assert 'set(dict(receipt.get("containers") or {})) == expected_runtime' in script
assert 'print("already-reconciled", flush=True)' in script
assert 'print("reconciled", flush=True)' not in script
assert 'migration_path = migration_root / "active-intent.json"' in script
assert 'intent.get("status") != "complete"' in script
assert 'elif migration_attempt_id:' in script
assert 'intent.get("completedStateSha256") != hashlib.sha256(state_bytes).hexdigest()' in script
assert 'observed.get("historicalStateSha256") != intent.get("baselineStateSha256")' in script
assert 'rf"[0-9]+-{recorded_live[:12]}-[0-9a-f]{{16}}"' in script
def test_reconciliation_intent_guard_rejects_unknown_and_accepts_exact_complete_intent():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "validate_migration_intent_state"
)
namespace = {
"Path": Path,
"hashlib": hashlib,
"json": json,
"os": os,
"re": re,
"stat": SimpleNamespace(S_IMODE=lambda _mode: 0o600),
"trusted_private_path": lambda *_args, **_kwargs: True,
}
exec(compile(ast.Module(body=[function], type_ignores=[]), "intent-test", "exec"), namespace)
validate = namespace["validate_migration_intent_state"]
repository = "jens/itworx_mcp_hub"
candidate = "b" * 40
migration_attempt = "a" * 32
state_bytes = b"exact-success-state-bytes"
for raw in (b'{"status":"started"}', b"not-json"):
with tempfile.TemporaryDirectory() as temporary:
state_root = Path(temporary)
migration_root = state_root / "mcp-hub-topology-migrations"
migration_root.mkdir()
(migration_root / "active-intent.json").write_bytes(raw)
last_attempt = {"metadata": {"mcpHubMigrationAttemptId": migration_attempt}}
with pytest.raises(SystemExit) as exc:
validate(
state_root, state_bytes, repository, candidate, "success", "healthy",
last_attempt, "success", candidate,
)
assert exc.value.code == 56
with tempfile.TemporaryDirectory() as temporary:
last_attempt = {"metadata": {"mcpHubMigrationAttemptId": migration_attempt}}
with pytest.raises(SystemExit) as exc:
validate(
Path(temporary), state_bytes, repository, candidate, "success", "healthy",
last_attempt, "success", candidate,
)
assert exc.value.code == 56
with tempfile.TemporaryDirectory() as temporary:
state_root = Path(temporary)
migration_root = state_root / "mcp-hub-topology-migrations"
migration_root.mkdir()
intent = {
"schema": "itworx.mcp-hub-migration-intent/v1",
"status": "complete",
"attemptId": migration_attempt,
"repository": repository,
"candidateRevision": candidate,
"priorLiveRevision": "c" * 40,
"backupManifestSha256": "d" * 64,
"backupComposeSha256": "e" * 64,
"baselineStateSha256": "f" * 64,
"startedAtEpoch": 10,
"completedAtEpoch": 11,
"completedStateSha256": "0" * 64,
}
active_intent = migration_root / "active-intent.json"
active_intent.write_text(json.dumps(intent), encoding="utf-8")
last_attempt = {
"metadata": {
"mcpHubMigrationAttemptId": migration_attempt,
"mcpHubBackupReceipt": {
"repository": repository,
"revision": candidate,
"restoreVerified": True,
"manifestSha256": "d" * 64,
"composeSha256": "e" * 64,
},
}
}
with pytest.raises(SystemExit) as exc:
validate(
state_root, state_bytes, repository, candidate, "success", "healthy",
last_attempt, "success", candidate,
)
assert exc.value.code == 56
with tempfile.TemporaryDirectory() as temporary:
state_root = Path(temporary)
migration_root = state_root / "mcp-hub-topology-migrations"
receipt_root = state_root / "mcp-hub-backup-receipts"
backup_root = state_root / "mcp-hub-backups"
migration_root.mkdir()
receipt_root.mkdir()
backup_root.mkdir()
backup_directory = backup_root / f"1780400000-{candidate[:12]}-0123456789abcdef"
backup_directory.mkdir()
compose_digest = "e" * 64
manifest = {
"schema": "itworx.mcp-hub-backup/v1",
"repository": repository,
"revision": candidate,
"backupDirectory": str(backup_directory),
"composeSha256": compose_digest,
"configRestoreVerified": True,
"restore": {"status": "verified"},
"databases": {"itworx_mcp_hub": {}, "contextforge": {}},
}
manifest_path = backup_directory / "manifest.json"
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
manifest_digest = hashlib.sha256(manifest_path.read_bytes()).hexdigest()
receipt_path = receipt_root / f"1780400000-{candidate[:12]}-0123456789abcdef.json"
backup_receipt = {
"schema": "itworx.mcp-hub-backup-receipt/v1",
"repository": repository,
"revision": candidate,
"backupDirectory": str(backup_directory),
"manifestSha256": manifest_digest,
"composeSha256": compose_digest,
"restoreVerified": True,
"createdAtEpoch": 1780400000,
"receiptPath": str(receipt_path),
}
receipt_path.write_text(
json.dumps({key: value for key, value in backup_receipt.items() if key != "receiptPath"}),
encoding="utf-8",
)
observed_path = migration_root / f"observed-baseline-{candidate[:12]}-0123456789abcdef0123.json"
observed = {
"schema": "itworx.mcp-hub-observed-baseline/v1",
"claim": "observed_mixed_runtime_facts_only",
"candidateRevision": candidate,
"historicalStateSha256": "f" * 64,
"backupManifestSha256": manifest_digest,
"backupComposeSha256": compose_digest,
"backupRestoreVerified": True,
"postgresVolumeBaseline": [{}, {}],
}
observed_path.write_text(json.dumps(observed), encoding="utf-8")
observed_digest = hashlib.sha256(observed_path.read_bytes()).hexdigest()
successful_state_sha = hashlib.sha256(state_bytes).hexdigest()
intent = {
"schema": "itworx.mcp-hub-migration-intent/v1",
"status": "complete",
"attemptId": migration_attempt,
"repository": repository,
"candidateRevision": candidate,
"priorLiveRevision": "c" * 40,
"backupManifestSha256": manifest_digest,
"backupComposeSha256": compose_digest,
"baselineStateSha256": "f" * 64,
"startedAtEpoch": 10,
"completedAtEpoch": 11,
"completedStateSha256": successful_state_sha,
"observedBaselinePath": str(observed_path),
"observedBaselineSha256": observed_digest,
}
(migration_root / "active-intent.json").write_text(json.dumps(intent), encoding="utf-8")
last_attempt = {
"metadata": {
"mcpHubMigrationAttemptId": migration_attempt,
"mcpHubBackupReceipt": backup_receipt,
}
}
validate(
state_root, state_bytes, repository, candidate, "success", "healthy",
last_attempt, "success", candidate,
)
def test_container_reverification_requires_exact_health_service_label_ids_and_mounts():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'runtime_state.get("Running") is not True or health != "healthy"' in script
assert 'labels.get("com.docker.compose.project") != expected_compose_project' in script
assert 'labels.get("com.docker.compose.service") != name_service' in script
assert 'if recorded.get(key) != value or receipt_item.get(key) != value:' in script
assert 'live_attested_mounts(container) != recorded_mounts' in script
assert 'receipt_item.get("mounts_sha256")' in script
assert 'def live_attested_mounts(container):' in script
assert 'docker", "volume", "inspect", name' in script
assert 'mount["created_at"] = created_at' in script
assert 'created.astimezone(timezone.utc) > datetime.now(timezone.utc)' in script
def test_reconciliation_distinguishes_recreated_postgres_volume_by_created_at():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "live_attested_mounts"
)
volume = {
"Name": "hub-postgres",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/hub-postgres/_data",
"Options": {},
"CreatedAt": "2020-01-01T10:00:00Z",
}
namespace = {
"json": json,
"os": os,
"subprocess": SimpleNamespace(
PIPE=subprocess.PIPE,
DEVNULL=subprocess.DEVNULL,
run=lambda *_args, **_kwargs: SimpleNamespace(
returncode=0, stdout=json.dumps([volume])
)
),
"datetime": datetime,
"timezone": timezone,
}
exec(compile(ast.Module(body=[function], type_ignores=[]), "mount-test", "exec"), namespace)
attest = namespace["live_attested_mounts"]
container = {
"Mounts": [{
"Type": "volume", "Name": "hub-postgres",
"Source": "/var/lib/docker/volumes/hub-postgres/_data",
"Destination": "/var/lib/postgresql/data", "Driver": "local", "RW": True,
}]
}
original = attest(container)
assert original[0]["created_at"] == "2020-01-01T10:00:00Z"
volume["CreatedAt"] = "2021-01-01T10:00:00Z"
recreated = attest(container)
assert recreated[0]["name"] == original[0]["name"]
assert recreated[0]["source"] == original[0]["source"]
assert recreated[0]["created_at"] != original[0]["created_at"]
def test_each_service_uses_its_fixed_image_repository_or_reviewed_external_contract():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'def fixed_image_reference(service_name, image_ref):' in script
assert '"dataplane-router": "gitea.example.com/jens/itworx-mcp-hub/dataplane"' in script
assert '"docker-read-broker": "gitea.example.com/jens/itworx-mcp-hub/docker-read-broker"' in script
assert 'artifact["image_source"] != owned_image_source' in script
assert 'artifact["image_revision"] != recorded_live' in script
assert 'main_declares_external_image(image_ref)' in script
assert 'main_declares_contextforge_compat_image(' in script
assert 'def image_digest_matches(image, image_ref):' in script
assert 'aliases.add("docker.io/library/" + repository)' in script
def test_external_candidate_pins_are_exact_and_declared_by_protected_main():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "fixed_image_reference"
)
postgres = (
"gitea.example.com/jens/itworx-mcp-hub/postgres:"
"wp400-gosu-v2-20261002-a94e0b4d2029@sha256:"
"a60d81e06987671ae903757f1b71cdfbbff4d481b42610e4de2b7dcfa32aad73"
)
redis = (
"gitea.example.com/jens/itworx-mcp-hub/redis:"
"wp400-remediation-20261002-4e18f61c@sha256:"
"7d826b44f245ad32e8fe4bf40d42eae95855c3942c5f2e42784ee8fd3f9547a2"
)
previous_postgres = (
"postgres:18.6@sha256:"
"0377e72c5289ed2f98cf61b1a9c2db9eb9d300317fe14244492fbc94343b3d04"
)
previous_redis = (
"redis:8.10.2@sha256:"
"7ef5b5cec96495a04ca7feff88a9492efeab8053fb284d24bdd73344c9245a48"
)
declared = {previous_postgres, previous_redis, postgres, redis}
namespace = {
"re": re,
"main_declares_external_image": lambda image_ref: image_ref in declared,
"main_declares_sessionless_dataplane_image": lambda *_args: False,
"main_declares_contextforge_compat_image": lambda *_args: False,
}
exec(compile(ast.Module(body=[function], type_ignores=[]), "fixed-image-test", "exec"), namespace)
fixed = namespace["fixed_image_reference"]
assert fixed("postgres", "postgres:18.4@sha256:3a82e1f56c8f0f5616a11103ac3d47e632c3938698946a7ad26da0df1334744a")
assert fixed("postgres", "postgres:18.6@sha256:0377e72c5289ed2f98cf61b1a9c2db9eb9d300317fe14244492fbc94343b3d04")
assert fixed("redis", "redis:8.10.0@sha256:c29e49ab2f85760a3827b53882e6dd9f5c6c3f0bb7d724e07bb31cbf275a5236")
assert fixed("redis", "redis:8.10.2@sha256:7ef5b5cec96495a04ca7feff88a9492efeab8053fb284d24bdd73344c9245a48")
assert fixed("postgres", postgres)
assert fixed("redis", redis)
assert not fixed("redis", postgres)
assert not fixed("postgres", postgres[:-1] + "4")
assert not fixed("postgres", postgres.replace("wp400-gosu-v2-20261002-a94e0b4d2029", "wp400-gosu-v2-20261002-wrong"))
assert not fixed("postgres", postgres.replace("gitea.example.com/jens/itworx-mcp-hub", "gitea.example.com/other/itworx-mcp-hub"))
assert not fixed("redis", redis[:-1] + "3")
assert not fixed("redis", redis.replace("wp400-remediation-20261002-4e18f61c", "wp400-remediation-20261002-wrong"))
assert not fixed("redis", redis.replace("gitea.example.com/jens/itworx-mcp-hub", "gitea.example.com/other/itworx-mcp-hub"))
assert not fixed("postgres", "postgres:19.0@sha256:" + "a" * 64)
assert not fixed("redis", "redis:8.10.3@sha256:" + "b" * 64)
for service in ("web", "control-api", "dataplane-router", "connector-ingestion"):
assert fixed(service, "sha256:" + "a" * 64)
for service in ("postgres", "redis", "contextforge", "ingestion-clamd", "foreign"):
assert not fixed(service, "sha256:" + "a" * 64)
namespace["main_declares_external_image"] = lambda _image_ref: False
assert fixed("postgres", "postgres:18.4@sha256:3a82e1f56c8f0f5616a11103ac3d47e632c3938698946a7ad26da0df1334744a")
assert not fixed("postgres", previous_postgres)
assert fixed("redis", "redis:8.10.0@sha256:c29e49ab2f85760a3827b53882e6dd9f5c6c3f0bb7d724e07bb31cbf275a5236")
assert not fixed("redis", previous_redis)
assert not fixed("postgres", postgres)
assert not fixed("redis", redis)
@pytest.mark.parametrize("service, image_ref", [
(
"postgres",
"gitea.example.com/jens/itworx-mcp-hub/postgres:"
"wp400-gosu-v2-20261002-a94e0b4d2029@sha256:"
"a60d81e06987671ae903757f1b71cdfbbff4d481b42610e4de2b7dcfa32aad73",
),
(
"redis",
"gitea.example.com/jens/itworx-mcp-hub/redis:"
"wp400-remediation-20261002-4e18f61c@sha256:"
"7d826b44f245ad32e8fe4bf40d42eae95855c3942c5f2e42784ee8fd3f9547a2",
),
])
def test_new_db_pin_requires_exact_protected_main_compose_declaration(service, image_ref):
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "main_declares_external_image"
)
main_revision = "a" * 40
content = {"value": f"services:\n {service}:\n image: {image_ref}\n"}
calls = []
def git_run(*args):
calls.append(args)
return subprocess.CompletedProcess(args, 0, stdout=content["value"])
namespace = {"re": re, "main_revision": main_revision, "git_run": git_run}
exec(compile(ast.Module(body=[function], type_ignores=[]), "main-image-declaration-test", "exec"), namespace)
declares = namespace["main_declares_external_image"]
assert declares(image_ref)
assert calls == [("show", f"{main_revision}:infrastructure/compose/docker-compose.yml")]
calls.clear()
content["value"] = f"services:\n {service}:\n image: {image_ref[:-1]}0\n"
assert not declares(image_ref)
assert calls == [
("show", f"{main_revision}:infrastructure/compose/docker-compose.yml"),
("show", f"{main_revision}:infrastructure/compose/docker-compose.prod.yml"),
]
@pytest.fixture
def contextforge_reconciliation_proof():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
parsed = ast.parse(fake.calls[0][2]["json"]["Cmd"][2])
functions = [
node for node in parsed.body
if isinstance(node, ast.FunctionDef)
and node.name in {
"main_declares_contextforge_compat_image", "fixed_image_reference",
"receipt_bound_image_reference",
}
]
revision = "a" * 40
main_revision = "b" * 40
manifest = (
f"CONTEXTFORGE_UPSTREAM_COMMIT={'c' * 40}\n"
"CONTEXTFORGE_ALEMBIC_HEAD=123456789abc\n"
"CONTEXTFORGE_MCP_SDK=1.2.3\n"
"CONTEXTFORGE_LOCAL_IMAGE=itworx-mcp-hub/contextforge-schema:123456789abc\n"
)
builder = (
'git -C "$SRC" fetch --depth 1 origin "$CONTEXTFORGE_UPSTREAM_COMMIT"\n'
'git -C "$SRC" checkout --detach FETCH_HEAD\n'
'MCP_REQUIREMENT="mcp==${CONTEXTFORGE_MCP_SDK}"\n'
'alembic -c mcpgateway/alembic.ini heads\n'
'docker tag "$STAGED_IMAGE" "$CONTEXTFORGE_LOCAL_IMAGE"\n'
'--release-candidate\n'
'CANDIDATE_IMAGE="itworx-mcp-hub/contextforge-schema:${APPOPS_SOURCE_REVISION}"\n'
'docker tag "$STAGED_IMAGE" "$CANDIDATE_IMAGE"\n'
)
files = {
f"{commit}:infrastructure/images/contextforge/compat-image.env": manifest
for commit in (revision, main_revision)
}
files.update({
f"{commit}:scripts/build_contextforge_compat_image.sh": builder
for commit in (revision, main_revision)
})
def git_run(*args):
assert args[0] == "show"
return SimpleNamespace(returncode=0 if args[1] in files else 1,
stdout=files.get(args[1], ""))
namespace = {"re": re, "git_run": git_run, "main_revision": main_revision,
"recorded_live": revision}
exec( # noqa: S102 -- Execute only extracted repository-owned verifier functions.
compile(ast.Module(body=functions, type_ignores=[]), "contextforge-proof", "exec"), namespace
)
return SimpleNamespace(namespace=namespace, files=files, revision=revision)
def test_contextforge_reconciliation_accepts_exact_receipt_revision(contextforge_reconciliation_proof):
proof = contextforge_reconciliation_proof
namespace = proof.namespace
fixed = namespace["fixed_image_reference"]
reference = f"itworx-mcp-hub/contextforge-schema:{proof.revision}"
image_id = "sha256:" + "d" * 64
record = {"image_id": image_id, "image_ref": reference}
bound = namespace["receipt_bound_image_reference"](image_id, image_id, record, dict(record))
assert fixed("contextforge", bound)
assert fixed("contextforge", "itworx-mcp-hub/contextforge-schema:123456789abc")
# A later main may remove the candidate builder; the recorded receipt's
# ancestor revision must supply the source proof for this immutable image.
proof.files[f"{'b' * 40}:scripts/build_contextforge_compat_image.sh"] = ""
assert fixed("contextforge", bound)
@pytest.mark.parametrize("mutation", [
"wrong_revision", "short_revision", "mutable_tag", "wrong_service",
"missing_candidate_flag", "missing_candidate_assignment", "missing_candidate_tag",
"missing_fetch", "bad_manifest", "missing_recorded_source",
])
def test_contextforge_reconciliation_rejects_unbound_candidates(contextforge_reconciliation_proof, mutation):
proof = contextforge_reconciliation_proof
reference = f"itworx-mcp-hub/contextforge-schema:{proof.revision}"
service = "contextforge"
builder_path = f"{proof.revision}:scripts/build_contextforge_compat_image.sh"
fragments = {
"missing_candidate_flag": '--release-candidate',
"missing_candidate_assignment": 'CANDIDATE_IMAGE="itworx-mcp-hub/contextforge-schema:${APPOPS_SOURCE_REVISION}"',
"missing_candidate_tag": 'docker tag "$STAGED_IMAGE" "$CANDIDATE_IMAGE"',
"missing_fetch": 'git -C "$SRC" fetch --depth 1 origin "$CONTEXTFORGE_UPSTREAM_COMMIT"',
}
if mutation == "wrong_revision":
reference = f"itworx-mcp-hub/contextforge-schema:{'b' * 40}"
elif mutation == "short_revision":
reference = f"itworx-mcp-hub/contextforge-schema:{proof.revision[:12]}"
elif mutation == "mutable_tag":
reference = "itworx-mcp-hub/contextforge-schema:latest"
elif mutation == "wrong_service":
service = "foreign"
elif mutation in fragments:
proof.files[builder_path] = proof.files[builder_path].replace(fragments[mutation], "")
elif mutation == "bad_manifest":
path = f"{proof.revision}:infrastructure/images/contextforge/compat-image.env"
proof.files[path] = proof.files[path].replace("CONTEXTFORGE_ALEMBIC_HEAD=123456789abc", "CONTEXTFORGE_ALEMBIC_HEAD=invalid")
else:
proof.files.pop(builder_path)
assert not proof.namespace["fixed_image_reference"](service, reference)
def test_digest_attestation_normalizes_tagged_docker_hub_repo_digests():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "image_digest_matches"
)
namespace = {"re": re}
exec(compile(ast.Module(body=[function], type_ignores=[]), "reconcile-test", "exec"), namespace)
matches = namespace["image_digest_matches"]
digest = "sha256:" + "a" * 64
assert matches({"RepoDigests": [f"postgres@{digest}"]}, f"postgres:18.6@{digest}")
assert matches({"RepoDigests": [f"docker.io/library/redis@{digest}"]}, f"redis:8.10.2@{digest}")
assert matches({"RepoDigests": [f"clamav/clamav@{digest}"]}, f"clamav/clamav:1.4.6@{digest}")
assert matches(
{"RepoDigests": [f"registry.example:5000/team/web@{digest}"]},
f"registry.example:5000/team/web:release@{digest}",
)
assert not matches(
{"RepoDigests": [f"redis@sha256:{'b' * 64}"]}, f"redis:8.10.2@{digest}"
)
assert not matches({"RepoDigests": [f"foreign/redis@{digest}"]}, f"redis:8.10.2@{digest}")
def test_id_launched_image_uses_exact_state_and_receipt_reference():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "receipt_bound_image_reference"
)
namespace = {"re": re}
exec( # noqa: S102
compile(ast.Module(body=[function], type_ignores=[]), "receipt-image-test", "exec"),
namespace,
)
bound_reference = namespace["receipt_bound_image_reference"]
image_id = "sha256:" + "a" * 64
image_ref = (
"postgres:18.6@sha256:"
"0377e72c5289ed2f98cf61b1a9c2db9eb9d300317fe14244492fbc94343b3d04"
)
recorded = {"image_id": image_id, "image_ref": image_ref}
receipt_item = dict(recorded)
assert bound_reference(image_id, image_id, recorded, receipt_item) == image_ref
assert bound_reference(image_ref, image_id, recorded, receipt_item) == image_ref
assert 'image_ref = receipt_bound_image_reference(' in script
assert script.index('image_ref = receipt_bound_image_reference(') < script.index(
'if not fixed_image_reference(name_service, image_ref):'
)
assert 'image_digest_matches(image, image_ref)' in script
for launch_image, live_id, state, receipt in (
("sha256:" + "c" * 64, image_id, recorded, receipt_item),
(
image_id, image_id,
{**recorded, "image_id": "sha256:" + "c" * 64}, receipt_item,
),
(
image_id, image_id,
recorded, {**receipt_item, "image_id": "sha256:" + "c" * 64},
),
(
image_id, image_id,
{**recorded, "image_ref": "redis:8.10.2@sha256:" + "b" * 64},
receipt_item,
),
(image_id, image_id, recorded, {**receipt_item, "image_ref": ""}),
(image_id, "arbitrary-image-id", recorded, receipt_item),
(image_id, image_id,
{**recorded, "image_ref": "sha256:" + "b" * 64},
{**receipt_item, "image_ref": "sha256:" + "b" * 64}),
):
with pytest.raises(SystemExit) as exc:
bound_reference(launch_image, live_id, state, receipt)
assert exc.value.code == 52
def test_reconciliation_reports_invalid_state_and_live_evidence_fail_closed():
expected = {
42: ("validation_failed", "MCP Hub deployment state file is unavailable or invalid"),
45: ("validation_failed", "a live MCP Hub container is not running and healthy"),
47: ("validation_failed", "live MCP Hub Compose project differs from the controller target"),
51: ("validation_failed", "live MCP Hub image revision or external immutable image is not proven by the recorded-live to main chain"),
52: ("validation_failed", "controller state or live facts do not match the exact modern MCP Hub receipt"),
56: ("topology_migration_required", "MCP Hub legacy, unknown, mixed, or unresolved migration state requires guarded deployment or data-aware recovery"),
}
for exit_code, (status, reason) in expected.items():
result = McpHubDeploymentReconciliation(FakePortainer(exit_code=exit_code)).run()
assert result["status"] == status
assert result["reason"] == reason
def test_reconciliation_only_allows_fixed_17_topology_and_rejects_registry_drift():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'project.get("branch") != "main"' in script
assert 'project.get("slug") != expected_compose_project' in script
assert 'project.get("compose_project") != expected_compose_project' in script
assert 'frozenset(expected_containers) not in {frozenset(expected_fixed), frozenset(expected_runtime)}' in script
assert 'set(expected_containers) != expected_runtime' in script
assert 'set(project_names) != set(expected_containers)' in script
assert '"itworx-mcp-hub-connector-ingestion-1"' in script
assert '"itworx-mcp-hub-ingestion-clamd-1"' in script