LEM scoring pipeline, native MLX Metal bindings, Claude SDK wrapper, RAG with Qdrant/Ollama, unified AI facade, and MCP protocol server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
214 lines
5.1 KiB
Go
214 lines
5.1 KiB
Go
package agentic
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestBuildTaskContext_Good(t *testing.T) {
|
|
// Create a temp directory with some files
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create a test file
|
|
testFile := filepath.Join(tmpDir, "main.go")
|
|
err := os.WriteFile(testFile, []byte("package main\n\nfunc main() {}\n"), 0644)
|
|
require.NoError(t, err)
|
|
|
|
task := &Task{
|
|
ID: "test-123",
|
|
Title: "Test Task",
|
|
Description: "A test task description",
|
|
Priority: PriorityMedium,
|
|
Status: StatusPending,
|
|
Files: []string{"main.go"},
|
|
CreatedAt: time.Now(),
|
|
}
|
|
|
|
ctx, err := BuildTaskContext(task, tmpDir)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, ctx)
|
|
assert.Equal(t, task, ctx.Task)
|
|
assert.Len(t, ctx.Files, 1)
|
|
assert.Equal(t, "main.go", ctx.Files[0].Path)
|
|
assert.Equal(t, "go", ctx.Files[0].Language)
|
|
}
|
|
|
|
func TestBuildTaskContext_Bad_NilTask(t *testing.T) {
|
|
ctx, err := BuildTaskContext(nil, ".")
|
|
assert.Error(t, err)
|
|
assert.Nil(t, ctx)
|
|
assert.Contains(t, err.Error(), "task is required")
|
|
}
|
|
|
|
func TestGatherRelatedFiles_Good(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create test files
|
|
files := map[string]string{
|
|
"app.go": "package app\n\nfunc Run() {}\n",
|
|
"config.ts": "export const config = {};\n",
|
|
"README.md": "# Project\n",
|
|
}
|
|
|
|
for name, content := range files {
|
|
path := filepath.Join(tmpDir, name)
|
|
err := os.WriteFile(path, []byte(content), 0644)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
task := &Task{
|
|
ID: "task-1",
|
|
Title: "Test",
|
|
Files: []string{"app.go", "config.ts"},
|
|
}
|
|
|
|
gathered, err := GatherRelatedFiles(task, tmpDir)
|
|
require.NoError(t, err)
|
|
assert.Len(t, gathered, 2)
|
|
|
|
// Check languages detected correctly
|
|
foundGo := false
|
|
foundTS := false
|
|
for _, f := range gathered {
|
|
if f.Path == "app.go" {
|
|
foundGo = true
|
|
assert.Equal(t, "go", f.Language)
|
|
}
|
|
if f.Path == "config.ts" {
|
|
foundTS = true
|
|
assert.Equal(t, "typescript", f.Language)
|
|
}
|
|
}
|
|
assert.True(t, foundGo, "should find app.go")
|
|
assert.True(t, foundTS, "should find config.ts")
|
|
}
|
|
|
|
func TestGatherRelatedFiles_Bad_NilTask(t *testing.T) {
|
|
files, err := GatherRelatedFiles(nil, ".")
|
|
assert.Error(t, err)
|
|
assert.Nil(t, files)
|
|
}
|
|
|
|
func TestGatherRelatedFiles_Good_MissingFiles(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
task := &Task{
|
|
ID: "task-1",
|
|
Title: "Test",
|
|
Files: []string{"nonexistent.go", "also-missing.ts"},
|
|
}
|
|
|
|
// Should not error, just return empty list
|
|
gathered, err := GatherRelatedFiles(task, tmpDir)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, gathered)
|
|
}
|
|
|
|
func TestDetectLanguage(t *testing.T) {
|
|
tests := []struct {
|
|
path string
|
|
expected string
|
|
}{
|
|
{"main.go", "go"},
|
|
{"app.ts", "typescript"},
|
|
{"app.tsx", "typescript"},
|
|
{"script.js", "javascript"},
|
|
{"script.jsx", "javascript"},
|
|
{"main.py", "python"},
|
|
{"lib.rs", "rust"},
|
|
{"App.java", "java"},
|
|
{"config.yaml", "yaml"},
|
|
{"config.yml", "yaml"},
|
|
{"data.json", "json"},
|
|
{"index.html", "html"},
|
|
{"styles.css", "css"},
|
|
{"styles.scss", "scss"},
|
|
{"query.sql", "sql"},
|
|
{"README.md", "markdown"},
|
|
{"unknown.xyz", "text"},
|
|
{"", "text"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.path, func(t *testing.T) {
|
|
result := detectLanguage(tt.path)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExtractKeywords(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
text string
|
|
expected int // minimum number of keywords expected
|
|
}{
|
|
{
|
|
name: "simple title",
|
|
text: "Add user authentication feature",
|
|
expected: 2,
|
|
},
|
|
{
|
|
name: "with stop words",
|
|
text: "The quick brown fox jumps over the lazy dog",
|
|
expected: 3,
|
|
},
|
|
{
|
|
name: "technical text",
|
|
text: "Implement OAuth2 authentication with JWT tokens",
|
|
expected: 3,
|
|
},
|
|
{
|
|
name: "empty",
|
|
text: "",
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "only stop words",
|
|
text: "the a an and or but in on at",
|
|
expected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
keywords := extractKeywords(tt.text)
|
|
assert.GreaterOrEqual(t, len(keywords), tt.expected)
|
|
// Keywords should not exceed 5
|
|
assert.LessOrEqual(t, len(keywords), 5)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTaskContext_FormatContext(t *testing.T) {
|
|
task := &Task{
|
|
ID: "test-456",
|
|
Title: "Test Formatting",
|
|
Description: "This is a test description",
|
|
Priority: PriorityHigh,
|
|
Status: StatusInProgress,
|
|
}
|
|
|
|
ctx := &TaskContext{
|
|
Task: task,
|
|
Files: []FileContent{{Path: "main.go", Content: "package main", Language: "go"}},
|
|
GitStatus: " M main.go",
|
|
RecentCommits: "abc123 Initial commit",
|
|
RelatedCode: []FileContent{{Path: "util.go", Content: "package util", Language: "go"}},
|
|
}
|
|
|
|
formatted := ctx.FormatContext()
|
|
|
|
assert.Contains(t, formatted, "# Task Context")
|
|
assert.Contains(t, formatted, "test-456")
|
|
assert.Contains(t, formatted, "Test Formatting")
|
|
assert.Contains(t, formatted, "## Task Files")
|
|
assert.Contains(t, formatted, "## Git Status")
|
|
assert.Contains(t, formatted, "## Recent Commits")
|
|
assert.Contains(t, formatted, "## Related Code")
|
|
}
|