Files
crank/tests/unit/test_check_runtime_config.py

206 lines
7.6 KiB
Python

import json
import subprocess
import tempfile
import unittest
from pathlib import Path
SCRIPT = Path(__file__).parents[2] / "scripts/check-runtime-config.py"
def field(name: str, process: str = "shared", mode: str = "effective") -> dict:
return {
"env_name": name,
"process": process,
"mode": mode,
"required": False,
"default": "value",
"sensitivity": "public",
}
class RuntimeConfigContractTests(unittest.TestCase):
def fixture(self) -> Path:
root = Path(tempfile.mkdtemp())
(root / "docs/schemas").mkdir(parents=True)
(root / "deploy/community").mkdir(parents=True)
contract = {
"schema_version": 1,
"fields": [
field("CRANK_SHARED"),
field("CRANK_ADMIN", "admin_api"),
field("CRANK_MCP", "mcp_server"),
field("CRANK_OLD", mode="deprecated_no_effect"),
],
"deployment_only_fields": ["COMPOSE_PROJECT_NAME"],
}
(root / "docs/schemas/runtime-config.schema.json").write_text(
json.dumps(contract), encoding="utf-8"
)
section = (
"# BEGIN GENERATED CRANK RUNTIME CONFIG\n"
"CRANK_ADMIN=value\nCRANK_MCP=value\nCRANK_SHARED=value\n"
"# END GENERATED CRANK RUNTIME CONFIG\n"
)
for relative in (
".env.example",
"deploy/community/.env.example",
"deploy/community/.env.images.example",
):
(root / relative).write_text(section, encoding="utf-8")
compose = (
"services:\n"
" admin-api:\n environment:\n"
" CRANK_SHARED: ${CRANK_SHARED:-value}\n"
" CRANK_ADMIN: ${CRANK_ADMIN:-value}\n"
" mcp-server:\n environment:\n"
" CRANK_SHARED: ${CRANK_SHARED:-value}\n"
" CRANK_MCP: ${CRANK_MCP:-value}\n"
)
for relative in (
"docker-compose.yml",
"deploy/community/docker-compose.yml",
"deploy/community/docker-compose.images.yml",
):
(root / relative).write_text(compose, encoding="utf-8")
return root
def run_check(self, root: Path) -> subprocess.CompletedProcess[str]:
return subprocess.run(
["python3", str(SCRIPT), "--root", str(root)],
text=True,
capture_output=True,
check=False,
)
def test_valid_contract_passes(self) -> None:
result = self.run_check(self.fixture())
self.assertEqual(result.returncode, 0, result.stderr)
def test_missing_generated_field_fails_closed(self) -> None:
root = self.fixture()
path = root / ".env.example"
path.write_text(
path.read_text().replace("CRANK_MCP=value\n", ""), encoding="utf-8"
)
result = self.run_check(root)
self.assertNotEqual(result.returncode, 0)
def test_compose_extra_or_wrong_process_field_fails(self) -> None:
root = self.fixture()
path = root / "docker-compose.yml"
path.write_text(
path.read_text().replace(
"CRANK_ADMIN: ${CRANK_ADMIN:-value}",
"CRANK_ADMIN: ${CRANK_ADMIN:-value}\n"
" CRANK_MCP: ${CRANK_MCP:-value}",
),
encoding="utf-8",
)
result = self.run_check(root)
self.assertNotEqual(result.returncode, 0)
def test_deprecated_startup_bootstrap_password_may_be_absent_from_compose(self) -> None:
root = self.fixture()
schema_path = root / "docs/schemas/runtime-config.schema.json"
schema = json.loads(schema_path.read_text(encoding="utf-8"))
schema["fields"].append(
{
"env_name": "CRANK_BOOTSTRAP_ADMIN_PASSWORD",
"process": "admin_api",
"mode": "effective",
"required": False,
"default": None,
"sensitivity": "secret",
"compatibility": "deprecated startup-bootstrap password; use local bootstrap contract",
}
)
schema_path.write_text(json.dumps(schema), encoding="utf-8")
for relative in (
".env.example",
"deploy/community/.env.example",
"deploy/community/.env.images.example",
):
path = root / relative
path.write_text(
path.read_text().replace(
"CRANK_SHARED=value\n",
"CRANK_SHARED=value\nCRANK_BOOTSTRAP_ADMIN_PASSWORD=\n",
),
encoding="utf-8",
)
result = self.run_check(root)
self.assertEqual(result.returncode, 0, result.stderr)
def test_divergent_inline_default_fails(self) -> None:
root = self.fixture()
path = root / "docker-compose.yml"
path.write_text(
path.read_text().replace("${CRANK_SHARED:-value}", "${CRANK_SHARED:-drift}", 1),
encoding="utf-8",
)
result = self.run_check(root)
self.assertNotEqual(result.returncode, 0)
self.assertIn("divergent Compose inline default", result.stderr)
def test_wrong_interpolation_reference_fails(self) -> None:
root = self.fixture()
path = root / "docker-compose.yml"
path.write_text(
path.read_text().replace(
"CRANK_ADMIN: ${CRANK_ADMIN:-value}",
"CRANK_ADMIN: ${CRANK_SHARED:-value}",
),
encoding="utf-8",
)
result = self.run_check(root)
self.assertNotEqual(result.returncode, 0)
self.assertIn("wrong Compose interpolation reference", result.stderr)
def test_required_and_empty_operator_forms_are_enforced(self) -> None:
for required, expression in (
(True, "${CRANK_SHARED:-value}"),
(False, "${CRANK_SHARED-value}"),
(False, "${CRANK_SHARED:?required}"),
):
with self.subTest(required=required, expression=expression):
root = self.fixture()
schema_path = root / "docs/schemas/runtime-config.schema.json"
schema = json.loads(schema_path.read_text(encoding="utf-8"))
schema["fields"][0]["required"] = required
schema_path.write_text(json.dumps(schema), encoding="utf-8")
compose_path = root / "docker-compose.yml"
compose_path.write_text(
compose_path.read_text().replace(
"${CRANK_SHARED:-value}", expression, 1
),
encoding="utf-8",
)
result = self.run_check(root)
self.assertNotEqual(result.returncode, 0)
def test_required_empty_aware_error_form_passes(self) -> None:
root = self.fixture()
schema_path = root / "docs/schemas/runtime-config.schema.json"
schema = json.loads(schema_path.read_text(encoding="utf-8"))
schema["fields"][0]["required"] = True
schema_path.write_text(json.dumps(schema), encoding="utf-8")
for relative in (
"docker-compose.yml",
"deploy/community/docker-compose.yml",
"deploy/community/docker-compose.images.yml",
):
path = root / relative
path.write_text(
path.read_text().replace(
"${CRANK_SHARED:-value}", "${CRANK_SHARED:?must be configured}"
),
encoding="utf-8",
)
result = self.run_check(root)
self.assertEqual(result.returncode, 0, result.stderr)
if __name__ == "__main__":
unittest.main()