diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-08 22:18:05 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-08 22:18:05 +0200 |
| commit | 9952f53408c8c688f97afbde93cfd9d77fbe8974 (patch) | |
| tree | 61631688ba195afcaeb6239cca3e0993182ffdff /internal/tmux | |
| parent | 03cbe41dfa124a78116609cdfa49014ad4d09e8c (diff) | |
Add unit tests to improve coverage above 80% target
Implement comprehensive unit tests for critical internal packages
to increase overall test coverage from 80.9% to 81.8%, providing
a buffer above the 80% threshold specified in project guidelines.
Changes:
- Add config parsing tests (parseTemperatureValue, decodeModelEntry, resolvedModel, parseSurfaceEntries)
- Add HumanBytes utility tests with edge cases and boundary values
- Fix HumanBytes bug: corrected suffix array indexing (off-by-one error)
- Fix HumanBytes to handle negative values correctly
- Add comprehensive shellJoin and isSafeBare tests for shell escaping
- Update comments to reflect actual behavior and implementation details
Coverage impact:
- internal/appconfig: Improved config parsing function coverage
- internal/textutil: HumanBytes now at 100% coverage (fixed bug in process)
- internal/tmux: shellJoin and isSafeBare now at 100% coverage
- Overall project: 80.9% → 81.8% (+0.9%)
All tests pass. Code formatted with gofumpt.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/tmux')
| -rw-r--r-- | internal/tmux/tmux_test.go | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/internal/tmux/tmux_test.go b/internal/tmux/tmux_test.go index 4db2e4a..a8d1574 100644 --- a/internal/tmux/tmux_test.go +++ b/internal/tmux/tmux_test.go @@ -31,6 +31,207 @@ func TestHasBinary_UsesLookPath(t *testing.T) { } } +// --- Phase 3: Shell Utility Tests --- + +func TestShellJoin(t *testing.T) { + tests := []struct { + name string + argv []string + expected string + }{ + { + name: "simple alphanumeric", + argv: []string{"ls", "-la"}, + expected: "ls -la", + }, + { + name: "with spaces", + argv: []string{"echo", "hello world"}, + expected: "echo 'hello world'", + }, + { + name: "with single quotes", + argv: []string{"echo", "it's fine"}, + expected: "echo 'it'\\''s fine'", + }, + { + name: "with double quotes", + argv: []string{"echo", `say "hello"`}, + expected: `echo 'say "hello"'`, + }, + { + name: "with dollar signs", + argv: []string{"echo", "$PATH"}, + expected: "echo '$PATH'", + }, + { + name: "with backticks", + argv: []string{"echo", "`whoami`"}, + expected: "echo '`whoami`'", + }, + { + name: "with backslashes", + argv: []string{"echo", `path\to\file`}, + expected: `echo 'path\to\file'`, + }, + { + name: "empty string", + argv: []string{"echo", ""}, + expected: "echo ''", + }, + { + name: "only empty strings", + argv: []string{"", "", ""}, + expected: "'' '' ''", + }, + { + name: "with newlines", + argv: []string{"echo", "line1\nline2"}, + expected: "echo 'line1\nline2'", + }, + { + name: "with tabs", + argv: []string{"echo", "col1\tcol2"}, + expected: "echo 'col1\tcol2'", + }, + { + name: "with semicolons", + argv: []string{"echo", "a;b"}, + expected: "echo 'a;b'", + }, + { + name: "with pipes", + argv: []string{"echo", "a|b"}, + expected: "echo 'a|b'", + }, + { + name: "with ampersands", + argv: []string{"echo", "a&b"}, + expected: "echo 'a&b'", + }, + { + name: "safe bare characters", + argv: []string{"cat", "/path/to/file-name_123.txt"}, + expected: "cat /path/to/file-name_123.txt", + }, + { + name: "with colons and @", + argv: []string{"ssh", "user@host:22"}, + expected: "ssh 'user@host:22'", // @ is not safe, so gets quoted + }, + { + name: "unicode characters", + argv: []string{"echo", "hello 世界"}, + expected: "echo 'hello 世界'", + }, + { + name: "mixed safe and unsafe", + argv: []string{"git", "commit", "-m", "fix: resolve issue #123"}, + expected: "git commit -m 'fix: resolve issue #123'", + }, + { + name: "multiple single quotes", + argv: []string{"echo", "can't won't shouldn't"}, + expected: "echo 'can'\\''t won'\\''t shouldn'\\''t'", + }, + { + name: "only spaces", + argv: []string{"echo", " "}, + expected: "echo ' '", + }, + { + name: "parentheses", + argv: []string{"echo", "(hello)"}, + expected: "echo '(hello)'", + }, + { + name: "brackets", + argv: []string{"echo", "[hello]"}, + expected: "echo '[hello]'", + }, + { + name: "braces", + argv: []string{"echo", "{hello}"}, + expected: "echo '{hello}'", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := shellJoin(tt.argv) + if got != tt.expected { + t.Errorf("shellJoin(%q) = %q, want %q", tt.argv, got, tt.expected) + } + }) + } +} + +func TestIsSafeBare(t *testing.T) { + tests := []struct { + name string + input string + expected bool + }{ + // Safe cases - only alphanumeric, dash, underscore, dot, slash, colon + {"simple word", "hello", true}, + {"with numbers", "file123", true}, + {"with dash", "my-file", true}, + {"with underscore", "my_file", true}, + {"with dot", "file.txt", true}, + {"with slash", "/path/to/file", true}, + {"with colon only", "path:to:file", true}, + {"uppercase", "README", true}, + {"mixed alphanumeric", "AaBb123", true}, + {"complex safe", "path/to/file-name_v1.2.txt", true}, + {"just numbers", "12345", true}, + {"just dashes", "---", true}, + {"just underscores", "___", true}, + {"just dots", "...", true}, + {"just slashes", "///", true}, + {"just colons", ":::", true}, + + // Unsafe cases - contain special characters + {"with space", "hello world", false}, + {"with single quote", "it's", false}, + {"with double quote", `say "hi"`, false}, + {"with dollar", "$VAR", false}, + {"with backtick", "`cmd`", false}, + {"with backslash", `path\file`, false}, + {"with newline", "line1\nline2", false}, + {"with tab", "col1\tcol2", false}, + {"with semicolon", "cmd;cmd", false}, + {"with pipe", "a|b", false}, + {"with ampersand", "a&b", false}, + {"with asterisk", "*.txt", false}, + {"with question mark", "file?.txt", false}, + {"with exclamation", "hello!", false}, + {"with at sign", "user@host", false}, + {"with hash", "tag#123", false}, + {"with percent", "50%", false}, + {"with caret", "a^b", false}, + {"with tilde", "~/path", false}, + {"with equals", "key=value", false}, + {"with plus", "a+b", false}, + {"with parenthesis", "(test)", false}, + {"with bracket", "[test]", false}, + {"with brace", "{test}", false}, + {"with less than", "a<b", false}, + {"with greater than", "a>b", false}, + {"with comma", "a,b", false}, + {"empty string", "", true}, // edge case: no unsafe chars + {"unicode", "hello世界", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := isSafeBare(tt.input) + if got != tt.expected { + t.Errorf("isSafeBare(%q) = %v, want %v", tt.input, got, tt.expected) + } + }) + } +} + func TestSplitRun_AssemblesArgs(t *testing.T) { captured := struct { name string |
