From 9321fd3490b1739e4c84fd229a2cfef244ffdc29 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Fri, 24 Jul 2026 09:30:26 +0200 Subject: cli: pager for reading output (-P/--pager, --no-pager, config pager, $PAGER fallback); v0.3.0 --- internal/cli/pager_test.go | 109 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 internal/cli/pager_test.go (limited to 'internal/cli/pager_test.go') diff --git a/internal/cli/pager_test.go b/internal/cli/pager_test.go new file mode 100644 index 0000000..3882162 --- /dev/null +++ b/internal/cli/pager_test.go @@ -0,0 +1,109 @@ +package cli + +import ( + "bytes" + "testing" + + "github.com/lukaszkasprzak/lectio/internal/config" +) + +// TestPagerRequested exercises pagerRequested's precedence: --no-pager beats +// -P beats a non-empty cfg.Pager; with nothing set, paging is off. +func TestPagerRequested(t *testing.T) { + cases := []struct { + name string + pagerFlag, noPager bool + cfgPager string + want bool + }{ + {"nothing set", false, false, "", false}, + {"cfg.Pager set alone", false, false, "less -R", true}, + {"-P alone", true, false, "", true}, + {"-P overrides empty cfg", true, false, "", true}, + {"--no-pager beats -P", true, true, "", false}, + {"--no-pager beats cfg.Pager", false, true, "less -R", false}, + {"--no-pager beats both", true, true, "less -R", false}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + cfg := config.Config{Pager: c.cfgPager} + got := pagerRequested(c.pagerFlag, c.noPager, cfg) + if got != c.want { + t.Errorf("pagerRequested(%v, %v, Pager=%q) = %v, want %v", + c.pagerFlag, c.noPager, c.cfgPager, got, c.want) + } + }) + } +} + +// TestPagerCommand exercises pagerCommand's fallback chain: cfg.Pager wins, +// then $PAGER, then the "less -R" built-in default. No exec happens here. +func TestPagerCommand(t *testing.T) { + t.Run("cfg.Pager set", func(t *testing.T) { + t.Setenv("PAGER", "most") + cfg := config.Config{Pager: "more -c"} + got := pagerCommand(cfg) + want := []string{"more", "-c"} + if !equalStrings(got, want) { + t.Errorf("pagerCommand = %v, want %v", got, want) + } + }) + + t.Run("cfg empty, PAGER set", func(t *testing.T) { + t.Setenv("PAGER", "most") + cfg := config.Config{} + got := pagerCommand(cfg) + want := []string{"most"} + if !equalStrings(got, want) { + t.Errorf("pagerCommand = %v, want %v", got, want) + } + }) + + t.Run("both empty falls back to less -R", func(t *testing.T) { + t.Setenv("PAGER", "") + cfg := config.Config{} + got := pagerCommand(cfg) + want := []string{"less", "-R"} + if !equalStrings(got, want) { + t.Errorf("pagerCommand = %v, want %v", got, want) + } + }) +} + +func equalStrings(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true +} + +// TestIsTerminalWriterBuffer confirms a bytes.Buffer (what every test uses +// for stdout) is never treated as a terminal, so Run never pages in tests. +func TestIsTerminalWriterBuffer(t *testing.T) { + if isTerminalWriter(&bytes.Buffer{}) { + t.Error("isTerminalWriter(&bytes.Buffer{}) = true, want false") + } +} + +// TestPagerFlagDoesNotBreakBufferOutput confirms -P set against a +// bytes.Buffer stdout (non-TTY) still writes plainly -- pagerRequested may +// be true, but isTerminalWriter gates it off, so Run's existing dispatch +// runs unchanged. +func TestPagerFlagDoesNotBreakBufferOutput(t *testing.T) { + t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + var out, errb bytes.Buffer + code := Run([]string{"-P", "-o", "-l", "trad"}, nil, &out, &errb) + if code != 0 && code != 1 { + t.Fatalf("-P run code=%d (stderr=%q)", code, errb.String()) + } + // The key assertion: no hang, no panic, output goes to the buffer + // directly (a real pager exec would have blocked/failed differently). + if out.Len() == 0 && code == 0 { + t.Errorf("-P run produced no stdout output on success") + } +} -- cgit v1.3