#!/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 from unittest import mock SCRIPT_DIR = Path(__file__).resolve().parent RUNNER_PATH = SCRIPT_DIR / "nodedc-deploy" BUILDER = ( SCRIPT_DIR / "build-device-plane-postgres-bootstrap-artifact.mjs" ) EXPECTED_ENTRIES = [ "docker-compose.device-plane.yml", "deployment/device-postgres-bootstrap-v1.json", ] def load_runner(): loader = importlib.machinery.SourceFileLoader( "nodedc_device_plane_postgres_runner_under_test", 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 DevicePlanePostgresBootstrapTest(unittest.TestCase): def build(self, artifact_dir, patch_id): environment = os.environ.copy() environment["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(artifact_dir) result = subprocess.run( ["node", str(BUILDER), patch_id], check=True, capture_output=True, text=True, env=environment, ) return json.loads(result.stdout) def test_bootstrap_artifact_is_exact_deterministic_and_runner_accepted(self): with tempfile.TemporaryDirectory( prefix="nodedc-device-plane-postgres-artifact-", ) as directory: artifact_dir = Path(directory) first = self.build( artifact_dir, "device-plane-postgres-bootstrap-unit-001", ) artifact = Path(first["artifact"]) first_bytes = artifact.read_bytes() second = self.build( artifact_dir, "device-plane-postgres-bootstrap-unit-001", ) second_bytes = Path(second["artifact"]).read_bytes() self.assertEqual(first["entries"], EXPECTED_ENTRIES) self.assertEqual(first["services"], ["device-postgres"]) self.assertEqual(first["mode"], "create-if-absent") self.assertEqual(first["rollbackVolumePolicy"], "preserve") self.assertEqual( first["sha256"], hashlib.sha256(first_bytes).hexdigest(), ) self.assertEqual(first_bytes, second_bytes) with tarfile.open(artifact, "r:gz") as archive: names = {member.name for member in archive.getmembers()} self.assertEqual( archive.extractfile("files.txt") .read() .decode("utf-8") .splitlines(), EXPECTED_ENTRIES, ) self.assertEqual( names, { "manifest.env", "files.txt", "payload", "payload/docker-compose.device-plane.yml", "payload/deployment", "payload/deployment/device-postgres-bootstrap-v1.json", }, ) with tempfile.TemporaryDirectory( prefix="nodedc-device-plane-postgres-load-", ) as work_directory: manifest, entries, _payload = RUNNER.load_artifact( artifact, Path(work_directory), ) self.assertEqual(manifest["component"], "device-plane") self.assertEqual(entries, EXPECTED_ENTRIES) self.assertTrue( RUNNER.is_device_plane_postgres_bootstrap_slice( manifest["component"], entries, ), ) self.assertEqual( RUNNER.component_services("device-plane", entries), ("device-postgres",), ) self.assertEqual( RUNNER.component_builds("device-plane", entries), (), ) def test_preflight_accepts_only_absent_container_and_volume(self): absent_container = mock.Mock(returncode=0, stdout="") absent_volume = mock.Mock(returncode=1) with mock.patch.object( RUNNER.subprocess, "run", side_effect=[absent_container, absent_volume], ): self.assertEqual( RUNNER.preflight_device_plane_postgres_bootstrap(), "absent", ) existing_container = mock.Mock( returncode=0, stdout="abc123def456\n", ) with mock.patch.object( RUNNER.subprocess, "run", return_value=existing_container, ): with self.assertRaisesRegex( RUNNER.DeployError, "container already exists", ): RUNNER.preflight_device_plane_postgres_bootstrap() with mock.patch.object( RUNNER.subprocess, "run", side_effect=[ absent_container, mock.Mock(returncode=0), ], ): with self.assertRaisesRegex( RUNNER.DeployError, "volume already exists", ): RUNNER.preflight_device_plane_postgres_bootstrap() def test_bootstrap_health_acceptance_is_database_service_only(self): with mock.patch.object( RUNNER, "healthcheck_compose_service", ) as health: RUNNER.run_healthchecks( "device-plane", EXPECTED_ENTRIES, ("device-postgres",), ) health.assert_called_once_with( "device-plane", "device-postgres", ) with self.assertRaisesRegex( RUNNER.DeployError, "service set mismatch", ): RUNNER.run_healthchecks( "device-plane", EXPECTED_ENTRIES, ("device-control-core",), ) if __name__ == "__main__": unittest.main(verbosity=2)