154 lines
6.4 KiB
Python
154 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
|
import hashlib
|
|
import importlib.machinery
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import tarfile
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
PLATFORM_ROOT = SCRIPT_DIR.parent.parent
|
|
ENGINE_ROOT = PLATFORM_ROOT.parent / "NODEDC_ENGINE_INFRA"
|
|
RUNNER_PATH = SCRIPT_DIR / "nodedc-deploy"
|
|
BUILDER = SCRIPT_DIR / "build-engine-mcp-autonomy-provider-v5-artifact.mjs"
|
|
PATCH_ID = "20991231-998"
|
|
CANONICAL_ARTIFACT = (
|
|
PLATFORM_ROOT
|
|
/ "infra/deploy-artifacts/nodedc-engine-mcp-autonomy-provider-v5-20260720-004.tgz"
|
|
)
|
|
CANONICAL_SHA256 = "3400954cdce078892a06b04b9bfd85a90f6ea775b1a0caf99eba1f880ef6601c"
|
|
|
|
|
|
def load_runner():
|
|
loader = importlib.machinery.SourceFileLoader(
|
|
"nodedc_engine_mcp_autonomy_provider_v5",
|
|
str(RUNNER_PATH),
|
|
)
|
|
spec = importlib.util.spec_from_loader(loader.name, loader)
|
|
module = importlib.util.module_from_spec(spec)
|
|
loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
RUNNER = load_runner()
|
|
|
|
|
|
class EngineMcpAutonomyProviderV5Test(unittest.TestCase):
|
|
def build(self, artifact_dir):
|
|
environment = os.environ.copy()
|
|
environment["NODEDC_ENGINE_SOURCE_ROOT"] = str(ENGINE_ROOT)
|
|
environment["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(artifact_dir)
|
|
completed = subprocess.run(
|
|
["node", str(BUILDER), PATCH_ID],
|
|
cwd=PLATFORM_ROOT,
|
|
env=environment,
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
return json.loads(completed.stdout)
|
|
|
|
def test_canonical_artifact_digest_and_members_are_pinned(self):
|
|
self.assertEqual(
|
|
hashlib.sha256(CANONICAL_ARTIFACT.read_bytes()).hexdigest(),
|
|
CANONICAL_SHA256,
|
|
)
|
|
self.assertEqual(CANONICAL_ARTIFACT.read_bytes()[4:8], b"\0\0\0\0")
|
|
with tarfile.open(CANONICAL_ARTIFACT, "r:gz") as archive:
|
|
for member in archive:
|
|
self.assertTrue(member.isfile() or member.isdir())
|
|
self.assertFalse(Path(member.name).name.startswith("._"))
|
|
|
|
def test_builder_is_reproducible_and_slice_is_backend_only(self):
|
|
with tempfile.TemporaryDirectory(prefix="nodedc-engine-mcp-autonomy-v5-") as directory:
|
|
root = Path(directory)
|
|
first = self.build(root / "first")
|
|
second = self.build(root / "second")
|
|
first_artifact = Path(first["artifact"])
|
|
second_artifact = Path(second["artifact"])
|
|
self.assertEqual(first_artifact.read_bytes(), second_artifact.read_bytes())
|
|
self.assertEqual(
|
|
first["artifactSha256"],
|
|
hashlib.sha256(first_artifact.read_bytes()).hexdigest(),
|
|
)
|
|
self.assertEqual(first["services"], ["nodedc-backend"])
|
|
self.assertEqual(
|
|
first["authority"],
|
|
"mcp-capability-intersect-user-objective",
|
|
)
|
|
|
|
extract = root / "extract"
|
|
with tarfile.open(first_artifact, "r:gz") as archive:
|
|
archive.extractall(extract, filter="data")
|
|
loaded = root / "loaded"
|
|
loaded.mkdir()
|
|
manifest, entries, payload = RUNNER.load_artifact(first_artifact, loaded)
|
|
descriptor = RUNNER.validate_engine_mcp_autonomy_provider_v5_payload(
|
|
payload,
|
|
entries,
|
|
)
|
|
self.assertEqual(manifest["component"], "engine")
|
|
self.assertEqual(
|
|
tuple(entries),
|
|
RUNNER.ENGINE_MCP_AUTONOMY_PROVIDER_V5_ARTIFACT_ENTRIES,
|
|
)
|
|
self.assertEqual(RUNNER.component_services("engine", entries), ("nodedc-backend",))
|
|
self.assertEqual(RUNNER.component_builds("engine", entries), ())
|
|
self.assertEqual(
|
|
descriptor["source"]["gatewaySha256"],
|
|
RUNNER.ENGINE_MCP_AUTONOMY_PROVIDER_V5_TARGET_SHA256[
|
|
RUNNER.ENGINE_NODE_INTELLIGENCE_GATEWAY_REL
|
|
],
|
|
)
|
|
|
|
def test_payload_pins_authority_policy_archive_and_catalog_lineage(self):
|
|
with tempfile.TemporaryDirectory(prefix="nodedc-engine-mcp-autonomy-v5-policy-") as directory:
|
|
result = self.build(Path(directory) / "artifact")
|
|
extract = Path(directory) / "extract"
|
|
with tarfile.open(result["artifact"], "r:gz") as archive:
|
|
archive.extractall(extract, filter="data")
|
|
payload = extract / "payload"
|
|
installer = (
|
|
payload
|
|
/ "nodedc-source/server/assets/engine-agent-npm/bin/nodedc-engine-codex-agent.mjs"
|
|
).read_text(encoding="utf-8")
|
|
catalog = json.loads((
|
|
payload / "nodedc-source/server/assets/provider-packages/v1/catalog.json"
|
|
).read_text(encoding="utf-8"))
|
|
self.assertIn("MCP tool availability establishes capability authority", installer)
|
|
self.assertIn("machine safety barrier, not a permission ceremony", installer)
|
|
self.assertIn("Three identical failures with no new evidence", installer)
|
|
self.assertNotIn("only with explicit user confirmation", installer)
|
|
self.assertEqual(
|
|
[item["id"] for item in catalog["packages"]],
|
|
["gelios.provider.v4", "gelios.provider.v5"],
|
|
)
|
|
self.assertTrue(all(
|
|
capability["dataProductIds"] == ["fleet.positions.current.v4"]
|
|
for package in catalog["packages"] if package["id"] == "gelios.provider.v5"
|
|
for capability in package["capabilities"]
|
|
))
|
|
|
|
def test_payload_tampering_is_rejected(self):
|
|
with tempfile.TemporaryDirectory(prefix="nodedc-engine-mcp-autonomy-v5-tamper-") as directory:
|
|
result = self.build(Path(directory) / "artifact")
|
|
extract = Path(directory) / "extract"
|
|
RUNNER.safe_extract(Path(result["artifact"]), extract)
|
|
entries = RUNNER.parse_files_list(extract / "files.txt")
|
|
package = extract / "payload/nodedc-source/server/assets/engine-agent-npm/package.json"
|
|
package.write_text("{}\n", encoding="utf-8")
|
|
with self.assertRaisesRegex(RUNNER.DeployError, "target sha256 mismatch"):
|
|
RUNNER.validate_engine_mcp_autonomy_provider_v5_payload(
|
|
extract / "payload",
|
|
entries,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|