mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-06-12 00:19:20 +00:00
Increase test coverage to 45.6%
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
package sdk
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/lukaszraczylo/claude-mnemonic/pkg/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsSelfReferentialSummary(t *testing.T) {
|
||||
@@ -124,3 +127,381 @@ No substantive work performed yet.`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldSkipTool(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
toolName string
|
||||
expected bool
|
||||
}{
|
||||
// Tools that should be skipped
|
||||
{"TodoWrite", "TodoWrite", true},
|
||||
{"Task", "Task", true},
|
||||
{"TaskOutput", "TaskOutput", true},
|
||||
{"Glob", "Glob", true},
|
||||
{"ListDir", "ListDir", true},
|
||||
{"LS", "LS", true},
|
||||
{"KillShell", "KillShell", true},
|
||||
{"AskUserQuestion", "AskUserQuestion", true},
|
||||
{"EnterPlanMode", "EnterPlanMode", true},
|
||||
{"ExitPlanMode", "ExitPlanMode", true},
|
||||
{"Skill", "Skill", true},
|
||||
{"SlashCommand", "SlashCommand", true},
|
||||
|
||||
// Tools that should NOT be skipped
|
||||
{"Read", "Read", false},
|
||||
{"Edit", "Edit", false},
|
||||
{"Write", "Write", false},
|
||||
{"Grep", "Grep", false},
|
||||
{"Bash", "Bash", false},
|
||||
{"WebFetch", "WebFetch", false},
|
||||
{"WebSearch", "WebSearch", false},
|
||||
{"NotebookEdit", "NotebookEdit", false},
|
||||
|
||||
// Unknown tool (should not be skipped)
|
||||
{"UnknownTool", "SomeUnknownTool", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := shouldSkipTool(tt.toolName)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldSkipTrivialOperation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
toolName string
|
||||
inputStr string
|
||||
outputStr string
|
||||
expected bool
|
||||
}{
|
||||
// Short output (should be skipped)
|
||||
{
|
||||
name: "output_too_short",
|
||||
toolName: "Read",
|
||||
inputStr: `{"file_path": "/some/file.go"}`,
|
||||
outputStr: "short",
|
||||
expected: true,
|
||||
},
|
||||
// Trivial outputs
|
||||
{
|
||||
name: "no_matches_found",
|
||||
toolName: "Grep",
|
||||
inputStr: `{"pattern": "foo"}`,
|
||||
outputStr: "No matches found in the codebase",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "file_not_found",
|
||||
toolName: "Read",
|
||||
inputStr: `{"file_path": "/nonexistent.go"}`,
|
||||
outputStr: "Error: File not found at specified path",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "empty_array",
|
||||
toolName: "Grep",
|
||||
inputStr: `{"pattern": "foo"}`,
|
||||
outputStr: "[]",
|
||||
expected: true,
|
||||
},
|
||||
// Boring files
|
||||
{
|
||||
name: "package_lock_json",
|
||||
toolName: "Read",
|
||||
inputStr: `{"file_path": "/project/package-lock.json"}`,
|
||||
outputStr: "This is a very long package-lock.json content that has more than 50 characters",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "go_sum",
|
||||
toolName: "Read",
|
||||
inputStr: `{"file_path": "/project/go.sum"}`,
|
||||
outputStr: "This is a very long go.sum file content that has more than 50 characters",
|
||||
expected: true,
|
||||
},
|
||||
// Grep with too many matches
|
||||
{
|
||||
name: "grep_too_many_matches",
|
||||
toolName: "Grep",
|
||||
inputStr: `{"pattern": "import"}`,
|
||||
outputStr: func() string {
|
||||
s := ""
|
||||
for i := 0; i < 55; i++ {
|
||||
s += "match line\n"
|
||||
}
|
||||
return s
|
||||
}(),
|
||||
expected: true,
|
||||
},
|
||||
// Boring Bash commands
|
||||
{
|
||||
name: "git_status",
|
||||
toolName: "Bash",
|
||||
inputStr: `{"command": "git status"}`,
|
||||
outputStr: "On branch main\nYour branch is up to date with 'origin/main'.\nnothing to commit, working tree clean",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "ls_command",
|
||||
toolName: "Bash",
|
||||
inputStr: `{"command": "ls -la /some/directory"}`,
|
||||
outputStr: "total 123\ndrwxr-xr-x some long listing that is at least 50 chars",
|
||||
expected: true,
|
||||
},
|
||||
// Valid operations that should NOT be skipped
|
||||
{
|
||||
name: "valid_read",
|
||||
toolName: "Read",
|
||||
inputStr: `{"file_path": "/project/main.go"}`,
|
||||
outputStr: "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Hello World\")\n}",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "valid_edit",
|
||||
toolName: "Edit",
|
||||
inputStr: `{"file_path": "/project/handler.go", "old_string": "foo", "new_string": "bar"}`,
|
||||
outputStr: "Edit applied successfully. File /project/handler.go has been modified with the requested changes.",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := shouldSkipTrivialOperation(tt.toolName, tt.inputStr, tt.outputStr)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncateForLog(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
maxLen int
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "shorter_than_max",
|
||||
input: "hello",
|
||||
maxLen: 10,
|
||||
expected: "hello",
|
||||
},
|
||||
{
|
||||
name: "equal_to_max",
|
||||
input: "hello",
|
||||
maxLen: 5,
|
||||
expected: "hello",
|
||||
},
|
||||
{
|
||||
name: "longer_than_max",
|
||||
input: "hello world",
|
||||
maxLen: 5,
|
||||
expected: "hello...",
|
||||
},
|
||||
{
|
||||
name: "empty_string",
|
||||
input: "",
|
||||
maxLen: 5,
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := truncateForLog(tt.input, tt.maxLen)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToJSONString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input interface{}
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "nil_value",
|
||||
input: nil,
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "string_value",
|
||||
input: "hello",
|
||||
expected: "hello",
|
||||
},
|
||||
{
|
||||
name: "int_value",
|
||||
input: 42,
|
||||
expected: "42",
|
||||
},
|
||||
{
|
||||
name: "map_value",
|
||||
input: map[string]string{"key": "value"},
|
||||
expected: `{"key":"value"}`,
|
||||
},
|
||||
{
|
||||
name: "slice_value",
|
||||
input: []string{"a", "b", "c"},
|
||||
expected: `["a","b","c"]`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := toJSONString(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCaptureFileMtimes(t *testing.T) {
|
||||
// Create a temp directory with test files
|
||||
tmpDir, err := os.MkdirTemp("", "mtime-test-*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Create test files
|
||||
file1 := filepath.Join(tmpDir, "file1.txt")
|
||||
file2 := filepath.Join(tmpDir, "file2.txt")
|
||||
|
||||
err = os.WriteFile(file1, []byte("content1"), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.WriteFile(file2, []byte("content2"), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run("captures_mtimes_for_existing_files", func(t *testing.T) {
|
||||
mtimes := captureFileMtimes([]string{file1}, []string{file2}, "")
|
||||
assert.Len(t, mtimes, 2)
|
||||
assert.Contains(t, mtimes, file1)
|
||||
assert.Contains(t, mtimes, file2)
|
||||
assert.Greater(t, mtimes[file1], int64(0))
|
||||
assert.Greater(t, mtimes[file2], int64(0))
|
||||
})
|
||||
|
||||
t.Run("handles_nonexistent_files", func(t *testing.T) {
|
||||
mtimes := captureFileMtimes([]string{"/nonexistent/file.txt"}, nil, "")
|
||||
assert.Empty(t, mtimes)
|
||||
})
|
||||
|
||||
t.Run("handles_relative_paths_with_cwd", func(t *testing.T) {
|
||||
mtimes := captureFileMtimes([]string{"file1.txt"}, nil, tmpDir)
|
||||
assert.Len(t, mtimes, 1)
|
||||
assert.Contains(t, mtimes, "file1.txt")
|
||||
})
|
||||
|
||||
t.Run("empty_inputs", func(t *testing.T) {
|
||||
mtimes := captureFileMtimes(nil, nil, "")
|
||||
assert.Empty(t, mtimes)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetFileMtimes(t *testing.T) {
|
||||
// Create a temp file
|
||||
tmpDir, err := os.MkdirTemp("", "getmtime-test-*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
testFile := filepath.Join(tmpDir, "test.txt")
|
||||
err = os.WriteFile(testFile, []byte("content"), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mtimes := GetFileMtimes([]string{testFile}, "")
|
||||
assert.Len(t, mtimes, 1)
|
||||
assert.Contains(t, mtimes, testFile)
|
||||
assert.Greater(t, mtimes[testFile], int64(0))
|
||||
}
|
||||
|
||||
func TestGetFileContent(t *testing.T) {
|
||||
// Create a temp directory with test files
|
||||
tmpDir, err := os.MkdirTemp("", "content-test-*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
t.Run("reads_existing_file", func(t *testing.T) {
|
||||
testFile := filepath.Join(tmpDir, "test.txt")
|
||||
content := "test content"
|
||||
err := os.WriteFile(testFile, []byte(content), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
result, ok := GetFileContent(testFile, "")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, content, result)
|
||||
})
|
||||
|
||||
t.Run("returns_false_for_nonexistent_file", func(t *testing.T) {
|
||||
result, ok := GetFileContent("/nonexistent/file.txt", "")
|
||||
assert.False(t, ok)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
|
||||
t.Run("truncates_long_content", func(t *testing.T) {
|
||||
testFile := filepath.Join(tmpDir, "long.txt")
|
||||
longContent := ""
|
||||
for i := 0; i < 3000; i++ {
|
||||
longContent += "x"
|
||||
}
|
||||
err := os.WriteFile(testFile, []byte(longContent), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
result, ok := GetFileContent(testFile, "")
|
||||
assert.True(t, ok)
|
||||
assert.Contains(t, result, "[truncated]")
|
||||
assert.LessOrEqual(t, len(result), 2100)
|
||||
})
|
||||
|
||||
t.Run("resolves_relative_path_with_cwd", func(t *testing.T) {
|
||||
testFile := filepath.Join(tmpDir, "relative.txt")
|
||||
content := "relative content"
|
||||
err := os.WriteFile(testFile, []byte(content), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
result, ok := GetFileContent("relative.txt", tmpDir)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, content, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMaxConcurrentCLICalls(t *testing.T) {
|
||||
assert.Equal(t, 4, MaxConcurrentCLICalls)
|
||||
}
|
||||
|
||||
func TestObservationTypes(t *testing.T) {
|
||||
expected := []string{"bugfix", "feature", "refactor", "change", "discovery", "decision"}
|
||||
assert.Equal(t, expected, ObservationTypes)
|
||||
}
|
||||
|
||||
func TestObservationConcepts(t *testing.T) {
|
||||
expectedConcepts := []string{
|
||||
"how-it-works",
|
||||
"why-it-exists",
|
||||
"what-changed",
|
||||
"problem-solution",
|
||||
"gotcha",
|
||||
"pattern",
|
||||
"trade-off",
|
||||
}
|
||||
assert.Equal(t, expectedConcepts, ObservationConcepts)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user