status across configured hosts via read-only commands (claude-docker wrapper on the Pi5, local docker/systemctl here). Stable table + findings list, exit 0/2/1, CI job containers skipped, unreachable hosts become findings. Parsers unit-tested; smoked against the real fleet (19 genuine findings). Spec: doc/tool-parity.md 4.2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011KikHkfCiC3yELbsMN8fT9
79 lines
2.1 KiB
Go
79 lines
2.1 KiB
Go
package check
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
const psOut = `GITEA-ACTIONS-TASK-120-WORKFLOW-x running Up 2 minutes catthehacker/ubuntu:act-latest
|
|
helm-hub running Up 58 minutes localhost:5000/helmd:latest
|
|
gitea-d running Up 27 hours (healthy) gitea/gitea:1.24
|
|
deluge exited Exited (1) 3 days ago linuxserver/deluge
|
|
kuma running Up 4 days (unhealthy) louislam/uptime-kuma:1`
|
|
|
|
func TestParseDockerPS(t *testing.T) {
|
|
rows := ParseDockerPS(psOut)
|
|
if len(rows) != 4 {
|
|
t.Fatalf("got %d rows, want 4 (CI worker skipped)", len(rows))
|
|
}
|
|
if rows[0].Name != "helm-hub" || !rows[0].Healthy() {
|
|
t.Errorf("helm-hub should be healthy: %+v", rows[0])
|
|
}
|
|
for _, r := range rows {
|
|
switch r.Name {
|
|
case "deluge":
|
|
if r.Healthy() {
|
|
t.Error("exited container marked healthy")
|
|
}
|
|
case "kuma":
|
|
if r.Healthy() {
|
|
t.Error("unhealthy container marked healthy")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const dfOut = `Mounted on Use% Avail
|
|
/ 81% 82G
|
|
/srv/storage1 58% 1.2T`
|
|
|
|
func TestParseDF(t *testing.T) {
|
|
rows := ParseDF(dfOut)
|
|
if len(rows) != 2 || rows[0].Target != "/" || rows[0].UsedPct != 81 || rows[1].Avail != "1.2T" {
|
|
t.Errorf("df parse wrong: %+v", rows)
|
|
}
|
|
}
|
|
|
|
func TestParseFailedUnits(t *testing.T) {
|
|
units := ParseFailedUnits("● backup.service loaded failed failed Nightly backup\nfoo.timer loaded failed failed X\n\n")
|
|
if len(units) != 2 || units[0] != "backup.service" || units[1] != "foo.timer" {
|
|
t.Errorf("failed units: %v", units)
|
|
}
|
|
if len(ParseFailedUnits("")) != 0 {
|
|
t.Error("empty output should give no units")
|
|
}
|
|
}
|
|
|
|
func TestEvaluate(t *testing.T) {
|
|
findings := Evaluate("pi5",
|
|
ParseDockerPS(psOut),
|
|
ParseDF(dfOut),
|
|
[]string{"backup.service"}, nil, 80)
|
|
var text []string
|
|
for _, f := range findings {
|
|
text = append(text, f.String())
|
|
}
|
|
joined := strings.Join(text, "\n")
|
|
for _, want := range []string{"deluge", "kuma", "disk / at 81%", "backup.service"} {
|
|
if !strings.Contains(joined, want) {
|
|
t.Errorf("findings missing %q:\n%s", want, joined)
|
|
}
|
|
}
|
|
if strings.Contains(joined, "storage1") {
|
|
t.Error("58% disk should not be a finding at threshold 80")
|
|
}
|
|
if len(findings) != 4 {
|
|
t.Errorf("got %d findings, want 4", len(findings))
|
|
}
|
|
}
|