summaryrefslogtreecommitdiff
path: root/internal/tmux
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-08 22:18:05 +0200
committerPaul Buetow <paul@buetow.org>2026-02-08 22:18:05 +0200
commit9952f53408c8c688f97afbde93cfd9d77fbe8974 (patch)
tree61631688ba195afcaeb6239cca3e0993182ffdff /internal/tmux
parent03cbe41dfa124a78116609cdfa49014ad4d09e8c (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.go201
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