Files
felhom-controller/controller/internal/agentapi/diskverdict_test.go
T
admin c97975c1df v0.169.0: disk-health card + degradation notification (Lemezek állapota)
Consumes the agent v0.94.0 smart payload (MinAgent floor unchanged; feature-detect
by presence). One pure verdict fn agentapi.DiskVerdictFor shared by the dashboard
card and the 6h check. Card via a 60s /disks TTL cache (anti-smartctl-storm);
unreachable agent -> Nincs adat, page never blocks. disk-health-check (6h) emits
disk_health_degraded on a degradation only vs an in-memory baseline (first run
silent, recovery/UNKNOWN never notify, multi-attr -> one event). No global banner
(deliberate). Pairs with the hub allowlist bump.

Tests: verdict table (>=90 red-proof), notifier emit, check first-run-silent
(red-proof), degradation-once, recovery-silent, UNKNOWN-excluded, FAILING-critical,
nil-smart card, TTL cache.
2026-07-24 21:27:16 +02:00

64 lines
2.9 KiB
Go

package agentapi
import "testing"
func ip(v int) *int { return &v }
// Verdict table (Part 2). Red-proof: change the PercentageUsed boundary from `>= 90` to `> 90` in
// DiskVerdictFor → the "NVMe percentage_used exactly 90 → Figyelmeztetés" case fails.
func TestDiskVerdictFor(t *testing.T) {
cases := []struct {
name string
in *SmartSummary
want DiskVerdict
}{
{"nil → unknown", nil, DiskVerdictUnknown},
{"empty health → unknown", &SmartSummary{Health: ""}, DiskVerdictUnknown},
{"UNKNOWN → unknown", &SmartSummary{Health: SmartUnknown}, DiskVerdictUnknown},
{"FAILING → fail", &SmartSummary{Health: SmartFailing}, DiskVerdictFail},
{"FAILING beats counters", &SmartSummary{Health: SmartFailing, ReallocatedSectors: ip(0)}, DiskVerdictFail},
{"PASSED clean → ok", &SmartSummary{Health: SmartPassed, ReallocatedSectors: ip(0), PendingSectors: ip(0), TemperatureC: ip(30)}, DiskVerdictOK},
{"PASSED nil counters → ok", &SmartSummary{Health: SmartPassed}, DiskVerdictOK},
{"reallocated>0 → warn", &SmartSummary{Health: SmartPassed, ReallocatedSectors: ip(1)}, DiskVerdictWarn},
{"pending>0 → warn", &SmartSummary{Health: SmartPassed, PendingSectors: ip(5)}, DiskVerdictWarn},
{"offline_unc>0 → warn", &SmartSummary{Health: SmartPassed, OfflineUncorrectable: ip(2)}, DiskVerdictWarn},
{"critical_warning>0 → warn", &SmartSummary{Health: SmartPassed, CriticalWarning: ip(1)}, DiskVerdictWarn},
{"media_errors>0 → warn", &SmartSummary{Health: SmartPassed, MediaErrors: ip(3)}, DiskVerdictWarn},
{"percentage_used 89 → ok", &SmartSummary{Health: SmartPassed, PercentageUsed: ip(89)}, DiskVerdictOK},
{"percentage_used exactly 90 → warn", &SmartSummary{Health: SmartPassed, PercentageUsed: ip(90)}, DiskVerdictWarn},
{"percentage_used 95 → warn", &SmartSummary{Health: SmartPassed, PercentageUsed: ip(95)}, DiskVerdictWarn},
}
for _, c := range cases {
if got := DiskVerdictFor(c.in); got != c.want {
t.Errorf("%s: DiskVerdictFor = %d, want %d", c.name, got, c.want)
}
}
}
func TestDiskVerdict_Label(t *testing.T) {
want := map[DiskVerdict]string{
DiskVerdictUnknown: "Nincs adat",
DiskVerdictOK: "Rendben",
DiskVerdictWarn: "Figyelmeztetés",
DiskVerdictFail: "Hiba",
}
for v, w := range want {
if got := v.Label(); got != w {
t.Errorf("verdict %d Label = %q, want %q", v, got, w)
}
}
}
// A warn lists every triggering attribute at once (Scenario "multiple attributes degrade" → ONE event).
func TestDegradedAttributes_ListsAll(t *testing.T) {
s := &SmartSummary{Health: SmartPassed, PendingSectors: ip(5), ReallocatedSectors: ip(2), PercentageUsed: ip(91)}
got := DegradedAttributes(s)
if len(got) != 3 {
t.Fatalf("want 3 attributes, got %d: %v", len(got), got)
}
// clean disk → none
if a := DegradedAttributes(&SmartSummary{Health: SmartPassed}); len(a) != 0 {
t.Errorf("clean disk should list no attributes, got %v", a)
}
}