import importlib.util import os def load_app_module(): repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) app_path = os.path.join(repo_root, "app.py") spec = importlib.util.spec_from_file_location("app", app_path) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) return mod def _iter_routes(app): for r in getattr(app, "routes", []): path = getattr(r, "path", None) methods = getattr(r, "methods", None) if not path or not methods: continue for m in methods: if m in ("HEAD", "OPTIONS"): continue yield (m, path) def test_contract_routes_exist_and_match_locked_baseline(): app_mod = load_app_module() routes = set(_iter_routes(app_mod.app)) # Endpoint count exact (locked baseline) assert len(routes) == 30 expected = { # FastAPI built-ins (default) ("GET", "/openapi.json"), ("GET", "/docs"), ("GET", "/docs/oauth2-redirect"), ("GET", "/redoc"), # App routes ("GET", "/workloads"), ("GET", "/workloads/read/{filename:path}"), ("POST", "/workloads/save-file"), ("POST", "/workloads/deploy/{filename:path}"), ("GET", "/files/tree"), ("GET", "/files/read"), ("POST", "/files/save"), ("DELETE", "/files/delete"), ("POST", "/files/mkdir"), ("DELETE", "/files/rmdir"), ("GET", "/pods"), ("POST", "/actions/{action}/{name}"), ("GET", "/pods-dashboard"), ("POST", "/pods/actions/{action}/{podname}"), ("GET", "/containers-dashboard"), ("GET", "/containers"), ("POST", "/containers/{action}/{name}"), ("GET", "/debug/defined-containers"), ("GET", "/dashboard"), ("GET", "/test-hybrid"), ("GET", "/containers/logs/{name}"), ("GET", "/containers/inspect/{name}"), ("GET", "/systemd/allowlist"), ("POST", "/daemon-reload"), ("POST", "/{action}/{unit}"), ("POST", "/api//"), } assert routes == expected