mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-06-08 23:39:40 +00:00
4f4b4ac70f
- [x] Add language-specific chunkers with AST parsing (Go, Python, TypeScript) - [x] Implement chunking manager to dispatch files to appropriate chunkers - [x] Integrate code chunks into vector sync for semantic search - [x] Add tree-sitter dependency for Python/TypeScript parsing - [x] Reorder struct fields for consistency across codebase - [x] Rename error variables to follow Go conventions (err → unmarshalErr, etc.) - [x] Add code chunk metadata to vector documents (language, symbol name, line ranges) - [x] Update worker service to initialize chunking pipeline with all three languages
277 lines
6.4 KiB
Go
277 lines
6.4 KiB
Go
package sdk
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestTruncate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected string
|
|
maxLen int
|
|
}{
|
|
{
|
|
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... (truncated)",
|
|
},
|
|
{
|
|
name: "empty_string",
|
|
input: "",
|
|
maxLen: 5,
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "zero_max_length",
|
|
input: "hello",
|
|
maxLen: 0,
|
|
expected: "... (truncated)",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := truncate(tt.input, tt.maxLen)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuildObservationPrompt(t *testing.T) {
|
|
now := time.Now().UnixMilli()
|
|
|
|
tests := []struct { //nolint:govet
|
|
exec ToolExecution
|
|
contains []string
|
|
name string
|
|
}{
|
|
{
|
|
name: "basic_read_tool",
|
|
exec: ToolExecution{
|
|
ID: 1,
|
|
ToolName: "Read",
|
|
ToolInput: `{"file_path": "/path/to/file.go"}`,
|
|
ToolOutput: `package main\nfunc main() {}`,
|
|
CreatedAtEpoch: now,
|
|
CWD: "/project",
|
|
},
|
|
contains: []string{
|
|
"<observed_from_primary_session>",
|
|
"<what_happened>Read</what_happened>",
|
|
"<working_directory>/project</working_directory>",
|
|
"<parameters>",
|
|
"file_path",
|
|
"<outcome>",
|
|
"</observed_from_primary_session>",
|
|
},
|
|
},
|
|
{
|
|
name: "edit_tool_with_json_input",
|
|
exec: ToolExecution{
|
|
ID: 2,
|
|
ToolName: "Edit",
|
|
ToolInput: `{"file_path": "/file.go", "old_string": "foo", "new_string": "bar"}`,
|
|
ToolOutput: "Edit applied successfully",
|
|
CreatedAtEpoch: now,
|
|
CWD: "",
|
|
},
|
|
contains: []string{
|
|
"<what_happened>Edit</what_happened>",
|
|
"file_path",
|
|
"old_string",
|
|
"new_string",
|
|
},
|
|
},
|
|
{
|
|
name: "no_cwd",
|
|
exec: ToolExecution{
|
|
ID: 3,
|
|
ToolName: "Bash",
|
|
ToolInput: `{"command": "go test"}`,
|
|
ToolOutput: "ok",
|
|
CreatedAtEpoch: now,
|
|
CWD: "",
|
|
},
|
|
contains: []string{
|
|
"<what_happened>Bash</what_happened>",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := BuildObservationPrompt(tt.exec)
|
|
|
|
for _, s := range tt.contains {
|
|
assert.Contains(t, result, s, "Expected result to contain: %s", s)
|
|
}
|
|
|
|
// Check CWD only appears when set
|
|
if tt.exec.CWD == "" {
|
|
assert.NotContains(t, result, "<working_directory>")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuildObservationPrompt_TruncatesLongContent(t *testing.T) {
|
|
longInput := strings.Repeat("x", 5000)
|
|
longOutput := strings.Repeat("y", 7000)
|
|
|
|
exec := ToolExecution{
|
|
ID: 1,
|
|
ToolName: "Read",
|
|
ToolInput: longInput,
|
|
ToolOutput: longOutput,
|
|
CreatedAtEpoch: time.Now().UnixMilli(),
|
|
CWD: "/project",
|
|
}
|
|
|
|
result := BuildObservationPrompt(exec)
|
|
|
|
// Input should be truncated to ~3000
|
|
assert.Contains(t, result, "truncated")
|
|
// The result should not be excessively long
|
|
assert.Less(t, len(result), 10000)
|
|
}
|
|
|
|
func TestBuildSummaryPrompt(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
req SummaryRequest
|
|
contains []string
|
|
}{
|
|
{
|
|
name: "basic_request",
|
|
req: SummaryRequest{
|
|
SessionDBID: 1,
|
|
SDKSessionID: "sdk-123",
|
|
Project: "test-project",
|
|
},
|
|
contains: []string{
|
|
"PROGRESS SUMMARY CHECKPOINT",
|
|
"<summary>",
|
|
"<request>",
|
|
"<investigated>",
|
|
"<learned>",
|
|
"<completed>",
|
|
"<next_steps>",
|
|
"<notes>",
|
|
"</summary>",
|
|
},
|
|
},
|
|
{
|
|
name: "with_assistant_message",
|
|
req: SummaryRequest{
|
|
SessionDBID: 2,
|
|
SDKSessionID: "sdk-456",
|
|
Project: "project-b",
|
|
LastAssistantMessage: "I fixed the authentication bug by updating the JWT validation.",
|
|
},
|
|
contains: []string{
|
|
"Claude's Full Response to User:",
|
|
"fixed the authentication",
|
|
},
|
|
},
|
|
{
|
|
name: "empty_assistant_message",
|
|
req: SummaryRequest{
|
|
SessionDBID: 3,
|
|
SDKSessionID: "sdk-789",
|
|
Project: "project-c",
|
|
LastAssistantMessage: "",
|
|
},
|
|
contains: []string{
|
|
"PROGRESS SUMMARY CHECKPOINT",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := BuildSummaryPrompt(tt.req)
|
|
|
|
for _, s := range tt.contains {
|
|
assert.Contains(t, result, s, "Expected result to contain: %s", s)
|
|
}
|
|
|
|
// Check assistant message only appears when set
|
|
if tt.req.LastAssistantMessage == "" {
|
|
assert.NotContains(t, result, "Claude's Full Response")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuildSummaryPrompt_TruncatesLongAssistantMessage(t *testing.T) {
|
|
longMessage := strings.Repeat("a", 5000)
|
|
|
|
req := SummaryRequest{
|
|
SessionDBID: 1,
|
|
SDKSessionID: "sdk-123",
|
|
Project: "test",
|
|
LastAssistantMessage: longMessage,
|
|
}
|
|
|
|
result := BuildSummaryPrompt(req)
|
|
|
|
// Should contain truncation indicator
|
|
assert.Contains(t, result, "truncated")
|
|
// Result should be reasonable length (less than full 5000 + overhead)
|
|
assert.Less(t, len(result), 6000)
|
|
}
|
|
|
|
func TestToolExecution_Struct(t *testing.T) {
|
|
exec := ToolExecution{
|
|
ID: 42,
|
|
ToolName: "Write",
|
|
ToolInput: `{"file_path": "/test.go"}`,
|
|
ToolOutput: "File written",
|
|
CreatedAtEpoch: 1234567890000,
|
|
CWD: "/workspace",
|
|
}
|
|
|
|
assert.Equal(t, int64(42), exec.ID)
|
|
assert.Equal(t, "Write", exec.ToolName)
|
|
assert.Equal(t, `{"file_path": "/test.go"}`, exec.ToolInput)
|
|
assert.Equal(t, "File written", exec.ToolOutput)
|
|
assert.Equal(t, int64(1234567890000), exec.CreatedAtEpoch)
|
|
assert.Equal(t, "/workspace", exec.CWD)
|
|
}
|
|
|
|
func TestSummaryRequest_Struct(t *testing.T) {
|
|
req := SummaryRequest{
|
|
SessionDBID: 100,
|
|
SDKSessionID: "sdk-abc",
|
|
Project: "my-project",
|
|
UserPrompt: "Fix the bug",
|
|
LastUserMessage: "Please fix the auth bug",
|
|
LastAssistantMessage: "I've fixed the authentication issue",
|
|
}
|
|
|
|
assert.Equal(t, int64(100), req.SessionDBID)
|
|
assert.Equal(t, "sdk-abc", req.SDKSessionID)
|
|
assert.Equal(t, "my-project", req.Project)
|
|
assert.Equal(t, "Fix the bug", req.UserPrompt)
|
|
assert.Equal(t, "Please fix the auth bug", req.LastUserMessage)
|
|
assert.Equal(t, "I've fixed the authentication issue", req.LastAssistantMessage)
|
|
}
|