fcd9f09927
Docker unreachable, protected container down, and the four storage-path entries now carry a bundle key beside their wire text; the wire text is unchanged byte for byte. Hungarian values of the four storage keys equal the frozen formats (i18n_go_parity). Red-proven: a zero MsgRef on the unavailable-path warning fails TestR79_RemainingProducersCarryTheirDashboardSentence and TestR79_HealthBannersFollowTheHousehold (producer-driven, English household sees Hungarian). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
146 lines
6.4 KiB
Go
146 lines
6.4 KiB
Go
package monitor
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/system"
|
|
)
|
|
|
|
// R-516 item 10 — the resource warnings („SSD disk usage high: 90%") reached the household's banner in
|
|
// English on every page. checkResources now attaches the dashboard's own sentence (a bundle key) beside
|
|
// each one, and the wire text is byte-for-byte what it was.
|
|
//
|
|
// COMPANION RED-PROOF: give the SSD warning a zero MsgRef (addWarning) — the "warn" case fails here
|
|
// and the banner test in internal/web shows the English wire text again.
|
|
func TestR516_ResourceWarningsCarryTheirDashboardSentence(t *testing.T) {
|
|
cfg := config.Default()
|
|
cases := []struct {
|
|
name string
|
|
info system.SystemInfo
|
|
wantIssue, wantKey []string
|
|
wantWire []string
|
|
}{
|
|
{"warn", system.SystemInfo{DiskPercent: 85, HDDConfigured: true, HDDPercent: 85, MemPercent: 90, CPUPercent: 95,
|
|
TemperatureCelsius: 80, TemperatureSource: "coretemp"}, nil,
|
|
[]string{"health.system_disk_high", "health.data_disk_high", "health.memory_high", "health.cpu_high", "health.temperature_high"},
|
|
[]string{"SSD disk usage high: 85%", "HDD disk usage high: 85%", "Memory usage high: 90%", "CPU usage high: 95%", "Temperature high: 80°C (coretemp)"}},
|
|
{"critical", system.SystemInfo{DiskPercent: 95, HDDConfigured: true, HDDPercent: 96},
|
|
[]string{"health.system_disk_critical", "health.data_disk_critical"}, nil,
|
|
[]string{"SSD disk usage critical: 95%", "HDD disk usage critical: 96%"}},
|
|
}
|
|
for _, c := range cases {
|
|
r := &HealthReport{}
|
|
checkResources(r, c.info, cfg, nil, false)
|
|
texts, keys := r.Warnings, []string{}
|
|
for i := range r.Warnings {
|
|
keys = append(keys, r.WarningMsgAt(i).Key)
|
|
}
|
|
if c.wantIssue != nil {
|
|
texts, keys = r.Issues, nil
|
|
for i := range r.Issues {
|
|
keys = append(keys, r.IssueMsgAt(i).Key)
|
|
}
|
|
}
|
|
want := c.wantKey
|
|
if c.wantIssue != nil {
|
|
want = c.wantIssue
|
|
}
|
|
if len(texts) != len(c.wantWire) {
|
|
t.Fatalf("%s: %d entries %q, want %d — the cases below would be compared against nothing", c.name, len(texts), texts, len(c.wantWire))
|
|
}
|
|
for i := range texts {
|
|
if texts[i] != c.wantWire[i] {
|
|
t.Errorf("%s: the wire text moved: %q, want %q", c.name, texts[i], c.wantWire[i])
|
|
}
|
|
if keys[i] != want[i] {
|
|
t.Errorf("%s: %q carries dashboard key %q, want %q", c.name, texts[i], keys[i], want[i])
|
|
}
|
|
}
|
|
if len(r.Warnings) != len(r.WarningKinds) || len(r.Warnings) != len(r.WarningMsgs) || len(r.Issues) != len(r.IssueMsgs) {
|
|
t.Errorf("%s: the parallel slices drifted apart", c.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// R-79 option A (operator ruling D3, 2026-10-08) — the six producers R-516 item 10 left verbatim:
|
|
// Docker unreachable, a protected container down, and the four storage-path entries. Each now
|
|
// carries its dashboard sentence; the wire text is byte for byte what it was (it is ON THE WIRE,
|
|
// report health.*; the frozen storage formats are also pinned by TestStorageWarningFormatsAreFrozen).
|
|
//
|
|
// COMPANION RED-PROOF: give any one of them a zero MsgRef (e.g. the unavailable-path warning back to
|
|
// addWarning) — its case fails here, and TestR79_StorageBannerFollowsTheHousehold in internal/web
|
|
// shows the Hungarian wire text to an English household again.
|
|
func TestR79_RemainingProducersCarryTheirDashboardSentence(t *testing.T) {
|
|
type entry struct{ wire, key string }
|
|
check := func(name string, texts []string, msgs func(int) MsgRef, want []entry) {
|
|
t.Helper()
|
|
if len(texts) != len(want) {
|
|
t.Fatalf("%s: %d entries %q, want %d — the cases below would be compared against nothing", name, len(texts), texts, len(want))
|
|
}
|
|
for i := range texts {
|
|
if texts[i] != want[i].wire {
|
|
t.Errorf("%s: the wire text moved: %q, want %q", name, texts[i], want[i].wire)
|
|
}
|
|
if k := msgs(i).Key; k != want[i].key {
|
|
t.Errorf("%s: %q carries dashboard key %q, want %q", name, texts[i], k, want[i].key)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Docker and protected containers: driven through the same adders RunHealthCheck calls.
|
|
r := &HealthReport{}
|
|
addDockerIssue(r, fmt.Errorf("docker not reachable: exit status 1"))
|
|
addProtectedIssues(r, []string{"cloudflared", "traefik"})
|
|
check("docker+protected", r.Issues, r.IssueMsgAt, []entry{
|
|
{"Docker: docker not reachable: exit status 1", "health.docker_unreachable"},
|
|
{"Protected container not running: cloudflared", "health.protected_container_down"},
|
|
{"Protected container not running: traefik", "health.protected_container_down"},
|
|
})
|
|
if a := r.IssueMsgAt(1).Args; len(a) != 1 || a[0] != "cloudflared" {
|
|
t.Errorf("protected-container sentence args = %v, want [cloudflared]", a)
|
|
}
|
|
|
|
// Storage paths: a missing path, a path that is not a mount point at 92 %, one at 97 %.
|
|
oldMount, oldUsage := isMountPointFn, diskUsageFn
|
|
defer func() { isMountPointFn, diskUsageFn = oldMount, oldUsage }()
|
|
isMountPointFn = func(p string) bool { return !strings.HasSuffix(p, "/notsep") }
|
|
diskUsageFn = func(p string) *system.DiskUsageInfo {
|
|
switch {
|
|
case strings.HasSuffix(p, "/notsep"):
|
|
return &system.DiskUsageInfo{UsedPercent: 92}
|
|
case strings.HasSuffix(p, "/full"):
|
|
return &system.DiskUsageInfo{UsedPercent: 97}
|
|
}
|
|
return nil
|
|
}
|
|
dir := t.TempDir()
|
|
for _, sub := range []string{"notsep", "full"} {
|
|
if err := os.Mkdir(filepath.Join(dir, sub), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
missing := filepath.Join(dir, "missing")
|
|
r = &HealthReport{}
|
|
addStorage(r, []settings.StoragePath{{Path: missing}, {Path: dir + "/notsep"}, {Path: dir + "/full"}})
|
|
check("storage warnings", r.Warnings, r.WarningMsgAt, []entry{
|
|
{"Adattároló nem elérhető: " + missing, "health.storage_unavailable"},
|
|
{"Az adattároló (" + dir + "/notsep) nem külön meghajtón van — az adatok a rendszermeghajtóra íródnak", "health.storage_not_separate"},
|
|
{"Adattároló használat magas: " + dir + "/notsep (92%)", "health.storage_usage_high"},
|
|
})
|
|
check("storage issues", r.Issues, r.IssueMsgAt, []entry{
|
|
{"Adattároló majdnem megtelt: " + dir + "/full (97%)", "health.storage_almost_full"},
|
|
})
|
|
if len(r.Warnings) != len(r.WarningKinds) || len(r.Warnings) != len(r.WarningMsgs) || len(r.Issues) != len(r.IssueMsgs) {
|
|
t.Errorf("storage: the parallel slices drifted apart")
|
|
}
|
|
if got := r.WarningKindAt(1); got != WarnKindStorageNotSeparate {
|
|
t.Errorf("not-separate warning lost its kind: %q", got)
|
|
}
|
|
}
|