Files
projectbrain-Public/scripts/legacy-deployment-migration/test_compose_contracts.py
T

85 lines
4.2 KiB
Python

"""Offline Compose rendering; never builds images or starts containers."""
import json
import os
from pathlib import Path
import shutil
import subprocess
import unittest
PROJECTS = Path(__file__).resolve().parents[3]
CASES = {
"VacatureRadar": ("app", ["docker-compose.unraid.yml"], {
"MIGRATION_LOCAL_ROOT": "/migration-fixture/local",
"VACATURERADAR_ENV_FILE": "/migration-fixture/runtime.env",
}, {"/app/local", "/app/media", "/app/logs"}),
"LumaOps": ("lumaops", ["docker-compose.yml", "docker-compose.unraid.yml"], {
"MIGRATION_CONFIG_ROOT": "/migration-fixture/config",
"MIGRATION_OPENRGB_ROOT": "/migration-fixture/openrgb",
"MIGRATION_LUMA_CONFIG_ROOT": "/migration-fixture/luma-config",
"MIGRATION_DATA_ROOT": "/migration-fixture/data",
"MIGRATION_LOGS_ROOT": "/migration-fixture/logs",
"MIGRATION_ENV_FILE": "/migration-fixture/runtime.env",
}, {"/config", "/config/openrgb", "/config/lumaops", "/data", "/logs"}),
}
@unittest.skipUnless(shutil.which("docker"), "Docker Compose CLI is required; no daemon used")
class ComposeTests(unittest.TestCase):
def render(self, name, missing=None):
_, files, variables, _ = CASES[name]
environment = dict(os.environ)
for key in list(environment):
if key.startswith("MIGRATION_") or key.startswith("VACATURERADAR_"):
environment.pop(key)
environment.update({
"MIGRATION_IMAGE": "migration-fixture:candidate",
"MIGRATION_NETWORK": name.lower() + "_default",
"MIGRATION_ATTEMPT": "fixture-migration-attempt",
"MIGRATION_SOURCE_REVISION": "a" * 40,
"MIGRATION_SOURCE_TREE": "b" * 40,
"MIGRATION_BUILD_ID": "a" * 12,
"MIGRATION_BUILD_DATE": "2026-09-09T00:00:00.000Z",
"LUMAOPS_ADMIN_TOKEN": "fixture-only-not-a-production-token-000000",
**variables,
})
if missing:
environment.pop(missing)
command = ["docker", "compose", "--env-file", os.devnull]
for file in [*files, "docker-compose.migration.yml"]:
command.extend(["-f", file])
command.extend(["config", "--no-env-resolution", "--no-path-resolution", "--format", "json"])
return subprocess.run(command, cwd=PROJECTS / name, env=environment,
capture_output=True, text=True, timeout=20)
def test_merged_mounts_are_exclusively_explicit_clones(self):
for name, (service, _, _, destinations) in CASES.items():
with self.subTest(name=name):
result = self.render(name)
self.assertEqual(result.returncode, 0, "Fixture Compose rendering failed")
config = json.loads(result.stdout)
runtime = config["services"][service]
self.assertEqual(runtime["network_mode"], name.lower() + "_default")
self.assertEqual(config["name"], name.lower() + "-managed")
self.assertEqual({m["target"] for m in runtime["volumes"]}, destinations)
for mount in runtime["volumes"]:
self.assertEqual(mount["type"], "bind")
self.assertTrue(mount["source"].startswith("/migration-fixture/"))
self.assertFalse(mount["bind"]["create_host_path"])
labels = runtime["build"]["labels"]
self.assertEqual(labels["org.opencontainers.image.revision"], "a" * 40)
self.assertEqual(labels["org.opencontainers.image.source-tree"], "b" * 40)
self.assertEqual(labels["org.opencontainers.image.source"], "jens/" + name.lower())
def test_missing_required_inputs_fail_closed(self):
for name, (_, _, variables, _) in CASES.items():
for variable in [*variables, "MIGRATION_NETWORK", "MIGRATION_IMAGE", "MIGRATION_ATTEMPT", "MIGRATION_SOURCE_REVISION",
"MIGRATION_SOURCE_TREE", "MIGRATION_BUILD_ID", "MIGRATION_BUILD_DATE"]:
with self.subTest(name=name, variable=variable):
self.assertNotEqual(self.render(name, variable).returncode, 0)
if __name__ == "__main__":
unittest.main()