package chlog import ( "encoding/json" "strings" "testing" "time" ) func sampleRow() Row { return Row{ Timestamp: "2026-08-23 05:00:01.234", Host: "node1", Source: "k8s", Namespace: "logging", Pod: "vector-abc", Container: "vector", Stream: "stdout", Severity: "Error", Message: "something broke", Labels: map[string]string{"app": "vector"}, Fields: map[string]string{"req_id": "42"}, } } func render(t *testing.T, format string, color bool, r Row) string { t.Helper() f, err := NewFormatter(format, color) if err != nil { t.Fatal(err) } var b strings.Builder if err := f(&b, r); err != nil { t.Fatal(err) } return b.String() } func TestTextNoColor(t *testing.T) { out := render(t, "text", false, sampleRow()) want := "2026-08-23T05:00:01.234Z logging/vector-abc something broke\n" if out != want { t.Errorf("got %q, want %q", out, want) } if strings.Contains(out, "\x1b[") { t.Error("no-color output contains ANSI escapes") } } func TestTextColor(t *testing.T) { out := render(t, "text", true, sampleRow()) if !strings.Contains(out, "\x1b[") { t.Error("color output missing ANSI escapes") } if !strings.Contains(out, "logging/vector-abc") || !strings.Contains(out, "something broke") { t.Errorf("content missing: %q", out) } } func TestTextVMRowUsesHost(t *testing.T) { r := sampleRow() r.Source = "vm" r.Namespace = "" r.Pod = "" out := render(t, "text", false, r) if !strings.Contains(out, " node1 ") { t.Errorf("vm row should show host: %q", out) } } func TestJSONFormat(t *testing.T) { out := render(t, "json", false, sampleRow()) var m map[string]any if err := json.Unmarshal([]byte(out), &m); err != nil { t.Fatalf("not valid JSON: %v: %q", err, out) } if m["timestamp"] != "2026-08-23T05:00:01.234Z" { t.Errorf("timestamp = %v", m["timestamp"]) } if m["message"] != "something broke" || m["namespace"] != "logging" { t.Errorf("fields wrong: %v", m) } labels, _ := m["labels"].(map[string]any) if labels["app"] != "vector" { t.Errorf("labels = %v", m["labels"]) } } func TestLogfmtFormat(t *testing.T) { out := render(t, "logfmt", false, sampleRow()) for _, want := range []string{ "ts=2026-08-23T05:00:01.234Z", "ns=logging", "pod=vector-abc", "severity=Error", "app=vector", `msg="something broke"`, } { if !strings.Contains(out, want) { t.Errorf("logfmt missing %q: %q", want, out) } } if !strings.HasSuffix(out, "\n") { t.Error("missing trailing newline") } } func TestLogfmtQuoting(t *testing.T) { r := sampleRow() r.Message = `plain` out := render(t, "logfmt", false, r) if !strings.Contains(out, "msg=plain") { t.Errorf("unquoted simple value expected: %q", out) } r.Message = "has \"quotes\" and = signs" out = render(t, "logfmt", false, r) if !strings.Contains(out, `msg="has \"quotes\" and = signs"`) { t.Errorf("quoted value wrong: %q", out) } } func TestUnknownFormat(t *testing.T) { if _, err := NewFormatter("yaml", false); err == nil { t.Fatal("expected error for unknown format") } } func TestRowKeyStableAndDistinct(t *testing.T) { a := sampleRow() b := sampleRow() if a.Key() != b.Key() { t.Error("identical rows should share a key") } b.Message = "different" if a.Key() == b.Key() { t.Error("different rows should not share a key") } } func TestRowTimeParses(t *testing.T) { r := sampleRow() want := time.Date(2026, 8, 23, 5, 0, 1, 234000000, time.UTC) if !r.Time().Equal(want) { t.Errorf("Time() = %s, want %s", r.Time(), want) } r.Timestamp = "2026-08-23 05:00:01" if r.Time().IsZero() { t.Error("timestamp without fraction should still parse") } }