Files
HODA/internal/apps/store_test.go
T
TMCandClaude Opus 5.5 60b4c79b0f
CI / build (push) Successful in 40s
Show internal URLs, list label problems, copy a label template
Tiles now show the public URL and, when set, the internal URL as a second
link. Label names ignore case and - versus _. Containers HODA can't fully
use are listed above the tiles with the reason: labels without enable,
a missing url, or an unknown label name with a suggestion (category ->
group). To find labels without enable, discovery reads all containers.

A "Copy labels" button copies every label as a commented template, and
falls back to the copy command where the clipboard API is missing (plain
http on the LAN).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:55:06 +01:00

91 lines
3.2 KiB
Go

package apps
import (
"testing"
"time"
)
func TestSnapshotOrder(t *testing.T) {
s := NewStore()
s.SetApps(LocalSource, "host-1", []App{
{ID: "000000000001", Name: "Vaultwarden", Group: "Dev"},
{ID: "000000000002", Name: "gitea", Group: "Dev"},
{ID: "000000000003", Name: "Jellyfin", Group: "Media"},
{ID: "000000000004", Name: "Zebra", Group: "dev", Order: -1},
{ID: "000000000006", Name: "Adminer", Group: DefaultGroup},
}, nil)
s.SetReported("http://host-2:8081", HostStatus{Name: "host-2", OK: true}, []App{
{ID: "000000000005", Name: "Immich", Group: "Media", Host: "host-2"},
}, nil)
resp := s.Snapshot(nil)
var names []string
for _, a := range resp.Apps {
names = append(names, a.Name)
}
want := []string{"Zebra", "gitea", "Vaultwarden", "Immich", "Jellyfin", "Adminer"}
if len(names) != len(want) {
t.Fatalf("apps = %v, want %v", names, want)
}
for i := range want {
if names[i] != want[i] {
t.Fatalf("apps = %v, want %v", names, want)
}
}
if len(resp.Hosts) != 2 || resp.Hosts[0].Name != "host-1" || resp.Hosts[1].Name != "host-2" {
t.Errorf("hosts = %+v, want host-1 then host-2", resp.Hosts)
}
for _, h := range resp.Hosts {
if !h.OK || h.LastOK != nil {
t.Errorf("host %+v: a reachable host is sent without lastOk", h)
}
}
if resp.Issues == nil || len(resp.Issues) != 0 {
t.Errorf("issues = %#v, want an empty list", resp.Issues)
}
}
func TestSnapshotIssues(t *testing.T) {
s := NewStore()
s.SetApps(LocalSource, "host-1", nil, []Issue{{Host: "host-1", Container: "keep", Message: "b"}, {Host: "host-1", Container: "fima", Message: "a"}})
s.SetReported("http://host-2:8081", HostStatus{Name: "host-2", OK: true}, nil, []Issue{{Host: "host-2", Container: "immich", Message: "c"}})
resp := s.Snapshot(nil)
if len(resp.Issues) != 3 || resp.Issues[0].Container != "fima" || resp.Issues[2].Host != "host-2" {
t.Errorf("issues = %+v, want all three sorted by host, then container", resp.Issues)
}
}
func TestSetErrorKeepsApps(t *testing.T) {
s := NewStore()
at := time.Date(2026, 11, 2, 14, 2, 0, 0, time.UTC)
s.now = func() time.Time { return at }
s.SetApps(LocalSource, "host-1", []App{{ID: "000000000001", Name: "Gitea"}}, nil)
s.SetError(LocalSource, "host-1")
resp := s.Snapshot(nil)
if len(resp.Apps) != 1 {
t.Fatalf("apps = %d, want the last known app kept", len(resp.Apps))
}
if resp.Hosts[0].OK || resp.Hosts[0].LastOK == nil || !resp.Hosts[0].LastOK.Equal(at) {
t.Errorf("host = %+v, want unreachable, last OK at %s", resp.Hosts[0], at)
}
}
func TestSnapshotLocalIcons(t *testing.T) {
s := NewStore()
s.SetApps(LocalSource, "host-1", []App{{ID: "000000000001", Name: "Gitea"}}, nil)
s.SetReported("http://host-2:8081", HostStatus{Name: "host-2", OK: true}, []App{
{ID: "000000000002", Name: "Immich", Icon: "/icons/000000000002?v=abc"},
}, nil)
resp := s.Snapshot(func(id string) string { return "/icons/" + id + "?v=local" })
for _, a := range resp.Apps {
want := map[string]string{"Gitea": "/icons/000000000001?v=local", "Immich": "/icons/000000000002?v=abc"}[a.Name]
if a.Icon != want {
t.Errorf("%s icon = %q, want %q", a.Name, a.Icon, want)
}
}
if key, _, ok := s.Lookup("000000000002"); !ok || key != "http://host-2:8081" {
t.Errorf("Lookup = %q, %v; want the agent's key", key, ok)
}
}