b36a85ac04
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
131 lines
5.1 KiB
Go
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")
|
|
}
|
|
}
|