166 lines
4.7 KiB
Python
166 lines
4.7 KiB
Python
import importlib
|
|
import importlib.util
|
|
from pathlib import Path
|
|
from fastapi.testclient import TestClient
|
|
|
|
|
|
def load_app_module():
|
|
try:
|
|
return importlib.import_module("app")
|
|
except ModuleNotFoundError:
|
|
app_path = Path("/app/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
|
|
|
|
|
|
# ============================================================
|
|
# /containers/{action}/{name}
|
|
# ============================================================
|
|
|
|
def test_container_action_invalid_action_contract():
|
|
app_mod = load_app_module()
|
|
client = TestClient(app_mod.app)
|
|
|
|
r = client.post("/containers/invalid/dummy")
|
|
|
|
# Freeze whatever baseline currently is
|
|
body = r.json()
|
|
|
|
# Must be JSON and deterministic structure
|
|
assert isinstance(body, (dict, list))
|
|
|
|
# If dict variant
|
|
if isinstance(body, dict):
|
|
assert set(body.keys()) == {"error"}
|
|
assert body["error"] == "Invalid action"
|
|
|
|
|
|
def test_container_action_failure_shape_contract():
|
|
app_mod = load_app_module()
|
|
client = TestClient(app_mod.app)
|
|
|
|
r = client.post("/containers/start/nonexistent_container_12345")
|
|
|
|
body = r.json()
|
|
|
|
# Freeze exact variant structure
|
|
if isinstance(body, dict) and body.get("method") == "podman":
|
|
assert "name" in body
|
|
assert "cmd" in body
|
|
assert "status_code" in body
|
|
else:
|
|
# Any other baseline variant must still be JSON
|
|
assert isinstance(body, (dict, list))
|
|
|
|
|
|
# ============================================================
|
|
# /actions/{action}/{name}
|
|
# ============================================================
|
|
|
|
def test_legacy_actions_invalid_action_contract():
|
|
app_mod = load_app_module()
|
|
client = TestClient(app_mod.app)
|
|
|
|
r = client.post("/actions/invalid/dummy")
|
|
body = r.json()
|
|
|
|
assert isinstance(body, dict)
|
|
assert set(body.keys()) == {"status"}
|
|
assert body["status"] == "unknown"
|
|
|
|
|
|
# ============================================================
|
|
# /pods/actions/{action}/{podname}
|
|
# ============================================================
|
|
|
|
def test_pods_action_invalid_action_contract():
|
|
app_mod = load_app_module()
|
|
client = TestClient(app_mod.app)
|
|
|
|
r = client.post("/pods/actions/invalid/dummy")
|
|
body = r.json()
|
|
|
|
# Baseline may return tuple-style 400 or JSON
|
|
assert isinstance(body, (dict, list))
|
|
|
|
if isinstance(body, dict) and "error" in body:
|
|
assert body["error"] == "Invalid action"
|
|
|
|
|
|
def test_pods_action_variant_shape_contract():
|
|
app_mod = load_app_module()
|
|
client = TestClient(app_mod.app)
|
|
|
|
r = client.post("/pods/actions/start/nonexistent_pod_12345")
|
|
body = r.json()
|
|
|
|
# Freeze possible baseline variants
|
|
if isinstance(body, dict):
|
|
if body.get("method") == "systemd_then_podman":
|
|
assert set(body.keys()) == {"method", "note", "systemd", "podman"}
|
|
assert body["note"] == "systemd failed; falling back to podman"
|
|
elif body.get("method") == "podman":
|
|
assert set(body.keys()) == {"method", "result"}
|
|
else:
|
|
assert isinstance(body, list)
|
|
|
|
|
|
# ============================================================
|
|
# /{action}/{unit}
|
|
# ============================================================
|
|
|
|
def test_systemctl_wrapper_invalid_action_contract():
|
|
app_mod = load_app_module()
|
|
client = TestClient(app_mod.app)
|
|
|
|
r = client.post("/invalid/dummy.service")
|
|
body = r.json()
|
|
|
|
assert set(body.keys()) == {"detail"}
|
|
assert body["detail"] == "Invalid action"
|
|
|
|
|
|
def test_systemctl_wrapper_status_shape_contract():
|
|
app_mod = load_app_module()
|
|
client = TestClient(app_mod.app)
|
|
|
|
r = client.post("/status/nonexistent.service")
|
|
body = r.json()
|
|
|
|
# allowlist may cause 403
|
|
if r.status_code == 403:
|
|
assert set(body.keys()) == {"detail"}
|
|
else:
|
|
assert set(body.keys()) == {"cmd", "exit", "output"}
|
|
|
|
|
|
# ============================================================
|
|
# /api/<action>/<unit>
|
|
# ============================================================
|
|
|
|
def test_legacy_api_invalid_action_contract():
|
|
app_mod = load_app_module()
|
|
client = TestClient(app_mod.app)
|
|
|
|
r = client.post("/api/invalid/dummy.service")
|
|
body = r.json()
|
|
|
|
# Baseline: HTTPException 400 with {"detail": "..."}
|
|
assert r.status_code == 400
|
|
assert set(body.keys()) == {"detail"}
|
|
|
|
|
|
def test_legacy_api_status_shape_contract():
|
|
app_mod = load_app_module()
|
|
client = TestClient(app_mod.app)
|
|
|
|
r = client.post("/api/status/nonexistent.service")
|
|
body = r.json()
|
|
|
|
if r.status_code == 403:
|
|
assert set(body.keys()) == {"detail"}
|
|
else:
|
|
assert set(body.keys()) == {"cmd", "exit", "output"}
|