Something went wrong. Try again.
dockscope is a TUI for viewing Docker containers, logs, metrics and other info, inspired by lazydocker and dtop.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183package parser_test
import ( "testing" "time"
"tangled.org/samatkins.net/dockscope/internal/parser")
type funcParser func(raw string) (parser.LogEntry, bool)
func (f funcParser) Parse(raw string) (parser.LogEntry, bool) { return f(raw) }
func TestChain(t *testing.T) { t.Parallel()
t.Run("GIVEN a chain of parsers WHEN the first parser handles the input THEN its result is returned", func(t *testing.T) { t.Parallel()
fakeTime := time.Date(2026, 5, 9, 14, 32, 1, 0, time.UTC)
chain := parser.Chain{ funcParser(func(raw string) (parser.LogEntry, bool) { if raw == "match-first" { return parser.LogEntry{Raw: raw, TraceID: "first-trace", Timestamp: fakeTime}, true } return parser.LogEntry{}, false }), funcParser(func(raw string) (parser.LogEntry, bool) { return parser.LogEntry{Raw: raw, TraceID: "second-trace", Timestamp: fakeTime}, true }), }
entry, ok := chain.Parse("match-first")
if !ok { t.Fatal("ok = false, want true") } if got, want := entry.TraceID, "first-trace"; got != want { t.Errorf("TraceID = %q, want %q", got, want) } })
t.Run("GIVEN a chain WHERE no parser handles the input WHEN chain parses THEN a raw fallback entry is returned", func(t *testing.T) { t.Parallel()
chain := parser.Chain{ funcParser(func(raw string) (parser.LogEntry, bool) { return parser.LogEntry{}, false }), funcParser(func(raw string) (parser.LogEntry, bool) { return parser.LogEntry{}, false }), }
entry, ok := chain.Parse("some raw text")
if !ok { t.Fatal("ok = false, want true (Chain always returns true via fallback)") } if got, want := entry.Raw, "some raw text"; got != want { t.Errorf("Raw = %q, want %q", got, want) } if got, want := entry.Message, "some raw text"; got != want { t.Errorf("Message = %q, want %q", got, want) } if entry.TraceID != "" { t.Errorf("TraceID = %q, want empty", entry.TraceID) } if entry.Timestamp.IsZero() { t.Error("Timestamp is zero, want non-zero") } })
t.Run("GIVEN a chain of parsers WHEN second parser handles the input THEN its result is returned", func(t *testing.T) { t.Parallel()
chain := parser.Chain{ funcParser(func(raw string) (parser.LogEntry, bool) { return parser.LogEntry{}, false }), funcParser(func(raw string) (parser.LogEntry, bool) { if raw == "match-second" { return parser.LogEntry{Raw: raw, TraceID: "second-trace"}, true } return parser.LogEntry{}, false }), }
entry, ok := chain.Parse("match-second")
if !ok { t.Fatal("ok = false, want true") } if got, want := entry.TraceID, "second-trace"; got != want { t.Errorf("TraceID = %q, want %q", got, want) } })
t.Run("GIVEN a parser that signals success without setting Raw WHEN chain parses THEN that result is preserved", func(t *testing.T) { t.Parallel()
chain := parser.Chain{ funcParser(func(raw string) (parser.LogEntry, bool) { return parser.LogEntry{TraceID: "no-raw-trace", Message: "structured"}, true }), }
entry, ok := chain.Parse("input")
if !ok { t.Fatal("ok = false, want true") } if got, want := entry.TraceID, "no-raw-trace"; got != want { t.Errorf("TraceID = %q, want %q (chain must not require Raw to be set)", got, want) } if entry.Raw != "" { t.Errorf("Raw = %q, want empty (chain should not synthesize Raw when parser succeeded)", entry.Raw) } })
t.Run("GIVEN an empty chain WHEN chain parses THEN a raw fallback entry is returned", func(t *testing.T) { t.Parallel()
chain := parser.Chain{}
entry, ok := chain.Parse("bare text")
if !ok { t.Fatal("ok = false, want true") } if got, want := entry.Raw, "bare text"; got != want { t.Errorf("Raw = %q, want %q", got, want) } if got, want := entry.Message, "bare text"; got != want { t.Errorf("Message = %q, want %q", got, want) } })}
func TestNoopParser(t *testing.T) { t.Parallel()
t.Run("GIVEN any input WHEN NoopParser.Parse THEN Raw and Message are set to input", func(t *testing.T) { t.Parallel()
p := parser.NoopParser{} entry, ok := p.Parse("hello world")
if !ok { t.Fatal("ok = false, want true") } if got, want := entry.Raw, "hello world"; got != want { t.Errorf("Raw = %q, want %q", got, want) } if got, want := entry.Message, "hello world"; got != want { t.Errorf("Message = %q, want %q", got, want) } })
t.Run("GIVEN any input WHEN NoopParser.Parse THEN Timestamp is non-zero", func(t *testing.T) { t.Parallel()
p := parser.NoopParser{} entry, _ := p.Parse("hello")
if entry.Timestamp.IsZero() { t.Error("Timestamp is zero, want non-zero") } })
t.Run("GIVEN any input WHEN NoopParser.Parse THEN TraceID is empty", func(t *testing.T) { t.Parallel()
p := parser.NoopParser{} entry, _ := p.Parse("trace_id=abc123")
if entry.TraceID != "" { t.Errorf("TraceID = %q, want empty", entry.TraceID) } })}