test(#21): тесты инвариантов compose, Make-цели стенда и обновлённый deploy/README
- tests/deploy/test_compose_manifests.py — 7 тестов на compose-инварианты (без Docker, только PyYAML) — готовы к включению в CI (#17); - Makefile: dev-up/dev-up-stub/dev-down/dev-logs/dev-verify/dev-verify-stub/ dev-reset/test-netns/test-netns-keep/test-netns-ci/test-manifests/lint-shell, переменные HOST_DIR, DASH_PORT, NETNS_ARGS; dev-up без docker падает с внятным сообщением, а не трейсбеком; - deploy/README.md — таблица артефактов и быстрые команды стенда. Refs #21 Refs #15
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
"""Тесты статических инвариантов compose-файлов стенда (задача #21).
|
||||
|
||||
Проверяют то, что иначе выяснилось бы только на Docker-хосте: порт 8080,
|
||||
монтирования /config и /data, healthcheck, capabilities, privileged netns-раннер.
|
||||
Работают без Docker — только PyYAML (см. requirements-dev.txt).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import unittest
|
||||
|
||||
import validate_compose
|
||||
|
||||
HERE = pathlib.Path(__file__).resolve().parent
|
||||
ROOT = HERE.parents[1]
|
||||
|
||||
|
||||
@unittest.skipUnless(validate_compose.yaml is not None, "нужен PyYAML (make venv)")
|
||||
class ComposeManifestsTest(unittest.TestCase):
|
||||
def test_dev_compose_matches_acceptance_criteria(self) -> None:
|
||||
errors, _ = validate_compose.validate(require_dockerfile=False)
|
||||
self.assertEqual([], errors, "compose-файлы стенда нарушают инварианты AC #21")
|
||||
|
||||
def test_dev_compose_publishes_app_port_to_host(self) -> None:
|
||||
doc = validate_compose.load(validate_compose.DEV)
|
||||
ports = " ".join(str(p) for p in doc["services"]["awg-dash"]["ports"])
|
||||
self.assertIn(":8080", ports, "наружу должен публиковаться контейнерный порт приложения 8080")
|
||||
|
||||
def test_dev_compose_mounts_state_directories(self) -> None:
|
||||
doc = validate_compose.load(validate_compose.DEV)
|
||||
targets = validate_compose._mount_targets(doc["services"]["awg-dash"])
|
||||
self.assertIn("/config", targets)
|
||||
self.assertIn("/data", targets)
|
||||
|
||||
def test_healthcheck_hits_api_v1_health(self) -> None:
|
||||
doc = validate_compose.load(validate_compose.DEV)
|
||||
test_cmd = validate_compose.jsonish(doc["services"]["awg-dash"]["healthcheck"]["test"])
|
||||
self.assertIn("/api/v1/health", test_cmd)
|
||||
|
||||
def test_test_compose_runs_netns_harness_privileged(self) -> None:
|
||||
doc = validate_compose.load(validate_compose.TEST)
|
||||
svc = doc["services"]["netns"]
|
||||
self.assertTrue(svc["privileged"])
|
||||
self.assertIn("tests/netns/run.sh", " ".join(svc["entrypoint"]))
|
||||
|
||||
def test_stub_override_is_labelled_dev_only(self) -> None:
|
||||
text = validate_compose.STUB.read_text(encoding="utf-8")
|
||||
self.assertIn("api_stub.py", text)
|
||||
self.assertIn("DEV", text.upper())
|
||||
|
||||
def test_netns_harness_entrypoints_exist(self) -> None:
|
||||
for rel in (
|
||||
"tests/netns/run.sh",
|
||||
"tests/netns/topology.sh",
|
||||
"tests/netns/lib.sh",
|
||||
"tests/netns/probe.py",
|
||||
"tests/netns/mock_wg_endpoint.py",
|
||||
"tests/netns/leak_sink.py",
|
||||
"deploy/api_stub.py",
|
||||
"deploy/verify-dev-stand.sh",
|
||||
):
|
||||
with self.subTest(rel=rel):
|
||||
self.assertTrue((ROOT / rel).exists(), f"нет файла {rel}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
Reference in New Issue
Block a user