Files
Sidekick/internal_tools_test.go
T

224 lines
6.2 KiB
Go
Raw Normal View History

2026-03-21 16:20:16 +02:00
package sidekick
import (
"os"
"strings"
"testing"
)
2026-03-21 17:11:17 +02:00
func TestShellExecHandler(t *testing.T) {
// Success
got, err := shellExecHandler(map[string]interface{}{"command": "echo hello"})
if err != nil { t.Fatal(err) }
if strings.TrimSpace(got) != "hello" { t.Errorf("expected hello, got %s", got) }
// Failure
got, err = shellExecHandler(map[string]interface{}{"command": "ls /nonexistent-file-path-that-should-not-exist"})
if err != nil { t.Fatal(err) }
if !strings.Contains(got, "Error:") { t.Errorf("expected error in output, got %s", got) }
// Empty
got, err = shellExecHandler(map[string]interface{}{"command": "true"})
if err != nil { t.Fatal(err) }
if got != "(empty output)" { t.Errorf("expected empty output, got %s", got) }
}
2026-03-21 16:20:16 +02:00
func TestHashLine(t *testing.T) {
line := "hello world"
h1 := hashLine(line)
h2 := hashLine(line)
if h1 != h2 {
t.Errorf("hashLine is not deterministic: %s != %s", h1, h2)
}
if len(h1) != 4 {
t.Errorf("hashLine output length is not 4: %d", len(h1))
}
h3 := hashLine("hello worle")
if h1 == h3 {
t.Errorf("hashLine collision for similar strings: %s == %s", h1, h3)
}
}
func TestStripHashlines(t *testing.T) {
input := "1:abcd:line 1\n2:ef01:line 2\nnot a hashline\n3:ghij:line 3"
// ghij is not valid hex, so it should NOT be stripped if we follow the code strictly
// Wait, my code checks for hex: (c >= 'a' && c <= 'f')
// So 'ghij' should not be stripped.
expected := "line 1\nline 2\nnot a hashline\n3:ghij:line 3"
got := stripHashlines(input)
if got != expected {
t.Errorf("stripHashlines failed.\nGot:\n%s\nExpected:\n%s", got, expected)
}
input2 := "10:1234:content with : colons"
expected2 := "content with : colons"
got2 := stripHashlines(input2)
if got2 != expected2 {
t.Errorf("stripHashlines failed for content with colons.\nGot: %s\nExpected: %s", got2, expected2)
}
}
func TestEditFileHandler_ExactHashlines(t *testing.T) {
tmpFile := "test_edit_exact.txt"
initial := "line one\nline two\nline three\nline four"
os.WriteFile(tmpFile, []byte(initial), 0644)
defer os.Remove(tmpFile)
h2 := hashLine("line two")
h3 := hashLine("line three")
args := map[string]interface{}{
"path": tmpFile,
"old_string": "2:" + h2 + ":line two\n3:" + h3 + ":line three",
"new_string": "replaced two\nreplaced three",
}
_, err := editFileHandler(args)
if err != nil {
t.Fatal(err)
}
got, _ := os.ReadFile(tmpFile)
expected := "line one\nreplaced two\nreplaced three\nline four"
if string(got) != expected {
t.Errorf("editFileHandler did not precisely replace using hashlines.\nGot: %s\nExpected: %s", string(got), expected)
}
}
func TestGrepFileHandler_Hashlines(t *testing.T) {
tmpFile := "test_read_hash.txt"
content := "line one\nline two"
os.WriteFile(tmpFile, []byte(content), 0644)
defer os.Remove(tmpFile)
args := map[string]interface{}{"path": tmpFile}
got, err := readFileHandler(args)
if err != nil {
t.Fatal(err)
}
lines := strings.Split(got, "\n")
if len(lines) != 2 {
t.Fatalf("expected 2 lines, got %d", len(lines))
}
if !strings.HasPrefix(lines[0], "1:") || !strings.HasSuffix(lines[0], ":line one") {
t.Errorf("line 0 format wrong: %s", lines[0])
}
if !strings.HasPrefix(lines[1], "2:") || !strings.HasSuffix(lines[1], ":line two") {
t.Errorf("line 1 format wrong: %s", lines[1])
}
}
func TestWriteFileHandler_Strip(t *testing.T) {
tmpFile := "test_write_strip.txt"
defer os.Remove(tmpFile)
content := "1:abcd:clean line\n2:1234:another line"
args := map[string]interface{}{
"path": tmpFile,
"content": content,
}
_, err := writeFileHandler(args)
if err != nil {
t.Fatal(err)
}
got, _ := os.ReadFile(tmpFile)
expected := "clean line\nanother line"
if string(got) != expected {
t.Errorf("writeFileHandler did not strip hashlines.\nGot: %s\nExpected: %s", string(got), expected)
}
}
func TestEditFileHandler_Strip(t *testing.T) {
tmpFile := "test_edit_strip.txt"
initial := "find me\nkeep me"
os.WriteFile(tmpFile, []byte(initial), 0644)
defer os.Remove(tmpFile)
args := map[string]interface{}{
"path": tmpFile,
"old_string": "1:abcd:find me",
"new_string": "1:beef:replaced",
}
_, err := editFileHandler(args)
if err != nil {
t.Fatal(err)
}
got, _ := os.ReadFile(tmpFile)
expected := "replaced\nkeep me"
if string(got) != expected {
t.Errorf("editFileHandler did not strip hashlines.\nGot: %s\nExpected: %s", string(got), expected)
}
}
func TestGrepFileHandler_HashlineAnchors(t *testing.T) {
tmpFile := "test_grep_anchors.txt"
content := "first line\nsecond line\nthird needle\nfourth line\nfifth needle"
os.WriteFile(tmpFile, []byte(content), 0644)
defer os.Remove(tmpFile)
args := map[string]interface{}{
"path": tmpFile,
"pattern": "needle",
"context_lines": 1.0,
}
got, err := grepFileHandler(args)
if err != nil {
t.Fatal(err)
}
// Lines in file:
// 1: first line
// 2: second line
// 3: third needle (MATCH)
// 4: fourth line
// 5: fifth needle (MATCH)
// Expected output structure should contain:
// --- Match around line 3 ---
// 2:<hash>:second line
// 3:<hash>:third needle
// 4:<hash>:fourth line
// --- Match around line 5 ---
// 4:<hash>:fourth line
// 5:<hash>:fifth needle
lines := strings.Split(got, "\n")
findLine := func(prefix string) bool {
for _, l := range lines {
if strings.HasPrefix(l, prefix) {
return true
}
}
return false
}
// Check anchors
if !findLine("--- Match around line 3 ---") {
t.Errorf("missing match anchor for line 3")
}
if !findLine("--- Match around line 5 ---") {
t.Errorf("missing match anchor for line 5")
}
// Check specific hashline prefixes
h2 := hashLine("second line")
h3 := hashLine("third needle")
h5 := hashLine("fifth needle")
if !findLine("2:" + h2 + ":second line") {
t.Errorf("missing correctly hashed context line 2")
}
if !findLine("3:" + h3 + ":third needle") {
t.Errorf("missing correctly hashed match line 3")
}
if !findLine("5:" + h5 + ":fifth needle") {
t.Errorf("missing correctly hashed match line 5")
}
}