#!/usr/bin/env python3 import hashlib import importlib.machinery import importlib.util import json import os import subprocess import tempfile import unittest from pathlib import Path SCRIPT_DIR = Path(__file__).resolve().parent PLATFORM_ROOT = SCRIPT_DIR.parent.parent BUILDER_PATH = SCRIPT_DIR / "build-tasker-attachment-formats-artifact.mjs" RUNNER_PATH = SCRIPT_DIR / "nodedc-deploy" RELEASE = "20260829-001" SOURCE_COMMIT = "f9308539bfe71e4e2359c47ed5c230fa1fb386c9" EXPECTED_FILES = { "plane-src/apps/api/plane/settings/common.py", "plane-src/apps/web/core/components/issues/attachment/attachment-list-item.tsx", "plane-src/apps/web/core/components/issues/peek-overview/view.tsx", "plane-src/apps/web/styles/globals.css", "plane-src/packages/services/src/file/helper.ts", } def load_runner(): loader = importlib.machinery.SourceFileLoader("nodedc_tasker_attachment_formats_runner", 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 TaskerAttachmentFormatsArtifactTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.temporary = tempfile.TemporaryDirectory(prefix="nodedc-tasker-attachment-formats-") cls.root = Path(cls.temporary.name) cls.builds = [] for index in range(2): output = cls.root / f"build-{index}" output.mkdir() env = os.environ.copy() env["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(output) result = subprocess.run( ["node", str(BUILDER_PATH), RELEASE], cwd=PLATFORM_ROOT, env=env, check=True, capture_output=True, text=True, ) cls.builds.append(json.loads(result.stdout)) @classmethod def tearDownClass(cls): cls.temporary.cleanup() def test_artifact_is_reproducible_and_runner_accepted(self): first = self.builds[0] second = self.builds[1] first_path = Path(first["artifact"]) second_path = Path(second["artifact"]) first_bytes = first_path.read_bytes() self.assertEqual(first_bytes, second_path.read_bytes()) self.assertEqual(first_bytes[4:8], bytes(4)) self.assertEqual(first["sha256"], hashlib.sha256(first_bytes).hexdigest()) self.assertEqual(first["sourceCommit"], SOURCE_COMMIT) self.assertEqual(first["patchId"], f"tasker-attachment-formats-{RELEASE}") self.assertEqual(set(first["files"]), EXPECTED_FILES) with tempfile.TemporaryDirectory() as directory: manifest, entries, payload = RUNNER.load_artifact(first_path, Path(directory)) self.assertEqual(manifest, { "id": first["patchId"], "component": "tasker", "type": "app-overlay", }) self.assertEqual(entries, first["files"]) for relative_path in entries: self.assertTrue((payload / relative_path).is_file()) def test_runner_selects_only_registered_tasker_scope(self): entries = self.builds[0]["files"] self.assertEqual(RUNNER.component_services("tasker", entries), ("api", "worker", "beat-worker", "web")) self.assertEqual(len(RUNNER.component_builds("tasker", entries)), 2) def test_builder_rejects_an_unexpected_release(self): env = os.environ.copy() env["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(self.root / "unexpected-release") result = subprocess.run( ["node", str(BUILDER_PATH), "20260829-002"], cwd=PLATFORM_ROOT, env=env, capture_output=True, text=True, ) self.assertNotEqual(result.returncode, 0) self.assertIn("usage: build-tasker-attachment-formats-artifact.mjs", result.stderr) if __name__ == "__main__": unittest.main(verbosity=2)