Files
felhom-controller/controller/internal/web/inframeta_test.go
T
admin b36a85ac04 R-522: a disconnected tunnel is told on the dashboard row
Since v0.292.0 (R-841) cloudflared's health check asks /ready, so a lost
connection reads unhealthy; the cloudflared row now also carries the tag
"Kivulrol nem erheto el" with a plain title, in hu+en. New parity case
dashboard_tunnel_offline (born as a key).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-05 23:12:59 +02:00

131 lines
5.1 KiB
Go

package web
import (
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
"gitea.dooplex.hu/admin/felhom-controller/internal/system"
)
// v0.126.0 Part B — infra stacks carry curated identity (name + description + Védett chip),
// and the guarded-wrong outcome: NO customer link for cloudflared/traefik (filebrowser is
// the ONLY Linked infra stack).
// COMPANION red-proof (recorded in REPORT): set Linked:true on cloudflared → the
// single-Megnyitás assertions below FAIL.
func infraStacksTestSet() []stacks.Stack {
return []stacks.Stack{
{Name: "cloudflared", Protected: true, State: stacks.StateRunning,
Meta: stacks.Metadata{Slug: "cloudflared", DisplayName: "Cloudflared"}},
{Name: "traefik", Protected: true, State: stacks.StateRunning,
Meta: stacks.Metadata{Slug: "traefik", DisplayName: "Traefik"}},
{Name: "filebrowser", Protected: true, State: stacks.StateRunning,
Meta: stacks.Metadata{Slug: "filebrowser", DisplayName: "Filebrowser"}},
}
}
func infraPageData(page string) map[string]interface{} {
return map[string]interface{}{
"Page": page, "Title": "t",
"Stacks": infraStacksTestSet(),
"MissingStorage": map[string]string{},
"NetworkWarnings": map[string]string{},
"NetworkStubs": map[string]string{},
"StorageLabels": map[string]string{},
"Subdomains": map[string]string{"filebrowser": "files"},
"RunningCount": 3, "StoppedCount": 0, "TotalCount": 3,
"SystemInfo": system.SystemInfo{},
"BackupEnabled": false,
"Domain": "demo-felhom.eu",
}
}
func assertInfraIdentity(t *testing.T, surface, html string) {
t.Helper()
for _, want := range []string{
"Cloudflare Tunnel",
"Biztonságos internetkapcsolat — a szerver portnyitás nélkül érhető el kívülről.",
"Traefik",
"Forgalomirányító (reverse proxy) — a kéréseket a megfelelő alkalmazáshoz irányítja.",
"FileBrowser",
"Fájlkezelő — a tárhely fájljainak böngészése a böngészőből.",
} {
if !strings.Contains(html, want) {
t.Errorf("%s: infra identity %q missing", surface, want)
}
}
if !strings.Contains(html, "Védett") {
t.Errorf("%s: the Védett chip is missing", surface)
}
if !strings.Contains(html, `https://files.demo-felhom.eu`) {
t.Errorf("%s: filebrowser must link files.<domain>", surface)
}
// The guarded WRONG outcome: exactly ONE customer link (filebrowser) — a second
// https:// link would mean cloudflared/traefik got one.
if n := strings.Count(html, `https://`); n != 1 {
t.Errorf("%s: expected exactly 1 customer link (filebrowser), found %d https:// occurrences", surface, n)
}
}
func TestInfraMeta_DashboardRows(t *testing.T) {
html := renderBackupPage(t, "dashboard", infraPageData("dashboard"))
assertInfraIdentity(t, "dashboard", html)
if n := strings.Count(html, "Megnyitás"); n != 1 {
t.Errorf("dashboard: expected exactly 1 Megnyitás (filebrowser), found %d", n)
}
if n := strings.Count(html, `data-fallback="/static/infra-logo.svg"`); n != 3 {
t.Errorf("dashboard: all 3 infra rows must carry the generic-icon fallback, found %d", n)
}
}
func TestInfraMeta_AppCards(t *testing.T) {
html := renderBackupPage(t, "stacks", infraPageData("stacks"))
assertInfraIdentity(t, "stacks", html)
if !strings.Contains(html, "Védett rendszerkomponens") {
t.Error("stacks: protected card chip missing")
}
if n := strings.Count(html, `data-fallback="/static/infra-logo.svg"`); n != 3 {
t.Errorf("stacks: all 3 infra cards must carry the generic-icon fallback, found %d", n)
}
}
func TestInfraMetaFor_RegularAppIsNil(t *testing.T) {
if infraMetaFor("radarr") != nil {
t.Error("regular apps must have no infra metadata")
}
if m := infraMetaFor("cloudflared"); m == nil || m.Linked {
t.Error("cloudflared must exist and must NOT be Linked (no customer UI)")
}
if m := infraMetaFor("filebrowser"); m == nil || !m.Linked {
t.Error("filebrowser must be the Linked infra stack")
}
}
// R-522: a disconnected tunnel (cloudflared's /ready health check failing → State unhealthy) is told
// on the dashboard row as „Kívülről nem érhető el", not left at a state the household reads as fine;
// a connected tunnel and an unhealthy OTHER infra stack carry no such tag.
func TestR522_TunnelOfflineTagOnlyForUnhealthyCloudflared(t *testing.T) {
data := infraPageData("dashboard")
html := renderBackupPage(t, "dashboard", data)
if strings.Contains(html, `data-tunnel-offline="true"`) {
t.Fatal("a connected tunnel must not carry the offline tag")
}
set := infraStacksTestSet()
set[0].State = stacks.StateUnhealthy // cloudflared
data["Stacks"] = set
html = renderBackupPage(t, "dashboard", data)
if !strings.Contains(html, `data-tunnel-offline="true"`) || !strings.Contains(html, "rhet") {
t.Fatal("an unhealthy cloudflared must carry the offline tag with its sentence")
}
if !strings.Contains(html, "Kívülről nem érhető el") {
t.Error("the tag text must be the Hungarian sentence")
}
set = infraStacksTestSet()
set[1].State = stacks.StateUnhealthy // traefik
data["Stacks"] = set
if strings.Contains(renderBackupPage(t, "dashboard", data), `data-tunnel-offline="true"`) {
t.Error("only cloudflared's state means the tunnel is down")
}
}