#!/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 BUILDER = SCRIPT_DIR / "build-module-foundry-grid-lod-artifact.mjs" RUNNER_PATH = SCRIPT_DIR / "nodedc-deploy" PATCH_ID = "module-foundry-map-grid-lod-20260805-999" EXPECTED_FILES = ( "apps/catalog/src/CesiumMapRenderer.tsx", "apps/catalog/src/MapFixturePreview.tsx", "apps/catalog/src/mapGridPolicy.d.mts", "apps/catalog/src/mapGridPolicy.mjs", "apps/catalog/src/styles.css", "runtime-seed/page-layouts/map.json", "scripts/map-grid-lod.test.mjs", "scripts/map-object-layers.test.mjs", "server/catalog-server.mjs", "server/foundry-mcp.mjs", ) def load_runner(): loader = importlib.machinery.SourceFileLoader( "nodedc_module_foundry_grid_lod_artifact", 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 ModuleFoundryGridLodArtifactTest(unittest.TestCase): def build(self, artifact_dir): environment = os.environ.copy() 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_builder_is_deterministic_exact_scope_and_runner_compatible(self): with tempfile.TemporaryDirectory(prefix="nodedc-foundry-grid-lod-") 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["sha256"], hashlib.sha256(first_artifact.read_bytes()).hexdigest()) self.assertEqual(tuple(first["files"]), EXPECTED_FILES) extracted = root / "loaded" extracted.mkdir() manifest, entries, payload = RUNNER.load_artifact(first_artifact, extracted) self.assertEqual(manifest["component"], "module-foundry") self.assertEqual(tuple(entries), EXPECTED_FILES) self.assertEqual(RUNNER.component_services("module-foundry", entries), ("nodedc-module-foundry",)) renderer = (payload / EXPECTED_FILES[0]).read_text(encoding="utf-8") inspector = (payload / EXPECTED_FILES[1]).read_text(encoding="utf-8") policy = (payload / EXPECTED_FILES[3]).read_text(encoding="utf-8") self.assertIn("class GridLayerController", renderer) self.assertIn("viewer.flyTo(entity", renderer) self.assertIn("lineDiameterMeters", renderer) self.assertIn("LOD ${index + 1}", inspector) self.assertIn("Конус видимости 3D", inspector) self.assertIn("Размер major-тайла ENU", inspector) self.assertIn("Радиус ENU-поля", inspector) self.assertIn("Диаметр 3D-линий", inspector) self.assertIn("Кресты: длина", inspector) self.assertIn("GRID_LOD_HYSTERESIS_RATIO", policy) self.assertIn("gridLodProfiles", policy) with tarfile.open(first_artifact, "r:gz") as archive: members = archive.getmembers() names = [member.name for member in members] regular_payloads = [ archive.extractfile(member).read() for member in members if member.isfile() ] self.assertFalse(any(Path(name).name.startswith("._") for name in names)) self.assertFalse(any("/.git/" in name or "/node_modules/" in name or "/runtime-data/" in name for name in names)) self.assertNotIn(b"-----BEGIN PRIVATE KEY-----", b"\n".join(regular_payloads)) if __name__ == "__main__": unittest.main(verbosity=2)