diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-22 20:21:34 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-22 20:21:34 +0200 |
| commit | 3373b7e90c4e1ff1abcebe0594316131f546dfa8 (patch) | |
| tree | 8acc26309fce0048f156e5cec2acca9a5d891628 /internal/shell/shell_internal_test.go | |
| parent | 528a9c0888f1db2449d30bf9c0c8b55fcd3c645c (diff) | |
Add unit tests to reach 63.3% coverage (task 335)
- internal/shell/shell_internal_test.go (new): tests prefixCompleter.Do
with table-driven cases covering empty prefix, partial match, no match,
and multi-word line (prefix after space)
- internal/cli/cli_test.go (new): tests pure helpers (logMsg, warn,
printHelp, shredFile), dispatch paths with real store (ls, search, cat,
rm, shred, get, paste, export, pathexport, open, edit, unknown command,
empty argv), error paths for cmdAdd/cmdImport/cmdImportR, readPIN env-var
path, completionFn commands-only path, makeActionFn nil cases
- internal/store/store_test.go: added TestSearchActionPathExport,
TestSearchActionWithCallback, TestSearchActionNilCallback, TestFzfEmpty,
TestRemoveInteractiveInvalidThenDecline, TestImportForceOverwrite
Total coverage: 44.8% → 63.3%
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/shell/shell_internal_test.go')
| -rw-r--r-- | internal/shell/shell_internal_test.go | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/internal/shell/shell_internal_test.go b/internal/shell/shell_internal_test.go new file mode 100644 index 0000000..73284d7 --- /dev/null +++ b/internal/shell/shell_internal_test.go @@ -0,0 +1,106 @@ +// package shell (internal test) exercises unexported types that cannot be +// reached from the external shell_test package. +package shell + +import ( + "strings" + "testing" +) + +// TestPrefixCompleterDo exercises the Do method of prefixCompleter against a +// fixed candidate list. No TTY is required because the method is pure logic. +func TestPrefixCompleterDo(t *testing.T) { + candidates := []string{"add", "edit", "export", "ls", "search"} + + p := &prefixCompleter{ + fn: func(prefix string) []string { + var out []string + for _, c := range candidates { + if strings.HasPrefix(c, prefix) { + out = append(out, c) + } + } + return out + }, + } + + cases := []struct { + name string + line string // full line up to cursor + wantSuffix []string + wantLen int + }{ + { + // Empty input: all candidates returned; each suffix is the full word + space. + name: "empty prefix", + line: "", + wantSuffix: []string{"add ", "edit ", "export ", "ls ", "search "}, + wantLen: 0, + }, + { + // "e" prefix: edit, export. + name: "single char prefix", + line: "e", + wantSuffix: []string{"dit ", "xport "}, + wantLen: 1, + }, + { + // "ex" prefix: only export. + name: "two char prefix", + line: "ex", + wantSuffix: []string{"port "}, + wantLen: 2, + }, + { + // "z" prefix: no matches. + name: "no match", + line: "z", + wantSuffix: nil, + wantLen: 1, + }, + { + // Line has a space: prefix is the word after the last space. + // "cat e" → prefix is "e", completions for "e". + name: "prefix after space", + line: "cat e", + wantSuffix: []string{"dit ", "xport "}, + wantLen: 1, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + line := []rune(tc.line) + pos := len(line) + newLine, length := p.Do(line, pos) + + if length != tc.wantLen { + t.Errorf("length = %d; want %d", length, tc.wantLen) + } + if len(newLine) != len(tc.wantSuffix) { + t.Errorf("len(newLine) = %d; want %d (%v vs %v)", + len(newLine), len(tc.wantSuffix), toStrings(newLine), tc.wantSuffix) + return + } + // Build a set for order-independent comparison. + got := make(map[string]bool, len(newLine)) + for _, r := range newLine { + got[string(r)] = true + } + for _, want := range tc.wantSuffix { + if !got[want] { + t.Errorf("missing suffix %q in completions %v", want, toStrings(newLine)) + } + } + }) + } +} + +// toStrings converts a [][]rune to []string for readable error output. +func toStrings(runes [][]rune) []string { + out := make([]string, len(runes)) + for i, r := range runes { + out[i] = string(r) + } + return out +} |
