141 lines
6.2 KiB
Python
141 lines
6.2 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"
|
|
ENGINE_BUILDER = SCRIPT_DIR / "build-engine-mcp-ontology-sdk-artifact.mjs"
|
|
PLATFORM_BUILDER = SCRIPT_DIR / "build-platform-gelios-provider-v2-artifact.mjs"
|
|
ENGINE_ARTIFACT = (
|
|
PLATFORM_ROOT
|
|
/ "infra/deploy-artifacts/nodedc-engine-mcp-control-plane-20260718-005.tgz"
|
|
)
|
|
PLATFORM_ARTIFACT = (
|
|
PLATFORM_ROOT
|
|
/ "infra/deploy-artifacts/nodedc-platform-gelios-provider-v2-20260718-005.tgz"
|
|
)
|
|
ENGINE_SHA256 = "8d6e1581ebbba420984e2393b8c6f0f21e0b644f630c4ca6483a7cc4a0ae5c71"
|
|
PLATFORM_SHA256 = "e1b838ec265369af735766fc7d1bf23c5e05bd2d15ef65e2446b4f7efa7a1baa"
|
|
|
|
|
|
def load_runner():
|
|
loader = importlib.machinery.SourceFileLoader("nodedc_engine_mcp_ontology_sdk", 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 EngineMcpOntologySdkTest(unittest.TestCase):
|
|
def test_canonical_artifacts_have_pinned_digests_and_safe_members(self):
|
|
for artifact, expected in (
|
|
(ENGINE_ARTIFACT, ENGINE_SHA256),
|
|
(PLATFORM_ARTIFACT, PLATFORM_SHA256),
|
|
):
|
|
self.assertEqual(hashlib.sha256(artifact.read_bytes()).hexdigest(), expected)
|
|
self.assertEqual(artifact.read_bytes()[4:8], b"\0\0\0\0")
|
|
with tarfile.open(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_engine_builder_is_byte_reproducible(self):
|
|
current_sha256 = {
|
|
relative_path: hashlib.sha256((ENGINE_ROOT / relative_path).read_bytes()).hexdigest()
|
|
for relative_path in RUNNER.ENGINE_MCP_ONTOLOGY_SDK_TARGET_SHA256
|
|
}
|
|
if current_sha256 != RUNNER.ENGINE_MCP_ONTOLOGY_SDK_TARGET_SHA256:
|
|
self.skipTest("historical Ontology/SDK builder source has advanced to its exact successor")
|
|
outputs = []
|
|
with tempfile.TemporaryDirectory(prefix="nodedc-engine-mcp-ontology-sdk-") as directory:
|
|
for index in range(2):
|
|
target = Path(directory) / str(index)
|
|
target.mkdir()
|
|
env = os.environ.copy()
|
|
env["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(target)
|
|
result = subprocess.run(
|
|
["node", str(ENGINE_BUILDER), "20260718-005"],
|
|
cwd=PLATFORM_ROOT,
|
|
env=env,
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
outputs.append(Path(json.loads(result.stdout)["artifact"]).read_bytes())
|
|
self.assertEqual(outputs[0], outputs[1])
|
|
self.assertEqual(outputs[0], ENGINE_ARTIFACT.read_bytes())
|
|
|
|
def test_platform_builder_is_byte_reproducible(self):
|
|
outputs = []
|
|
with tempfile.TemporaryDirectory(prefix="nodedc-platform-gelios-v2-") as directory:
|
|
for index in range(2):
|
|
target = Path(directory) / str(index)
|
|
target.mkdir()
|
|
env = os.environ.copy()
|
|
env["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(target)
|
|
result = subprocess.run(
|
|
["node", str(PLATFORM_BUILDER), "20260718-005"],
|
|
cwd=PLATFORM_ROOT,
|
|
env=env,
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
outputs.append(Path(json.loads(result.stdout)["artifact"]).read_bytes())
|
|
self.assertEqual(outputs[0], outputs[1])
|
|
self.assertEqual(outputs[0], PLATFORM_ARTIFACT.read_bytes())
|
|
|
|
def test_engine_slice_is_exact_and_restarts_only_backend(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
manifest, entries, payload = RUNNER.load_artifact(ENGINE_ARTIFACT, root)
|
|
descriptor = RUNNER.validate_engine_mcp_ontology_sdk_payload(payload, entries)
|
|
self.assertEqual(manifest["component"], "engine")
|
|
self.assertEqual(tuple(entries), RUNNER.ENGINE_MCP_ONTOLOGY_SDK_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_ONTOLOGY_SDK_TARGET_SHA256[
|
|
RUNNER.ENGINE_NODE_INTELLIGENCE_GATEWAY_REL
|
|
],
|
|
)
|
|
|
|
def test_platform_provider_package_is_metadata_only(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
manifest, entries, _payload = RUNNER.load_artifact(PLATFORM_ARTIFACT, root)
|
|
self.assertEqual(manifest["component"], "platform")
|
|
self.assertTrue(RUNNER.is_platform_provider_catalog_only(entries))
|
|
self.assertEqual(RUNNER.component_services("platform", entries), ())
|
|
self.assertEqual(RUNNER.component_builds("platform", entries), ())
|
|
self.assertEqual(RUNNER.component_healthchecks("platform", entries, ()), ())
|
|
|
|
def test_engine_payload_tampering_is_rejected(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
RUNNER.safe_extract(ENGINE_ARTIFACT, root)
|
|
entries = RUNNER.parse_files_list(root / "files.txt")
|
|
catalog = root / "payload/nodedc-source/server/assets/provider-packages/v1/catalog.json"
|
|
catalog.write_text("{}\n", encoding="utf-8")
|
|
with self.assertRaisesRegex(RUNNER.DeployError, "target sha256 mismatch"):
|
|
RUNNER.validate_engine_mcp_ontology_sdk_payload(root / "payload", entries)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|