diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-14 09:40:12 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-14 09:40:12 +0200 |
| commit | 4cf21e265c1de0457af30aecb77ef0826468e366 (patch) | |
| tree | db5bbd9d644ddf6fbf72d76b11eebdd885fda308 | |
| parent | 4137c95d0b6882fbdc40d73b1f3528c28ceb653a (diff) | |
Release v0.22.2v0.22.2
| -rw-r--r-- | internal/termprint/columns_test.go | 180 | ||||
| -rw-r--r-- | internal/version.go | 2 |
2 files changed, 181 insertions, 1 deletions
diff --git a/internal/termprint/columns_test.go b/internal/termprint/columns_test.go new file mode 100644 index 0000000..28567d8 --- /dev/null +++ b/internal/termprint/columns_test.go @@ -0,0 +1,180 @@ +package termprint + +import ( + "bytes" + "strings" + "testing" +) + +func TestNewColumnPrinter(t *testing.T) { + t.Run("returns nil when no columns are configured", func(t *testing.T) { + if got := NewColumnPrinter(&bytes.Buffer{}, nil, nil); got != nil { + t.Fatalf("NewColumnPrinter() = %#v, want nil", got) + } + }) + + t.Run("uses longest input slice and fallback width", func(t *testing.T) { + cp := NewColumnPrinter(&bytes.Buffer{}, []string{"openai"}, []string{"gpt-5", "haiku"}) + if cp == nil { + t.Fatal("NewColumnPrinter() returned nil") + } + if cp.columns != 2 { + t.Fatalf("columns = %d, want 2", cp.columns) + } + if cp.colWidth != 48 { + t.Fatalf("colWidth = %d, want 48", cp.colWidth) + } + if got, want := cp.providers, []string{"openai", ""}; !equalStrings(got, want) { + t.Fatalf("providers = %#v, want %#v", got, want) + } + if got, want := cp.models, []string{"gpt-5", "haiku"}; !equalStrings(got, want) { + t.Fatalf("models = %#v, want %#v", got, want) + } + }) +} + +func TestDetectTerminalWidth(t *testing.T) { + if got := detectTerminalWidth(&bytes.Buffer{}); got != 0 { + t.Fatalf("detectTerminalWidth() = %d, want 0", got) + } +} + +func TestHeaderCells(t *testing.T) { + cp := &ColumnPrinter{ + columns: 4, + providers: []string{" openai ", "", "anthropic", ""}, + models: []string{" gpt-5 ", "haiku", "", ""}, + } + + got := cp.headerCells() + want := []string{"openai:gpt-5", "haiku", "anthropic", ""} + if !equalStrings(got, want) { + t.Fatalf("headerCells() = %#v, want %#v", got, want) + } +} + +func TestDividerCells(t *testing.T) { + cp := &ColumnPrinter{columns: 2, colWidth: 4} + + got := cp.dividerCells() + want := []string{"────", "────"} + if !equalStrings(got, want) { + t.Fatalf("dividerCells() = %#v, want %#v", got, want) + } +} + +func TestPrintHeader(t *testing.T) { + buf := &bytes.Buffer{} + cp := &ColumnPrinter{ + stdout: buf, + columns: 2, + colWidth: 6, + providers: []string{"openai", "anth"}, + models: []string{"gpt-5", ""}, + } + + cp.PrintHeader() + + got := buf.String() + if !strings.Contains(got, "opena…") { + t.Fatalf("PrintHeader() output = %q, want truncated provider/model header", got) + } + if !strings.Contains(got, "anth") { + t.Fatalf("PrintHeader() output = %q, want second header cell", got) + } + if !strings.Contains(got, "──────") { + t.Fatalf("PrintHeader() output = %q, want divider row", got) + } +} + +func TestWrap(t *testing.T) { + cp := &ColumnPrinter{colWidth: 4} + + t.Run("returns single segment when text fits", func(t *testing.T) { + got := cp.wrap("go") + want := []string{"go"} + if !equalStrings(got, want) { + t.Fatalf("wrap() = %#v, want %#v", got, want) + } + }) + + t.Run("wraps long text and expands tabs", func(t *testing.T) { + got := cp.wrap("ab\tcd") + want := []string{"ab ", " cd"} + if !equalStrings(got, want) { + t.Fatalf("wrap() = %#v, want %#v", got, want) + } + }) +} + +func TestWriteAndFlush(t *testing.T) { + buf := &bytes.Buffer{} + cp := &ColumnPrinter{ + stdout: buf, + columns: 2, + colWidth: 10, + partial: make([]string, 2), + } + + writer := cp.Writer(1) + if _, err := writer.Write([]byte("alpha")); err != nil { + t.Fatalf("Write() error = %v", err) + } + if got := cp.partial[1]; got != "alpha" { + t.Fatalf("partial[1] = %q, want %q", got, "alpha") + } + if got := buf.Len(); got != 0 { + t.Fatalf("buffer length = %d, want 0 before newline", got) + } + + if n, err := cp.write(1, "beta\nnext"); err != nil { + t.Fatalf("write() error = %v", err) + } else if n != len("beta\nnext") { + t.Fatalf("write() bytes = %d, want %d", n, len("beta\nnext")) + } + if got := cp.partial[1]; got != "next" { + t.Fatalf("partial[1] = %q, want %q", got, "next") + } + + cp.Flush(1) + + got := buf.String() + if !strings.Contains(got, "alphabeta") { + t.Fatalf("buffer = %q, want first emitted line", got) + } + if !strings.Contains(got, "next") { + t.Fatalf("buffer = %q, want flushed line", got) + } + if cp.partial[1] != "" { + t.Fatalf("partial[1] = %q, want empty after Flush", cp.partial[1]) + } + + if n, err := cp.write(-1, "ignored"); err != nil { + t.Fatalf("write() invalid index error = %v", err) + } else if n != len("ignored") { + t.Fatalf("write() invalid index bytes = %d, want %d", n, len("ignored")) + } +} + +func TestWriteLineToPadsAndTruncates(t *testing.T) { + buf := &bytes.Buffer{} + cp := &ColumnPrinter{columns: 1, colWidth: 4} + + cp.writeLineTo(buf, []string{"abcdef"}) + + if got, want := buf.String(), "abc…\n"; got != want { + t.Fatalf("writeLineTo() = %q, want %q", got, want) + } +} + +func equalStrings(got, want []string) bool { + if len(got) != len(want) { + return false + } + for i := range got { + if got[i] != want[i] { + return false + } + } + return true +} diff --git a/internal/version.go b/internal/version.go index 9be0423..a0e474c 100644 --- a/internal/version.go +++ b/internal/version.go @@ -1,4 +1,4 @@ // Summary: Hexai semantic version identifier used by CLI and LSP binaries. package internal -const Version = "0.22.1" +const Version = "0.22.2" |
