package main import ( "errors" "strings" "testing" "time" "node-lookup/internal/puppet" ) func mustTime(t *testing.T, s string) time.Time { t.Helper() ts, err := time.Parse(time.RFC3339Nano, s) if err != nil { t.Fatal(err) } return ts } func TestFormatWhen_Absolute(t *testing.T) { node := &puppet.Node{ReportTimestamp: "2026-07-15T04:05:06.000Z"} now := mustTime(t, "2026-07-15T10:00:00Z") got := formatWhen(node, nil, false, time.UTC, now) if got != "2026-07-15 04:05:06 UTC" { t.Fatalf("unexpected absolute time: %q", got) } } func TestFormatWhen_TimezoneApplied(t *testing.T) { loc, err := time.LoadLocation("Asia/Singapore") // UTC+8, no DST if err != nil { t.Skipf("tzdata unavailable: %v", err) } node := &puppet.Node{ReportTimestamp: "2026-07-15T04:05:06Z"} now := mustTime(t, "2026-07-15T10:00:00Z") got := formatWhen(node, nil, false, loc, now) if !strings.HasPrefix(got, "2026-07-15 12:05:06") { t.Fatalf("expected +08 time, got %q", got) } } func TestFormatWhen_Relative(t *testing.T) { node := &puppet.Node{ReportTimestamp: "2026-07-15T07:00:00Z"} now := mustTime(t, "2026-07-15T10:00:00Z") if got := formatWhen(node, nil, true, time.UTC, now); got != "3h ago" { t.Fatalf("expected '3h ago', got %q", got) } } func TestFormatWhen_EdgeCases(t *testing.T) { now := mustTime(t, "2026-07-15T10:00:00Z") if got := formatWhen(nil, errors.New("down"), false, time.UTC, now); !strings.HasPrefix(got, "error:") { t.Fatalf("expected error passthrough, got %q", got) } if got := formatWhen(nil, nil, false, time.UTC, now); got != "unknown node" { t.Fatalf("expected unknown node, got %q", got) } if got := formatWhen(&puppet.Node{ReportTimestamp: ""}, nil, false, time.UTC, now); got != "no report" { t.Fatalf("expected no report, got %q", got) } if got := formatWhen(&puppet.Node{ReportTimestamp: "garbage"}, nil, false, time.UTC, now); got != "garbage" { t.Fatalf("expected raw fallback, got %q", got) } } func TestHumanizeSince(t *testing.T) { base := mustTime(t, "2026-07-15T10:00:00Z") cases := []struct { ts string want string }{ {"2026-07-15T09:59:30Z", "30s ago"}, {"2026-07-15T09:45:00Z", "15m ago"}, {"2026-07-15T05:00:00Z", "5h ago"}, {"2026-07-13T10:00:00Z", "2d ago"}, {"2026-07-15T10:01:00Z", "1m from now"}, } for _, c := range cases { if got := humanizeSince(base, mustTime(t, c.ts)); got != c.want { t.Errorf("humanizeSince(%s) = %q, want %q", c.ts, got, c.want) } } }