#!/usr/bin/env bats # Seam: shared/homepage/services.json — the service config the checker reads # and the pages render. It must be a JSON list; every entry carries name, # category, and a host:port probe. category is user or admin. method, when # present, is tcp or http. load helpers @test "services.json: valid schema" { python3 - "$REPO_ROOT/shared/homepage/services.json" <<'PY' import json, sys path = sys.argv[1] try: data = json.load(open(path)) except FileNotFoundError: sys.exit("services.json not found") assert isinstance(data, list), "top level must be a list" assert data, "the list must not be empty" for svc in data: for field in ("name", "category", "probe"): assert field in svc, f"{svc.get('name', '?')} is missing {field}" assert svc["category"] in ("user", "admin"), svc["category"] if "method" in svc: assert svc["method"] in ("tcp", "http"), svc["method"] host, _, port = svc["probe"].rpartition(":") assert host and port.isdigit(), f"bad probe {svc['probe']}" PY } @test "services.json: every user service has an Open link, status-only backends do not" { python3 - "$REPO_ROOT/shared/homepage/services.json" <<'PY' import json, sys data = json.load(open(sys.argv[1])) for svc in data: show = svc.get("showLink", True) if show: assert svc.get("url"), f"{svc['name']} shows a link but has no url" else: assert not svc.get("url"), f"{svc['name']} is status-only but has a url" PY } @test "the LLM status cards track the fleet, not retired service names" { # This drifted twice: the card probing Dankserver's :7770 was still named # frank-small long after frank-medium took that endpoint, and Dankclaw's # second endpoint now exists again as a real service on its own port. The # probe target is what matters; the name has to follow it. python3 - "$REPO_ROOT/shared/homepage/services.json" <<'PY' import json, sys services = json.load(open(sys.argv[1])) by_probe = {s["probe"]: s["name"] for s in services} medium = by_probe.get("192.168.0.235:7770") assert medium == "frank-medium", f"Dankserver :7770 card is named {medium!r}" # Dankclaw serves two endpoints on two ports: frank-large (halogen, 7770) and # frank-small (llama.cpp on the RTX 3090, 7771). assert by_probe.get("127.0.0.1:7770") == "frank-large", by_probe.get("127.0.0.1:7770") assert by_probe.get("127.0.0.1:7771") == "frank-small", by_probe.get("127.0.0.1:7771") PY }