Files
serverAppConfigs/tests/homepage_config.bats
T
Frank 203b0be145 homepage: Protoss rename + fused Model Information component
The model cards become the StarCraft trio: frank-large -> frank-dragoon,
frank-primary -> frank-archon, frank-secondary -> frank-zealot (family
names only; endpoint/API ids unchanged). The three cards fuse into one
Model Information component: per-unit Protoss accent colours and hex-
clipped unit art, 15px type, clickable StarCraft unit-voice quotations
(original .ogg files from the fandom wiki, served from /sfx/), supervision
notes (dragoon/archon safe for autonomous use, zealot tight supervision),
compaction-model recommendation for frank-zealot, and uncensored-model
legal notes rendered as hover tooltips instead of wall text. docs.html
carries the same renames plus api-id mapping.

App-card updates: Seerr, Calibre-Web, RomM, Jellyfin set to WIP: DOWN
with Open links removed (new wip "down" badge state); Copyparty notes it
serves all other media apps content including WIP apps; Gitea and Plex
noted as needing logins separate from SSO; SearXNG noted as obfuscated,
not anonymous (upstream API keys tie to Frank).

Plumbing: Caddy @data allowlist grew /sfx/*; deploy_homepage copies sfx/;
homepage_config.bats pins the new probe names, the component fields,
art/voice-file presence, and the wip-down schema.
2026-10-08 02:40:45 -07:00

108 lines
4.9 KiB
Bash

#!/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, section, and a host:port probe. category is user or admin;
# section must be one the page's SECTION_ORDER renders. method, when
# present, is tcp or http. wip, when set, is "open", "closed" or "down".
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", "section", "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"]
wip = svc.get("wip")
assert wip is False or wip in ("open", "closed", "down"), f"{svc['name']} wip must be false, 'open', 'closed' or 'down'"
host, _, port = svc["probe"].rpartition(":")
assert host and port.isdigit(), f"bad probe {svc['probe']}"
PY
}
@test "services.json: every section is one the page renders" {
# A service whose section is not in app.js's SECTION_ORDER for its
# category is silently dropped from the page. Pin the contract.
python3 - "$REPO_ROOT/shared/homepage/services.json" <<'PY'
import json, sys
data = json.load(open(sys.argv[1]))
allowed = {
"user": {"AI", "Web Apps", "Media Library Apps"},
"admin": {"Media Library Apps", "Observability", "AI", "Infrastructure"},
}
for svc in data:
assert svc.get("section") in allowed[svc["category"]], (
f"{svc['name']} has unrenderable section {svc.get('section')!r} for {svc['category']}")
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; the endpoints
# were renamed to the Protoss set on 2026-10-08. 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}
assert by_probe.get("192.168.0.235:7770") == "frank-archon", by_probe.get("192.168.0.235:7770")
assert by_probe.get("127.0.0.1:7770") == "frank-dragoon", by_probe.get("127.0.0.1:7770")
assert by_probe.get("127.0.0.1:7771") == "frank-zealot", by_probe.get("127.0.0.1:7771")
PY
}
@test "the Model Information component is complete: text, art, voices, warnings" {
# The fused model panel is the family's only description of the fleet.
# Every model card must carry: equivalence note, supervision level, the
# unit art, at least one playable voice line, and (for uncensored models)
# the legal warning. The compaction recommendation must name the zealot.
python3 - "$REPO_ROOT/shared/homepage/services.json" "$REPO_ROOT/shared/homepage" <<'PY'
import json, os, sys
services = json.load(open(sys.argv[1]))
root = sys.argv[2]
fleet = [s for s in services if "model" in s]
assert {s["name"] for s in fleet} == {"frank-dragoon", "frank-archon", "frank-zealot"}, [s["name"] for s in fleet]
for s in fleet:
m = s["model"]
assert m["equiv"].strip(), f"{s['name']} has no equivalence note"
assert m["supervision"].strip(), f"{s['name']} has no supervision note"
assert m["sfx"], f"{s['name']} has no voice line"
assert os.path.isfile(os.path.join(root, "img", "unit-" + m["unit"] + ".jpg")), f"{s['name']} art missing"
for f in m["sfx"] + m.get("voice", {}).get("sfx", []):
assert os.path.isfile(os.path.join(root, "sfx", f)), f"{s['name']} voice {f} missing"
zealot = next(s for s in fleet if s["name"] == "frank-zealot")
assert "compaction" in zealot["model"]["use"].lower(), "zealot must be recommended as compaction model"
assert "supervision" in zealot["model"]["supervision"].lower()
for s in fleet:
m = s["model"]
uncensored = "ncensored" in m["spec"] or "bliterated" in m["spec"]
if uncensored:
assert "legal" in m.get("warn", "").lower(), f"{s['name']} is uncensored but has no legal warning"
PY
grep -q "renderModelInfo" "$REPO_ROOT/shared/homepage/app.js"
grep -q ".mi-card" "$REPO_ROOT/shared/homepage/style.css"
}