diff --git a/eval/go_evals/eval_test.go b/eval/go_evals/eval_test.go index a378e4cab..ca2da32fb 100644 --- a/eval/go_evals/eval_test.go +++ b/eval/go_evals/eval_test.go @@ -39,6 +39,7 @@ func allFileTools(workspace string) []tools.Tool { // --------------------------------------------------------------------------- func TestToolRegistry_AllToolsHaveSchema(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) registry := tools.NewToolRegistry() @@ -68,6 +69,7 @@ func TestToolRegistry_AllToolsHaveSchema(t *testing.T) { } func TestToolRegistry_SchemaPropertiesAreTyped(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) for _, tool := range allFileTools(workspace) { @@ -91,10 +93,11 @@ func TestToolRegistry_SchemaPropertiesAreTyped(t *testing.T) { // --------------------------------------------------------------------------- func TestToolExecution_ReadFile_NonExistent(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) readTool := tools.NewReadFileTool(workspace, true) - result := readTool.Execute(context.Background(), map[string]interface{}{ + result := readTool.Execute(t.Context(), map[string]interface{}{ "path": "nonexistent_file_12345.txt", }) @@ -103,10 +106,11 @@ func TestToolExecution_ReadFile_NonExistent(t *testing.T) { } func TestToolExecution_WriteAndReadFile(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) writeTool := tools.NewWriteFileTool(workspace, true) - writeResult := writeTool.Execute(context.Background(), map[string]interface{}{ + writeResult := writeTool.Execute(t.Context(), map[string]interface{}{ "path": "eval_test.txt", "content": "hello from eval test", }) @@ -114,7 +118,7 @@ func TestToolExecution_WriteAndReadFile(t *testing.T) { assert.False(t, writeResult.IsError, "write should succeed: %s", writeResult.ForLLM) readTool := tools.NewReadFileTool(workspace, true) - readResult := readTool.Execute(context.Background(), map[string]interface{}{ + readResult := readTool.Execute(t.Context(), map[string]interface{}{ "path": "eval_test.txt", }) require.NotNil(t, readResult) @@ -123,10 +127,11 @@ func TestToolExecution_WriteAndReadFile(t *testing.T) { } func TestToolExecution_ExecBlocking(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) execTool := tools.NewExecTool(workspace, false) - result := execTool.Execute(context.Background(), map[string]interface{}{ + result := execTool.Execute(t.Context(), map[string]interface{}{ "command": "echo dragonscale-eval-test", }) @@ -136,13 +141,14 @@ func TestToolExecution_ExecBlocking(t *testing.T) { } func TestToolExecution_ListDir(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) os.WriteFile(filepath.Join(workspace, "file_a.txt"), []byte("a"), 0644) os.WriteFile(filepath.Join(workspace, "file_b.txt"), []byte("b"), 0644) listTool := tools.NewListDirTool(workspace, true) - result := listTool.Execute(context.Background(), map[string]interface{}{ + result := listTool.Execute(t.Context(), map[string]interface{}{ "path": ".", }) @@ -153,16 +159,17 @@ func TestToolExecution_ListDir(t *testing.T) { } func TestToolExecution_EditFile(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) writeTool := tools.NewWriteFileTool(workspace, true) - writeTool.Execute(context.Background(), map[string]interface{}{ + writeTool.Execute(t.Context(), map[string]interface{}{ "path": "edit_target.txt", "content": "hello world foo bar", }) editTool := tools.NewEditFileTool(workspace, true) - result := editTool.Execute(context.Background(), map[string]interface{}{ + result := editTool.Execute(t.Context(), map[string]interface{}{ "path": "edit_target.txt", "old_text": "world", "new_text": "dragonscale", @@ -171,7 +178,7 @@ func TestToolExecution_EditFile(t *testing.T) { assert.False(t, result.IsError, "edit should succeed: %s", result.ForLLM) readTool := tools.NewReadFileTool(workspace, true) - readResult := readTool.Execute(context.Background(), map[string]interface{}{ + readResult := readTool.Execute(t.Context(), map[string]interface{}{ "path": "edit_target.txt", }) assert.Contains(t, readResult.ForLLM, "dragonscale") @@ -179,16 +186,17 @@ func TestToolExecution_EditFile(t *testing.T) { } func TestToolExecution_AppendFile(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) writeTool := tools.NewWriteFileTool(workspace, true) - writeTool.Execute(context.Background(), map[string]interface{}{ + writeTool.Execute(t.Context(), map[string]interface{}{ "path": "append_target.txt", "content": "line one\n", }) appendTool := tools.NewAppendFileTool(workspace, true) - result := appendTool.Execute(context.Background(), map[string]interface{}{ + result := appendTool.Execute(t.Context(), map[string]interface{}{ "path": "append_target.txt", "content": "line two\n", }) @@ -196,7 +204,7 @@ func TestToolExecution_AppendFile(t *testing.T) { assert.False(t, result.IsError, "append should succeed: %s", result.ForLLM) readTool := tools.NewReadFileTool(workspace, true) - readResult := readTool.Execute(context.Background(), map[string]interface{}{ + readResult := readTool.Execute(t.Context(), map[string]interface{}{ "path": "append_target.txt", }) assert.Contains(t, readResult.ForLLM, "line one") @@ -208,10 +216,11 @@ func TestToolExecution_AppendFile(t *testing.T) { // --------------------------------------------------------------------------- func TestToolExecution_ReadFile_WorkspaceRestriction(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) readTool := tools.NewReadFileTool(workspace, true) - result := readTool.Execute(context.Background(), map[string]interface{}{ + result := readTool.Execute(t.Context(), map[string]interface{}{ "path": "/etc/passwd", }) require.NotNil(t, result) @@ -219,10 +228,11 @@ func TestToolExecution_ReadFile_WorkspaceRestriction(t *testing.T) { } func TestToolExecution_WriteFile_WorkspaceRestriction(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) writeTool := tools.NewWriteFileTool(workspace, true) - result := writeTool.Execute(context.Background(), map[string]interface{}{ + result := writeTool.Execute(t.Context(), map[string]interface{}{ "path": "/tmp/escape_test.txt", "content": "should not write", }) @@ -231,10 +241,11 @@ func TestToolExecution_WriteFile_WorkspaceRestriction(t *testing.T) { } func TestToolExecution_ReadFile_PathTraversal(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) readTool := tools.NewReadFileTool(workspace, true) - result := readTool.Execute(context.Background(), map[string]interface{}{ + result := readTool.Execute(t.Context(), map[string]interface{}{ "path": "../../../../etc/hostname", }) require.NotNil(t, result) @@ -242,6 +253,7 @@ func TestToolExecution_ReadFile_PathTraversal(t *testing.T) { } func TestToolExecution_Unrestricted(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) tmpFile := filepath.Join(os.TempDir(), "dragonscale_unrestricted_test.txt") @@ -249,7 +261,7 @@ func TestToolExecution_Unrestricted(t *testing.T) { defer os.Remove(tmpFile) readTool := tools.NewReadFileTool(workspace, false) - result := readTool.Execute(context.Background(), map[string]interface{}{ + result := readTool.Execute(t.Context(), map[string]interface{}{ "path": tmpFile, }) require.NotNil(t, result) @@ -262,6 +274,7 @@ func TestToolExecution_Unrestricted(t *testing.T) { // --------------------------------------------------------------------------- func TestToolRegistry_ProgressiveDisclosure(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) registry := tools.NewToolRegistry() @@ -290,6 +303,7 @@ func TestToolRegistry_ProgressiveDisclosure(t *testing.T) { } func TestToolSearch_FindsReadFile(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) registry := tools.NewToolRegistry() @@ -299,7 +313,7 @@ func TestToolSearch_FindsReadFile(t *testing.T) { registry.RegisterMetaTools() searchTool := tools.NewToolSearchTool(registry) - result := searchTool.Execute(context.Background(), map[string]interface{}{ + result := searchTool.Execute(t.Context(), map[string]interface{}{ "query": "read file", }) @@ -309,6 +323,7 @@ func TestToolSearch_FindsReadFile(t *testing.T) { } func TestToolSearch_ListsAll(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) registry := tools.NewToolRegistry() @@ -317,7 +332,7 @@ func TestToolSearch_ListsAll(t *testing.T) { registry.RegisterMetaTools() searchTool := tools.NewToolSearchTool(registry) - result := searchTool.Execute(context.Background(), map[string]interface{}{ + result := searchTool.Execute(t.Context(), map[string]interface{}{ "query": "", }) @@ -327,6 +342,7 @@ func TestToolSearch_ListsAll(t *testing.T) { } func TestToolSearch_NoResults(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) registry := tools.NewToolRegistry() @@ -334,7 +350,7 @@ func TestToolSearch_NoResults(t *testing.T) { registry.RegisterMetaTools() searchTool := tools.NewToolSearchTool(registry) - result := searchTool.Execute(context.Background(), map[string]interface{}{ + result := searchTool.Execute(t.Context(), map[string]interface{}{ "query": "xyzzy_nonexistent_tool", }) @@ -343,6 +359,7 @@ func TestToolSearch_NoResults(t *testing.T) { } func TestToolCall_DispatchesCorrectly(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) os.WriteFile(filepath.Join(workspace, "dispatch_test.txt"), []byte("dispatch ok"), 0644) @@ -352,7 +369,7 @@ func TestToolCall_DispatchesCorrectly(t *testing.T) { registry.RegisterMetaTools() callTool := tools.NewToolCallTool(registry) - result := callTool.Execute(context.Background(), map[string]interface{}{ + result := callTool.Execute(t.Context(), map[string]interface{}{ "tool_name": "read_file", "arguments": map[string]interface{}{ "path": "dispatch_test.txt", @@ -365,6 +382,7 @@ func TestToolCall_DispatchesCorrectly(t *testing.T) { } func TestToolCall_RejectsRecursion(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) registry := tools.NewToolRegistry() @@ -372,7 +390,7 @@ func TestToolCall_RejectsRecursion(t *testing.T) { registry.RegisterMetaTools() callTool := tools.NewToolCallTool(registry) - result := callTool.Execute(context.Background(), map[string]interface{}{ + result := callTool.Execute(t.Context(), map[string]interface{}{ "tool_name": "tool_call", }) @@ -381,11 +399,12 @@ func TestToolCall_RejectsRecursion(t *testing.T) { } func TestToolCall_RejectsUnknownTool(t *testing.T) { + t.Parallel() registry := tools.NewToolRegistry() registry.RegisterMetaTools() callTool := tools.NewToolCallTool(registry) - result := callTool.Execute(context.Background(), map[string]interface{}{ + result := callTool.Execute(t.Context(), map[string]interface{}{ "tool_name": "nonexistent_tool_xyz", }) @@ -394,17 +413,19 @@ func TestToolCall_RejectsUnknownTool(t *testing.T) { } func TestToolCall_MissingToolName(t *testing.T) { + t.Parallel() registry := tools.NewToolRegistry() registry.RegisterMetaTools() callTool := tools.NewToolCallTool(registry) - result := callTool.Execute(context.Background(), map[string]interface{}{}) + result := callTool.Execute(t.Context(), map[string]interface{}{}) require.NotNil(t, result) assert.True(t, result.IsError, "missing tool_name should be rejected") } func TestToolCall_StringArguments(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) os.WriteFile(filepath.Join(workspace, "str_args.txt"), []byte("string args ok"), 0644) @@ -414,7 +435,7 @@ func TestToolCall_StringArguments(t *testing.T) { registry.RegisterMetaTools() callTool := tools.NewToolCallTool(registry) - result := callTool.Execute(context.Background(), map[string]interface{}{ + result := callTool.Execute(t.Context(), map[string]interface{}{ "tool_name": "read_file", "arguments": `{"path": "str_args.txt"}`, }) @@ -429,6 +450,7 @@ func TestToolCall_StringArguments(t *testing.T) { // --------------------------------------------------------------------------- func TestToolRegistry_GatewayMarking(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) registry := tools.NewToolRegistry() @@ -458,6 +480,7 @@ func TestToolRegistry_GatewayMarking(t *testing.T) { // --------------------------------------------------------------------------- func TestConfig_DefaultValues(t *testing.T) { + t.Parallel() cfg := config.DefaultConfig() assert.True(t, cfg.Agents.Defaults.RestrictToSandbox, "restrict to sandbox should be on by default") @@ -469,6 +492,7 @@ func TestConfig_DefaultValues(t *testing.T) { } func TestConfig_LoadEvalConfigs(t *testing.T) { + t.Parallel() evalDir := filepath.Join("..", "..", "eval", "configs") tests := []struct { @@ -492,6 +516,7 @@ func TestConfig_LoadEvalConfigs(t *testing.T) { } func TestConfig_MissingFileReturnsDefaults(t *testing.T) { + t.Parallel() cfg, err := config.LoadConfig("/nonexistent/path/config.json") require.NoError(t, err, "missing config should return defaults, not error") assert.Equal(t, 768, cfg.Memory.EmbeddingDims, "should have default embedding dims") @@ -502,10 +527,11 @@ func TestConfig_MissingFileReturnsDefaults(t *testing.T) { // --------------------------------------------------------------------------- func TestToolExecution_ExecTimeout(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) execTool := tools.NewExecTool(workspace, false) - ctx, cancel := context.WithTimeout(context.Background(), 1) + ctx, cancel := context.WithTimeout(t.Context(), 1) defer cancel() result := execTool.Execute(ctx, map[string]interface{}{ @@ -516,10 +542,11 @@ func TestToolExecution_ExecTimeout(t *testing.T) { } func TestToolExecution_ExecEmptyCommand(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) execTool := tools.NewExecTool(workspace, false) - result := execTool.Execute(context.Background(), map[string]interface{}{ + result := execTool.Execute(t.Context(), map[string]interface{}{ "command": "", }) @@ -532,6 +559,7 @@ func TestToolExecution_ExecEmptyCommand(t *testing.T) { // --------------------------------------------------------------------------- func TestToolSchema_JSONRoundtrip(t *testing.T) { + t.Parallel() workspace := testWorkspace(t) for _, tool := range allFileTools(workspace) { diff --git a/internal/fantasy/agent_stream_test.go b/internal/fantasy/agent_stream_test.go index f7fbd6cce..5f2354db7 100644 --- a/internal/fantasy/agent_stream_test.go +++ b/internal/fantasy/agent_stream_test.go @@ -3,9 +3,10 @@ package fantasy import ( "context" "fmt" - jsonv2 "github.com/go-json-experiment/json" "testing" + jsonv2 "github.com/go-json-experiment/json" + "github.com/stretchr/testify/require" ) @@ -111,7 +112,7 @@ func TestStreamingAgentCallbacks(t *testing.T) { // Create agent agent := NewAgent(mockModel) - ctx := context.Background() + ctx := t.Context() // Create streaming call with all callbacks streamCall := AgentStreamCall{ @@ -301,7 +302,7 @@ func TestStreamingAgentWithTools(t *testing.T) { WithTools(&EchoTool{}), ) - ctx := context.Background() + ctx := t.Context() // Track callback invocations var toolInputStartCalled bool @@ -399,7 +400,7 @@ func TestStreamingAgentTextDeltas(t *testing.T) { } agent := NewAgent(mockModel) - ctx := context.Background() + ctx := t.Context() // Track text deltas var textDeltas []string @@ -461,7 +462,7 @@ func TestStreamingAgentReasoning(t *testing.T) { } agent := NewAgent(mockModel) - ctx := context.Background() + ctx := t.Context() var reasoningDeltas []string var textDeltas []string @@ -502,7 +503,7 @@ func TestStreamingAgentError(t *testing.T) { } agent := NewAgent(mockModel) - ctx := context.Background() + ctx := t.Context() // Track error callbacks var errorOccurred bool @@ -568,7 +569,7 @@ func TestStreamingAgentSources(t *testing.T) { } agent := NewAgent(mockModel) - ctx := context.Background() + ctx := t.Context() var sources []SourceContent diff --git a/internal/fantasy/agent_test.go b/internal/fantasy/agent_test.go index 70ced785d..6a00c543b 100644 --- a/internal/fantasy/agent_test.go +++ b/internal/fantasy/agent_test.go @@ -145,7 +145,7 @@ func TestAgent_Generate_ResultContent_AllTypes(t *testing.T) { } agent := NewAgent(model, WithTools(tool1)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "prompt", }) @@ -209,7 +209,7 @@ func TestAgent_Generate_ResultText(t *testing.T) { } agent := NewAgent(model) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "prompt", }) @@ -279,7 +279,7 @@ func TestAgent_Generate_ResultToolCalls(t *testing.T) { } agent := NewAgent(model, WithTools(tool1, tool2)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test-input", }) @@ -353,7 +353,7 @@ func TestAgent_Generate_ResultToolResults(t *testing.T) { } agent := NewAgent(model, WithTools(tool1)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test-input", }) @@ -440,7 +440,7 @@ func TestAgent_Generate_MultipleSteps(t *testing.T) { } agent := NewAgent(model, WithTools(tool1)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test-input", }) @@ -499,7 +499,7 @@ func TestAgent_Generate_BasicText(t *testing.T) { } agent := NewAgent(model) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -531,7 +531,7 @@ func TestAgent_Generate_EmptyPrompt(t *testing.T) { model := &mockLanguageModel{} agent := NewAgent(model) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "", // Empty prompt should cause error }) @@ -570,7 +570,7 @@ func TestAgent_Generate_WithSystemPrompt(t *testing.T) { } agent := NewAgent(model, WithSystemPrompt("You are a helpful assistant")) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -623,7 +623,7 @@ func TestAgent_Generate_OptionsActiveTools(t *testing.T) { } agent := NewAgent(model, WithTools(tool1, tool2)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test-input", ActiveTools: []string{"tool1"}, // Only tool1 should be active }) @@ -872,7 +872,7 @@ func TestStopConditions_Integration(t *testing.T) { agent := NewAgent(model, WithStopConditions(StepCountIs(1))) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -904,7 +904,7 @@ func TestStopConditions_Integration(t *testing.T) { FinishReasonIs(FinishReasonStop), // Or stop on finish reason )) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -952,7 +952,7 @@ func TestPrepareStep(t *testing.T) { agent := NewAgent(model, WithSystemPrompt("Original system prompt")) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", PrepareStep: prepareStepFunc, }) @@ -989,7 +989,7 @@ func TestPrepareStep(t *testing.T) { agent := NewAgent(model) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", PrepareStep: prepareStepFunc, }) @@ -1034,7 +1034,7 @@ func TestPrepareStep(t *testing.T) { agent := NewAgent(model, WithTools(tool1, tool2, tool3)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", PrepareStep: prepareStepFunc, }) @@ -1073,7 +1073,7 @@ func TestPrepareStep(t *testing.T) { agent := NewAgent(model, WithTools(tool1)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", PrepareStep: prepareStepFunc, }) @@ -1133,7 +1133,7 @@ func TestPrepareStep(t *testing.T) { agent := NewAgent(model, WithSystemPrompt("Original system"), WithTools(tool1, tool2)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", PrepareStep: prepareStepFunc, }) @@ -1194,7 +1194,7 @@ func TestPrepareStep(t *testing.T) { agent := NewAgent(model, WithSystemPrompt("Parent system"), WithTools(tool1)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", PrepareStep: prepareStepFunc, }) @@ -1240,7 +1240,7 @@ func TestPrepareStep(t *testing.T) { agent := NewAgent(model, WithTools(tool1, tool2)) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", PrepareStep: prepareStepFunc, }) @@ -1289,7 +1289,7 @@ func TestToolCallRepair(t *testing.T) { agent := NewAgent(model, WithTools(tool), WithStopConditions(StepCountIs(2))) // Limit steps - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -1333,7 +1333,7 @@ func TestToolCallRepair(t *testing.T) { agent := NewAgent(model, WithTools(tool), WithStopConditions(StepCountIs(2))) // Limit steps - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -1388,7 +1388,7 @@ func TestToolCallRepair(t *testing.T) { agent := NewAgent(model, WithTools(tool), WithRepairToolCall(repairFunc), WithStopConditions(StepCountIs(2))) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -1438,7 +1438,7 @@ func TestToolCallRepair(t *testing.T) { agent := NewAgent(model, WithTools(tool), WithRepairToolCall(repairFunc), WithStopConditions(StepCountIs(2))) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -1476,7 +1476,7 @@ func TestToolCallRepair(t *testing.T) { agent := NewAgent(model, WithTools(tool), WithStopConditions(StepCountIs(2))) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -1521,7 +1521,7 @@ func TestToolCallRepair(t *testing.T) { agent := NewAgent(model, WithTools(tool), WithStopConditions(StepCountIs(2))) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "test prompt", }) @@ -1582,7 +1582,7 @@ func TestAgent_MediaToolResponses(t *testing.T) { agent := NewAgent(model, WithTools(imageTool), WithStopConditions(StepCountIs(3))) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "Generate an image", }) @@ -1636,7 +1636,7 @@ func TestAgent_MediaToolResponses(t *testing.T) { agent := NewAgent(model, WithTools(audioTool), WithStopConditions(StepCountIs(3))) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "Generate audio", }) @@ -1690,7 +1690,7 @@ func TestAgent_MediaToolResponses(t *testing.T) { agent := NewAgent(model, WithTools(imageTool), WithStopConditions(StepCountIs(3))) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "Take a screenshot", }) @@ -1749,7 +1749,7 @@ func TestAgent_MediaToolResponses(t *testing.T) { agent := NewAgent(model, WithTools(imageTool), WithStopConditions(StepCountIs(3))) - result, err := agent.Generate(context.Background(), AgentCall{ + result, err := agent.Generate(t.Context(), AgentCall{ Prompt: "Generate image", }) diff --git a/internal/fantasy/json_test.go b/internal/fantasy/json_test.go index c83da0350..19eb69731 100644 --- a/internal/fantasy/json_test.go +++ b/internal/fantasy/json_test.go @@ -2,12 +2,14 @@ package fantasy import ( "errors" - jsonv2 "github.com/go-json-experiment/json" - "reflect" "testing" + + jsonv2 "github.com/go-json-experiment/json" + "github.com/google/go-cmp/cmp" ) func TestMessageJSONSerialization(t *testing.T) { + t.Parallel() tests := []struct { name string message Message @@ -199,44 +201,29 @@ func compareMessagePart(t *testing.T, index int, original, decoded MessagePart) case ContentTypeText: orig := original.(TextPart) dec := decoded.(TextPart) - if orig.Text != dec.Text { - t.Errorf("content[%d] text mismatch: got %q, want %q", index, dec.Text, orig.Text) + if diff := cmp.Diff(orig, dec); diff != "" { + t.Errorf("content[%d] TextPart mismatch (-want +got):\n%s", index, diff) } case ContentTypeReasoning: orig := original.(ReasoningPart) dec := decoded.(ReasoningPart) - if orig.Text != dec.Text { - t.Errorf("content[%d] reasoning text mismatch: got %q, want %q", index, dec.Text, orig.Text) + if diff := cmp.Diff(orig, dec); diff != "" { + t.Errorf("content[%d] ReasoningPart mismatch (-want +got):\n%s", index, diff) } case ContentTypeFile: orig := original.(FilePart) dec := decoded.(FilePart) - if orig.Filename != dec.Filename { - t.Errorf("content[%d] filename mismatch: got %q, want %q", index, dec.Filename, orig.Filename) - } - if orig.MediaType != dec.MediaType { - t.Errorf("content[%d] media type mismatch: got %q, want %q", index, dec.MediaType, orig.MediaType) - } - if !reflect.DeepEqual(orig.Data, dec.Data) { - t.Errorf("content[%d] file data mismatch", index) + if diff := cmp.Diff(orig, dec); diff != "" { + t.Errorf("content[%d] FilePart mismatch (-want +got):\n%s", index, diff) } case ContentTypeToolCall: orig := original.(ToolCallPart) dec := decoded.(ToolCallPart) - if orig.ToolCallID != dec.ToolCallID { - t.Errorf("content[%d] tool call id mismatch: got %q, want %q", index, dec.ToolCallID, orig.ToolCallID) - } - if orig.ToolName != dec.ToolName { - t.Errorf("content[%d] tool name mismatch: got %q, want %q", index, dec.ToolName, orig.ToolName) - } - if orig.Input != dec.Input { - t.Errorf("content[%d] tool input mismatch: got %q, want %q", index, dec.Input, orig.Input) - } - if orig.ProviderExecuted != dec.ProviderExecuted { - t.Errorf("content[%d] provider executed mismatch: got %v, want %v", index, dec.ProviderExecuted, orig.ProviderExecuted) + if diff := cmp.Diff(orig, dec); diff != "" { + t.Errorf("content[%d] ToolCallPart mismatch (-want +got):\n%s", index, diff) } case ContentTypeToolResult: @@ -259,30 +246,35 @@ func compareToolResultOutput(t *testing.T, index int, original, decoded ToolResu case ToolResultContentTypeText: orig := original.(ToolResultOutputContentText) dec := decoded.(ToolResultOutputContentText) - if orig.Text != dec.Text { - t.Errorf("content[%d] tool result text mismatch: got %q, want %q", index, dec.Text, orig.Text) + if diff := cmp.Diff(orig, dec); diff != "" { + t.Errorf("content[%d] ToolResultOutputContentText mismatch (-want +got):\n%s", index, diff) } case ToolResultContentTypeError: orig := original.(ToolResultOutputContentError) dec := decoded.(ToolResultOutputContentError) - if orig.Error.Error() != dec.Error.Error() { - t.Errorf("content[%d] tool result error mismatch: got %q, want %q", index, dec.Error.Error(), orig.Error.Error()) + if orig.Error == nil && dec.Error == nil { + return + } + if orig.Error == nil || dec.Error == nil { + t.Errorf("content[%d] ToolResultOutputContentError mismatch (-want +got): %v != %v", index, orig.Error, dec.Error) + return + } + if diff := cmp.Diff(orig.Error.Error(), dec.Error.Error()); diff != "" { + t.Errorf("content[%d] ToolResultOutputContentError mismatch (-want +got):\n%s", index, diff) } case ToolResultContentTypeMedia: orig := original.(ToolResultOutputContentMedia) dec := decoded.(ToolResultOutputContentMedia) - if orig.Data != dec.Data { - t.Errorf("content[%d] tool result media data mismatch", index) - } - if orig.MediaType != dec.MediaType { - t.Errorf("content[%d] tool result media type mismatch: got %q, want %q", index, dec.MediaType, orig.MediaType) + if diff := cmp.Diff(orig, dec); diff != "" { + t.Errorf("content[%d] ToolResultOutputContentMedia mismatch (-want +got):\n%s", index, diff) } } } func TestHelperFunctions(t *testing.T) { + t.Parallel() t.Run("NewUserMessage - text only", func(t *testing.T) { msg := NewUserMessage("Hello") @@ -412,6 +404,7 @@ func TestHelperFunctions(t *testing.T) { } func TestEdgeCases(t *testing.T) { + t.Parallel() t.Run("empty text part", func(t *testing.T) { msg := Message{ Role: MessageRoleUser, @@ -520,6 +513,7 @@ func TestEdgeCases(t *testing.T) { } func TestInvalidJSONHandling(t *testing.T) { + t.Parallel() t.Run("unknown message part type", func(t *testing.T) { invalidJSON := `{ "role": "user", @@ -606,6 +600,7 @@ func (m *mockProviderData) UnmarshalJSON(data []byte) error { } func TestPromptSerialization(t *testing.T) { + t.Parallel() t.Run("serialize prompt (message slice)", func(t *testing.T) { prompt := Prompt{ NewSystemMessage("You are helpful"), @@ -647,6 +642,7 @@ func TestPromptSerialization(t *testing.T) { } func TestStreamPartErrorSerialization(t *testing.T) { + t.Parallel() t.Run("stream part with ProviderError containing OpenAI API error", func(t *testing.T) { // Create a mock OpenAI API error openaiErr := errors.New("invalid_api_key: Incorrect API key provided") diff --git a/internal/fantasy/jsonrepair/jsonrepair_test.go b/internal/fantasy/jsonrepair/jsonrepair_test.go index 5364932ea..5c0d618ee 100644 --- a/internal/fantasy/jsonrepair/jsonrepair_test.go +++ b/internal/fantasy/jsonrepair/jsonrepair_test.go @@ -7,6 +7,7 @@ import ( ) func TestRepairJSON(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -79,6 +80,7 @@ func TestRepairJSON(t *testing.T) { } func TestRepairJSONMultipleTopLevel(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -125,6 +127,7 @@ func TestRepairJSONMultipleTopLevel(t *testing.T) { } func TestRepairJSONEnsureASCII(t *testing.T) { + t.Parallel() got, err := RepairJSON("{'test_中国人_ascii':'统一码'}", WithEnsureASCII(false)) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -136,6 +139,7 @@ func TestRepairJSONEnsureASCII(t *testing.T) { } func TestRepairJSONStreamStable(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -197,6 +201,7 @@ func TestRepairJSONStreamStable(t *testing.T) { } func TestLoads(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -273,6 +278,7 @@ func TestLoads(t *testing.T) { } func TestRepairJSONSkipJSONLoads(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -320,6 +326,7 @@ func TestRepairJSONSkipJSONLoads(t *testing.T) { } func TestRepairJSONWithLog(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -368,6 +375,7 @@ func TestRepairJSONWithLog(t *testing.T) { } func TestRepairJSONStrict(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -438,6 +446,7 @@ func TestRepairJSONStrict(t *testing.T) { } func TestParseArrayObjects(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -479,6 +488,7 @@ func TestParseArrayObjects(t *testing.T) { } func TestParseArrayEdgeCases(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -620,6 +630,7 @@ func TestParseArrayEdgeCases(t *testing.T) { } func TestParseArrayMissingQuotes(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -656,6 +667,7 @@ func TestParseArrayMissingQuotes(t *testing.T) { } func TestParseComment(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -712,6 +724,7 @@ func TestParseComment(t *testing.T) { } func TestParseNumber(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -757,6 +770,7 @@ func TestParseNumber(t *testing.T) { } func TestParseNumberEdgeCases(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -863,6 +877,7 @@ func TestParseNumberEdgeCases(t *testing.T) { } func TestParseObjectObjects(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -912,6 +927,7 @@ func TestParseObjectObjects(t *testing.T) { } func TestParseObjectEdgeCases(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -1118,6 +1134,7 @@ func TestParseObjectEdgeCases(t *testing.T) { } func TestParseObjectMergeAtEnd(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -1179,6 +1196,7 @@ func TestParseObjectMergeAtEnd(t *testing.T) { } func TestParseStringBasics(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -1225,6 +1243,7 @@ func TestParseStringBasics(t *testing.T) { } func TestMissingAndMixedQuotes(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -1346,6 +1365,7 @@ func TestMissingAndMixedQuotes(t *testing.T) { } func TestEscaping(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -1416,6 +1436,7 @@ func TestEscaping(t *testing.T) { } func TestMarkdown(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -1452,6 +1473,7 @@ func TestMarkdown(t *testing.T) { } func TestLeadingTrailingCharacters(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -1493,6 +1515,7 @@ func TestLeadingTrailingCharacters(t *testing.T) { } func TestStringJSONLLMBlock(t *testing.T) { + t.Parallel() cases := []struct { name string input string @@ -1534,6 +1557,7 @@ func TestStringJSONLLMBlock(t *testing.T) { } func TestParseBooleanOrNull(t *testing.T) { + t.Parallel() loadCases := []struct { name string input string diff --git a/internal/fantasy/providers/azure/azure_test.go b/internal/fantasy/providers/azure/azure_test.go index 6113699d6..d02aa51b3 100644 --- a/internal/fantasy/providers/azure/azure_test.go +++ b/internal/fantasy/providers/azure/azure_test.go @@ -7,6 +7,7 @@ import ( ) func TestParseAzureURL(t *testing.T) { + t.Parallel() tests := []struct { name string input string diff --git a/internal/fantasy/providers/openai/openai_test.go b/internal/fantasy/providers/openai/openai_test.go index 4ce6f5d2b..121313f80 100644 --- a/internal/fantasy/providers/openai/openai_test.go +++ b/internal/fantasy/providers/openai/openai_test.go @@ -1,7 +1,6 @@ package openai import ( - "context" "encoding/base64" "errors" "net/http" @@ -817,7 +816,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -850,7 +849,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -875,7 +874,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -916,7 +915,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -943,7 +942,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ LogProbs: fantasy.Opt(true), @@ -978,7 +977,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -1003,7 +1002,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -1028,7 +1027,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -1061,7 +1060,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ LogitBias: map[string]int64{ @@ -1104,7 +1103,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "o1-mini") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions( &ProviderOptions{ @@ -1145,7 +1144,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-4o") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ TextVerbosity: fantasy.Opt("low"), @@ -1184,7 +1183,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, Tools: []fantasy.Tool{ fantasy.FunctionTool{ @@ -1257,7 +1256,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, Tools: []fantasy.Tool{ fantasy.FunctionTool{ @@ -1305,7 +1304,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, Tools: []fantasy.Tool{ fantasy.FunctionTool{ @@ -1375,7 +1374,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -1418,7 +1417,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-4o-mini") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -1454,7 +1453,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-4o-mini") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -1483,7 +1482,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "o1-preview") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, Temperature: &[]float64{0.5}[0], TopP: &[]float64{0.7}[0], @@ -1532,7 +1531,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "o1-preview") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, MaxOutputTokens: &[]int64{1000}[0], }) @@ -1577,7 +1576,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "o1-preview") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -1605,7 +1604,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "o1-preview") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ MaxCompletionTokens: fantasy.Opt(int64(255)), @@ -1644,7 +1643,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ Prediction: map[string]any{ @@ -1689,7 +1688,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ Store: fantasy.Opt(true), @@ -1728,7 +1727,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ Metadata: map[string]any{ @@ -1771,7 +1770,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ PromptCacheKey: fantasy.Opt("test-cache-key-123"), @@ -1810,7 +1809,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ SafetyIdentifier: fantasy.Opt("test-safety-identifier-123"), @@ -1847,7 +1846,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-4o-search-preview") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, Temperature: &[]float64{0.7}[0], }) @@ -1882,7 +1881,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "o3-mini") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ ServiceTier: fantasy.Opt("flex"), @@ -1919,7 +1918,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-4o-mini") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ ServiceTier: fantasy.Opt("flex"), @@ -1953,7 +1952,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-4o-mini") - _, err = model.Generate(context.Background(), fantasy.Call{ + _, err = model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ ServiceTier: fantasy.Opt("priority"), @@ -1990,7 +1989,7 @@ func TestDoGenerate(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - result, err := model.Generate(context.Background(), fantasy.Call{ + result, err := model.Generate(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ ServiceTier: fantasy.Opt("priority"), @@ -2298,7 +2297,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - stream, err := model.Stream(context.Background(), fantasy.Call{ + stream, err := model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -2355,7 +2354,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - stream, err := model.Stream(context.Background(), fantasy.Call{ + stream, err := model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, Tools: []fantasy.Tool{ fantasy.FunctionTool{ @@ -2442,7 +2441,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - stream, err := model.Stream(context.Background(), fantasy.Call{ + stream, err := model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -2482,7 +2481,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - stream, err := model.Stream(context.Background(), fantasy.Call{ + stream, err := model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -2524,7 +2523,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Stream(context.Background(), fantasy.Call{ + _, err = model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -2573,7 +2572,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - stream, err := model.Stream(context.Background(), fantasy.Call{ + stream, err := model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -2624,7 +2623,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - stream, err := model.Stream(context.Background(), fantasy.Call{ + stream, err := model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -2668,7 +2667,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Stream(context.Background(), fantasy.Call{ + _, err = model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ Store: fantasy.Opt(true), @@ -2711,7 +2710,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-3.5-turbo") - _, err = model.Stream(context.Background(), fantasy.Call{ + _, err = model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ Metadata: map[string]any{ @@ -2758,7 +2757,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "o3-mini") - _, err = model.Stream(context.Background(), fantasy.Call{ + _, err = model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ ServiceTier: fantasy.Opt("flex"), @@ -2801,7 +2800,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "gpt-4o-mini") - _, err = model.Stream(context.Background(), fantasy.Call{ + _, err = model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, ProviderOptions: NewProviderOptions(&ProviderOptions{ ServiceTier: fantasy.Opt("priority"), @@ -2845,7 +2844,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "o1-preview") - stream, err := model.Stream(context.Background(), fantasy.Call{ + stream, err := model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, }) @@ -2892,7 +2891,7 @@ func TestDoStream(t *testing.T) { require.NoError(t, err) model, _ := provider.LanguageModel(t.Context(), "o1-preview") - stream, err := model.Stream(context.Background(), fantasy.Call{ + stream, err := model.Stream(t.Context(), fantasy.Call{ Prompt: testPrompt, }) diff --git a/internal/fantasy/providertests/anthropic_test.go b/internal/fantasy/providertests/anthropic_test.go index 3987ea03f..a41be58a0 100644 --- a/internal/fantasy/providertests/anthropic_test.go +++ b/internal/fantasy/providertests/anthropic_test.go @@ -17,6 +17,7 @@ var anthropicTestModels = []testModel{ } func TestAnthropicCommon(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range anthropicTestModels { pairs = append(pairs, builderPair{m.name, anthropicBuilder(m.model), nil, nil}) @@ -56,6 +57,7 @@ func addAnthropicCaching(ctx context.Context, options fantasy.PrepareStepFunctio } func TestAnthropicCommonWithCacheControl(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range anthropicTestModels { pairs = append(pairs, builderPair{m.name, anthropicBuilder(m.model), nil, addAnthropicCaching}) @@ -64,6 +66,7 @@ func TestAnthropicCommonWithCacheControl(t *testing.T) { } func TestAnthropicThinking(t *testing.T) { + t.Parallel() opts := fantasy.ProviderOptions{ anthropic.Name: &anthropic.ProviderOptions{ Thinking: &anthropic.ThinkingProviderOption{ @@ -82,6 +85,7 @@ func TestAnthropicThinking(t *testing.T) { } func TestAnthropicThinkingWithCacheControl(t *testing.T) { + t.Parallel() opts := fantasy.ProviderOptions{ anthropic.Name: &anthropic.ProviderOptions{ Thinking: &anthropic.ThinkingProviderOption{ @@ -100,6 +104,7 @@ func TestAnthropicThinkingWithCacheControl(t *testing.T) { } func TestAnthropicObjectGeneration(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range anthropicTestModels { pairs = append(pairs, builderPair{m.name, anthropicBuilder(m.model), nil, nil}) diff --git a/internal/fantasy/providertests/azure_responses_test.go b/internal/fantasy/providertests/azure_responses_test.go index 8b3945ba4..ab4de1eea 100644 --- a/internal/fantasy/providertests/azure_responses_test.go +++ b/internal/fantasy/providertests/azure_responses_test.go @@ -14,6 +14,7 @@ import ( ) func TestAzureResponsesCommon(t *testing.T) { + t.Parallel() var pairs []builderPair models := []testModel{ {"azure-gpt-5-mini", "gpt-5-mini", true}, @@ -41,6 +42,7 @@ func azureReasoningBuilder(model string) builderFunc { } func TestAzureResponsesWithSummaryThinking(t *testing.T) { + t.Parallel() opts := fantasy.ProviderOptions{ openai.Name: &openai.ResponsesProviderOptions{ Include: []openai.IncludeType{ diff --git a/internal/fantasy/providertests/azure_test.go b/internal/fantasy/providertests/azure_test.go index 198b8dc1c..5569e7021 100644 --- a/internal/fantasy/providertests/azure_test.go +++ b/internal/fantasy/providertests/azure_test.go @@ -16,6 +16,7 @@ import ( const defaultBaseURL = "https://fantasy-playground-resource.openai.azure.com" func TestAzureCommon(t *testing.T) { + t.Parallel() testCommon(t, []builderPair{ {"azure-o4-mini", builderAzureO4Mini, nil, nil}, {"azure-gpt-5-mini", builderAzureGpt5Mini, nil, nil}, @@ -24,6 +25,7 @@ func TestAzureCommon(t *testing.T) { } func TestAzureThinking(t *testing.T) { + t.Parallel() opts := fantasy.ProviderOptions{ openai.Name: &openai.ProviderOptions{ ReasoningEffort: openai.ReasoningEffortOption(openai.ReasoningEffortHigh), diff --git a/internal/fantasy/providertests/bedrock_test.go b/internal/fantasy/providertests/bedrock_test.go index da591ade1..a7a7cfe21 100644 --- a/internal/fantasy/providertests/bedrock_test.go +++ b/internal/fantasy/providertests/bedrock_test.go @@ -11,6 +11,7 @@ import ( ) func TestBedrockCommon(t *testing.T) { + t.Parallel() testCommon(t, []builderPair{ {"bedrock-anthropic-claude-3-sonnet", builderBedrockClaude3Sonnet, nil, nil}, {"bedrock-anthropic-claude-3-opus", builderBedrockClaude3Opus, nil, nil}, @@ -19,6 +20,7 @@ func TestBedrockCommon(t *testing.T) { } func TestBedrockBasicAuth(t *testing.T) { + t.Parallel() testSimple(t, builderPair{"bedrock-anthropic-claude-3-sonnet", buildersBedrockBasicAuth, nil, nil}) } diff --git a/internal/fantasy/providertests/google_test.go b/internal/fantasy/providertests/google_test.go index c681411ab..ad34d41b2 100644 --- a/internal/fantasy/providertests/google_test.go +++ b/internal/fantasy/providertests/google_test.go @@ -26,6 +26,7 @@ var vertexTestModels = []testModel{ } func TestGoogleCommon(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range geminiTestModels { pairs = append(pairs, builderPair{m.name, geminiBuilder(m.model), nil, nil}) @@ -37,6 +38,7 @@ func TestGoogleCommon(t *testing.T) { } func TestGoogleThinking(t *testing.T) { + t.Parallel() opts := fantasy.ProviderOptions{ google.Name: &google.ProviderOptions{ ThinkingConfig: &google.ThinkingConfig{ @@ -57,6 +59,7 @@ func TestGoogleThinking(t *testing.T) { } func TestGoogleObjectGeneration(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range geminiTestModels { pairs = append(pairs, builderPair{m.name, geminiBuilder(m.model), nil, nil}) @@ -65,6 +68,7 @@ func TestGoogleObjectGeneration(t *testing.T) { } func TestGoogleVertexObjectGeneration(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range vertexTestModels { pairs = append(pairs, builderPair{m.name, vertexBuilder(m.model), nil, nil}) diff --git a/internal/fantasy/providertests/image_upload_test.go b/internal/fantasy/providertests/image_upload_test.go index cf7fad7ca..e875e8161 100644 --- a/internal/fantasy/providertests/image_upload_test.go +++ b/internal/fantasy/providertests/image_upload_test.go @@ -54,6 +54,7 @@ func geminiImageBuilder(model string) builderFunc { } func TestImageUploadAgent(t *testing.T) { + t.Parallel() pairs := []builderPair{ { name: "anthropic-claude-sonnet-4", @@ -100,6 +101,7 @@ func TestImageUploadAgent(t *testing.T) { } func TestImageUploadAgentStreaming(t *testing.T) { + t.Parallel() pairs := []builderPair{ { name: "anthropic-claude-sonnet-4", diff --git a/internal/fantasy/providertests/openai_responses_test.go b/internal/fantasy/providertests/openai_responses_test.go index db85f73d4..f774e6a28 100644 --- a/internal/fantasy/providertests/openai_responses_test.go +++ b/internal/fantasy/providertests/openai_responses_test.go @@ -12,6 +12,7 @@ import ( ) func TestOpenAIResponsesCommon(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range openaiTestModels { pairs = append(pairs, builderPair{m.name, openAIReasoningBuilder(m.model), nil, nil}) @@ -34,6 +35,7 @@ func openAIReasoningBuilder(model string) builderFunc { } func TestOpenAIResponsesWithSummaryThinking(t *testing.T) { + t.Parallel() opts := fantasy.ProviderOptions{ openai.Name: &openai.ResponsesProviderOptions{ Include: []openai.IncludeType{ @@ -54,6 +56,7 @@ func TestOpenAIResponsesWithSummaryThinking(t *testing.T) { } func TestOpenAIResponsesObjectGeneration(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range openaiTestModels { pairs = append(pairs, builderPair{m.name, openAIReasoningBuilder(m.model), nil, nil}) diff --git a/internal/fantasy/providertests/openai_test.go b/internal/fantasy/providertests/openai_test.go index e232ed999..ae7e94773 100644 --- a/internal/fantasy/providertests/openai_test.go +++ b/internal/fantasy/providertests/openai_test.go @@ -18,6 +18,7 @@ var openaiTestModels = []testModel{ } func TestOpenAICommon(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range openaiTestModels { pairs = append(pairs, builderPair{m.name, openAIBuilder(m.model), nil, nil}) @@ -26,6 +27,7 @@ func TestOpenAICommon(t *testing.T) { } func TestOpenAIObjectGeneration(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range openaiTestModels { pairs = append(pairs, builderPair{m.name, openAIBuilder(m.model), nil, nil}) diff --git a/internal/fantasy/providertests/openaicompat_test.go b/internal/fantasy/providertests/openaicompat_test.go index c19891345..e47b86359 100644 --- a/internal/fantasy/providertests/openaicompat_test.go +++ b/internal/fantasy/providertests/openaicompat_test.go @@ -13,6 +13,7 @@ import ( ) func TestOpenAICompatibleCommon(t *testing.T) { + t.Parallel() testCommon(t, []builderPair{ {"xai-grok-4-fast", builderXAIGrok4Fast, nil, nil}, {"xai-grok-code-fast", builderXAIGrokCodeFast, nil, nil}, @@ -24,6 +25,7 @@ func TestOpenAICompatibleCommon(t *testing.T) { } func TestOpenAICompatObjectGeneration(t *testing.T) { + t.Parallel() testObjectGeneration(t, []builderPair{ {"xai-grok-4-fast", builderXAIGrok4Fast, nil, nil}, {"xai-grok-code-fast", builderXAIGrokCodeFast, nil, nil}, @@ -32,6 +34,7 @@ func TestOpenAICompatObjectGeneration(t *testing.T) { } func TestOpenAICompatibleThinking(t *testing.T) { + t.Parallel() opts := fantasy.ProviderOptions{ openaicompat.Name: &openaicompat.ProviderOptions{ ReasoningEffort: openai.ReasoningEffortOption(openai.ReasoningEffortHigh), diff --git a/internal/fantasy/providertests/openrouter_test.go b/internal/fantasy/providertests/openrouter_test.go index 13f117c1c..b9466a47d 100644 --- a/internal/fantasy/providertests/openrouter_test.go +++ b/internal/fantasy/providertests/openrouter_test.go @@ -26,6 +26,7 @@ var openrouterTestModels = []testModel{ } func TestOpenRouterCommon(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range openrouterTestModels { pairs = append(pairs, builderPair{m.name, openrouterBuilder(m.model), nil, nil}) @@ -34,12 +35,14 @@ func TestOpenRouterCommon(t *testing.T) { } func TestOpenRouterCommonWithAnthropicCache(t *testing.T) { + t.Parallel() testCommon(t, []builderPair{ {"claude-sonnet-4", openrouterBuilder("anthropic/claude-sonnet-4"), nil, addAnthropicCaching}, }) } func TestOpenRouterThinking(t *testing.T) { + t.Parallel() opts := fantasy.ProviderOptions{ openrouter.Name: &openrouter.ProviderOptions{ Reasoning: &openrouter.ReasoningOptions{ diff --git a/internal/fantasy/providertests/provider_registry_test.go b/internal/fantasy/providertests/provider_registry_test.go index 2200af4bd..bc9e2c632 100644 --- a/internal/fantasy/providertests/provider_registry_test.go +++ b/internal/fantasy/providertests/provider_registry_test.go @@ -1,9 +1,10 @@ package providertests import ( - jsonv2 "github.com/go-json-experiment/json" "testing" + jsonv2 "github.com/go-json-experiment/json" + "charm.land/fantasy" "charm.land/fantasy/providers/anthropic" "charm.land/fantasy/providers/google" @@ -14,6 +15,7 @@ import ( ) func TestProviderRegistry_Serialization_OpenAIOptions(t *testing.T) { + t.Parallel() msg := fantasy.Message{ Role: fantasy.MessageRoleUser, Content: []fantasy.MessagePart{ @@ -52,7 +54,10 @@ func TestProviderRegistry_Serialization_OpenAIOptions(t *testing.T) { } func TestProviderRegistry_Serialization_OpenAIResponses(t *testing.T) { + t.Parallel( // Use ResponsesProviderOptions in provider options + ) + msg := fantasy.Message{ Role: fantasy.MessageRoleUser, Content: []fantasy.MessagePart{ @@ -95,6 +100,7 @@ func TestProviderRegistry_Serialization_OpenAIResponses(t *testing.T) { } func TestProviderRegistry_Serialization_OpenAIResponsesReasoningMetadata(t *testing.T) { + t.Parallel() resp := fantasy.Response{ Content: []fantasy.Content{ fantasy.TextContent{ @@ -144,6 +150,7 @@ func TestProviderRegistry_Serialization_OpenAIResponsesReasoningMetadata(t *test } func TestProviderRegistry_Serialization_AnthropicOptions(t *testing.T) { + t.Parallel() sendReasoning := true msg := fantasy.Message{ Role: fantasy.MessageRoleUser, @@ -172,6 +179,7 @@ func TestProviderRegistry_Serialization_AnthropicOptions(t *testing.T) { } func TestProviderRegistry_Serialization_GoogleOptions(t *testing.T) { + t.Parallel() msg := fantasy.Message{ Role: fantasy.MessageRoleUser, Content: []fantasy.MessagePart{ @@ -200,6 +208,7 @@ func TestProviderRegistry_Serialization_GoogleOptions(t *testing.T) { } func TestProviderRegistry_Serialization_OpenRouterOptions(t *testing.T) { + t.Parallel() includeUsage := true msg := fantasy.Message{ Role: fantasy.MessageRoleUser, @@ -231,6 +240,7 @@ func TestProviderRegistry_Serialization_OpenRouterOptions(t *testing.T) { } func TestProviderRegistry_Serialization_OpenAICompatOptions(t *testing.T) { + t.Parallel() effort := openai.ReasoningEffortHigh msg := fantasy.Message{ Role: fantasy.MessageRoleUser, @@ -262,7 +272,10 @@ func TestProviderRegistry_Serialization_OpenAICompatOptions(t *testing.T) { } func TestProviderRegistry_MultiProvider(t *testing.T) { + t.Parallel( // Test with multiple providers in one message + ) + sendReasoning := true msg := fantasy.Message{ Role: fantasy.MessageRoleUser, @@ -299,6 +312,7 @@ func TestProviderRegistry_MultiProvider(t *testing.T) { } func TestProviderRegistry_ErrorHandling(t *testing.T) { + t.Parallel() t.Run("unknown provider type", func(t *testing.T) { invalidJSON := `{ "role": "user", @@ -333,8 +347,11 @@ func TestProviderRegistry_ErrorHandling(t *testing.T) { } func TestProviderRegistry_AllTypesRegistered(t *testing.T) { + t.Parallel( // Verify all expected provider types are registered // We test that unmarshaling with proper type IDs doesn't fail with "unknown provider data type" + ) + tests := []struct { name string providerName string diff --git a/internal/fantasy/providertests/vercel_test.go b/internal/fantasy/providertests/vercel_test.go index 0361ec0ba..21e73d660 100644 --- a/internal/fantasy/providertests/vercel_test.go +++ b/internal/fantasy/providertests/vercel_test.go @@ -20,6 +20,7 @@ var vercelTestModels = []testModel{ } func TestVercelCommon(t *testing.T) { + t.Parallel() var pairs []builderPair for _, m := range vercelTestModels { pairs = append(pairs, builderPair{m.name, vercelBuilder(m.model), nil, nil}) @@ -28,12 +29,14 @@ func TestVercelCommon(t *testing.T) { } func TestVercelCommonWithAnthropicCache(t *testing.T) { + t.Parallel() testCommon(t, []builderPair{ {"claude-sonnet-4", vercelBuilder("anthropic/claude-sonnet-4"), nil, addAnthropicCaching}, }) } func TestVercelThinking(t *testing.T) { + t.Parallel() enabled := true opts := fantasy.ProviderOptions{ vercel.Name: &vercel.ProviderOptions{ diff --git a/internal/fantasy/react_fsm_test.go b/internal/fantasy/react_fsm_test.go index 87bd3e75d..a2918fd47 100644 --- a/internal/fantasy/react_fsm_test.go +++ b/internal/fantasy/react_fsm_test.go @@ -47,9 +47,10 @@ func driveGeneratePath(ctx context.Context, f *reactFSM) { // TestFSM_Start verifies the FSM transitions from Init to PrepareStep on Start. func TestFSM_Start(t *testing.T) { + t.Parallel() obs := &captureObserver{} f, _ := newTestFSM(t, obs) - ctx := context.Background() + ctx := t.Context() f.Fire(ctx, ReActTriggerStart) @@ -63,9 +64,10 @@ func TestFSM_Start(t *testing.T) { // TestFSM_FullHappyPath drives one complete step through all states and // ends in Done via Finished. func TestFSM_FullHappyPath(t *testing.T) { + t.Parallel() obs := &captureObserver{} f, _ := newTestFSM(t, obs) - ctx := context.Background() + ctx := t.Context() f.Fire(ctx, ReActTriggerStart) driveGeneratePath(ctx, f) @@ -81,9 +83,10 @@ func TestFSM_FullHappyPath(t *testing.T) { // TestFSM_Continue verifies that the loop can re-enter PrepareStep after a // tool-call step. func TestFSM_Continue(t *testing.T) { + t.Parallel() obs := &captureObserver{} f, _ := newTestFSM(t, obs) - ctx := context.Background() + ctx := t.Context() f.Fire(ctx, ReActTriggerStart) driveGeneratePath(ctx, f) @@ -101,9 +104,10 @@ func TestFSM_Continue(t *testing.T) { // TestFSM_StopConditionMet verifies the alternative Done path. func TestFSM_StopConditionMet(t *testing.T) { + t.Parallel() obs := &captureObserver{} f, _ := newTestFSM(t, obs) - ctx := context.Background() + ctx := t.Context() f.Fire(ctx, ReActTriggerStart) driveGeneratePath(ctx, f) @@ -115,9 +119,10 @@ func TestFSM_StopConditionMet(t *testing.T) { // TestFSM_ErrorTransition verifies the error state is reachable from any state. func TestFSM_ErrorTransition(t *testing.T) { + t.Parallel() obs := &captureObserver{} f, _ := newTestFSM(t, obs) - ctx := context.Background() + ctx := t.Context() f.Fire(ctx, ReActTriggerStart) f.Fire(ctx, ReActTriggerPrepared) @@ -130,9 +135,10 @@ func TestFSM_ErrorTransition(t *testing.T) { // TestFSM_RecoveredContinue verifies the error → PrepareStep recovery path. func TestFSM_RecoveredContinue(t *testing.T) { + t.Parallel() obs := &captureObserver{} f, _ := newTestFSM(t, obs) - ctx := context.Background() + ctx := t.Context() f.Fire(ctx, ReActTriggerStart) f.Fire(ctx, ReActTriggerErrored) // error before prepared @@ -146,8 +152,9 @@ func TestFSM_RecoveredContinue(t *testing.T) { // TestFSM_UnhandledTriggerIsPermissive verifies that firing an invalid trigger // from a given state does NOT return an error (permissive design). func TestFSM_UnhandledTriggerIsPermissive(t *testing.T) { + t.Parallel() f, _ := newTestFSM(t, nil) - ctx := context.Background() + ctx := t.Context() // From Init, firing Finished is not a permitted transition. // The FSM must silently ignore it (no panic, no error). @@ -159,8 +166,9 @@ func TestFSM_UnhandledTriggerIsPermissive(t *testing.T) { // TestFSM_TransitionLog verifies the log accumulates correctly and Snapshot // returns a copy. func TestFSM_TransitionLog(t *testing.T) { + t.Parallel() f, _ := newTestFSM(t, nil) - ctx := context.Background() + ctx := t.Context() f.Fire(ctx, ReActTriggerStart) f.Fire(ctx, ReActTriggerPrepared) @@ -177,10 +185,11 @@ func TestFSM_TransitionLog(t *testing.T) { // TestFSM_StepIndex verifies that the step index embedded in transitions // reflects the pointer value at emission time. func TestFSM_StepIndex(t *testing.T) { + t.Parallel() idx := 0 obs := &captureObserver{} f := newReActFSM(obs, &idx) - ctx := context.Background() + ctx := t.Context() f.Fire(ctx, ReActTriggerStart) // stepIndex = 0 idx = 1 @@ -194,8 +203,9 @@ func TestFSM_StepIndex(t *testing.T) { // TestFSM_NilObserverSafe verifies no panic when no observer is attached. func TestFSM_NilObserverSafe(t *testing.T) { + t.Parallel() f, _ := newTestFSM(t, nil) - ctx := context.Background() + ctx := t.Context() assert.NotPanics(t, func() { f.Fire(ctx, ReActTriggerStart) @@ -206,6 +216,7 @@ func TestFSM_NilObserverSafe(t *testing.T) { // TestReActTransitionLog_ConcurrentAppend verifies the log is safe under // concurrent writes. func TestReActTransitionLog_ConcurrentAppend(t *testing.T) { + t.Parallel() log := NewReActTransitionLog() const n = 100 var wg sync.WaitGroup diff --git a/internal/fantasy/schema/schema_test.go b/internal/fantasy/schema/schema_test.go index 8b48194ec..fdfbfb8c5 100644 --- a/internal/fantasy/schema/schema_test.go +++ b/internal/fantasy/schema/schema_test.go @@ -8,7 +8,10 @@ import ( ) func TestEnumSupport(t *testing.T) { + t.Parallel( // Test enum via struct tags + ) + type WeatherInput struct { Location string `json:"location" description:"City name"` Units string `json:"units" enum:"celsius,fahrenheit,kelvin" description:"Temperature units"` @@ -34,6 +37,7 @@ func TestEnumSupport(t *testing.T) { } func TestSchemaToParameters(t *testing.T) { + t.Parallel() testSchema := Schema{ Type: "object", Properties: map[string]*Schema{ diff --git a/internal/fantasy/tool_runtime_dag_test.go b/internal/fantasy/tool_runtime_dag_test.go index ffbc17168..c5e15d090 100644 --- a/internal/fantasy/tool_runtime_dag_test.go +++ b/internal/fantasy/tool_runtime_dag_test.go @@ -45,7 +45,7 @@ func TestDAGToolRuntime_IndependentToolsRunConcurrently(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - res, err = rt.Execute(context.Background(), []AgentTool{tool}, toolCalls, nil) + res, err = rt.Execute(t.Context(), []AgentTool{tool}, toolCalls, nil) }() // Both tools should start before we release. @@ -100,7 +100,7 @@ func TestDAGToolRuntime_DependenciesWaitAndInputIsResolved(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - res, err = rt.Execute(context.Background(), []AgentTool{toolA, toolB}, toolCalls, nil) + res, err = rt.Execute(t.Context(), []AgentTool{toolA, toolB}, toolCalls, nil) }() <-startA @@ -138,7 +138,7 @@ func TestDAGToolRuntime_CycleDetected(t *testing.T) { {ToolCallID: "b", ToolName: "p", Input: `{"x":"$tool.a"}`}, } - res, err := rt.Execute(context.Background(), []AgentTool{tool}, toolCalls, nil) + res, err := rt.Execute(t.Context(), []AgentTool{tool}, toolCalls, nil) require.Error(t, err) require.Nil(t, res) } @@ -172,7 +172,7 @@ func TestDAGToolRuntime_OnToolResultSerialized(t *testing.T) { return nil } - res, err := rt.Execute(context.Background(), []AgentTool{tool}, toolCalls, cb) + res, err := rt.Execute(t.Context(), []AgentTool{tool}, toolCalls, cb) require.NoError(t, err) require.Len(t, res, 2) @@ -206,7 +206,7 @@ func TestDAGToolRuntime_MetricsAndLogHooks(t *testing.T) { toolCalls := []ToolCallContent{ {ToolCallID: "a", ToolName: "p", Input: `{}`}, } - res, err := rt.Execute(context.Background(), []AgentTool{tool}, toolCalls, nil) + res, err := rt.Execute(t.Context(), []AgentTool{tool}, toolCalls, nil) require.NoError(t, err) require.Len(t, res, 1) require.True(t, metricsCalled) diff --git a/internal/fantasy/tool_runtime_parallel_test.go b/internal/fantasy/tool_runtime_parallel_test.go index 977a0ba85..7e5c429ce 100644 --- a/internal/fantasy/tool_runtime_parallel_test.go +++ b/internal/fantasy/tool_runtime_parallel_test.go @@ -46,7 +46,7 @@ func TestParallelToolRuntime_OrderAndCallbackDeterminism(t *testing.T) { {ToolCallID: "c3", ToolName: "p", Input: `{"delay_ms":30,"value":"c"}`}, } - results, err := runtime.Execute(context.Background(), []AgentTool{tool}, toolCalls, cb) + results, err := runtime.Execute(t.Context(), []AgentTool{tool}, toolCalls, cb) require.NoError(t, err) require.Len(t, results, 3) @@ -92,7 +92,7 @@ func TestParallelToolRuntime_BarrierForNonParallelTools(t *testing.T) { {ToolCallID: "p3", ToolName: "p", Input: `{}`}, } - results, err := runtime.Execute(context.Background(), []AgentTool{parallel, seq}, toolCalls, cb) + results, err := runtime.Execute(t.Context(), []AgentTool{parallel, seq}, toolCalls, cb) require.NoError(t, err) require.Len(t, results, 4) @@ -118,7 +118,7 @@ func TestParallelToolRuntime_CriticalErrorPropagation(t *testing.T) { {ToolCallID: "bad", ToolName: "p", Input: `{}`}, } - results, err := runtime.Execute(context.Background(), []AgentTool{tool}, toolCalls, nil) + results, err := runtime.Execute(t.Context(), []AgentTool{tool}, toolCalls, nil) require.Error(t, err) require.Nil(t, results) } @@ -148,7 +148,7 @@ func TestParallelToolRuntime_MetricsAndLogHooks(t *testing.T) { toolCalls := []ToolCallContent{ {ToolCallID: "a", ToolName: "p", Input: `{}`}, } - res, err := rt.Execute(context.Background(), []AgentTool{tool}, toolCalls, nil) + res, err := rt.Execute(t.Context(), []AgentTool{tool}, toolCalls, nil) require.NoError(t, err) require.Len(t, res, 1) require.True(t, metricsCalled) diff --git a/internal/fantasy/tool_test.go b/internal/fantasy/tool_test.go index 7567c7cd4..4763c462a 100644 --- a/internal/fantasy/tool_test.go +++ b/internal/fantasy/tool_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" ) @@ -14,7 +15,8 @@ type CalculatorInput struct { } func TestTypedToolFuncExample(t *testing.T) { - // Create a typed tool using the function API + t.Parallel() + tool := NewAgentTool( "calculator", "Evaluates simple mathematical expressions", @@ -39,13 +41,14 @@ func TestTypedToolFuncExample(t *testing.T) { Input: `{"expression": "2+2"}`, } - result, err := tool.Run(context.Background(), call) + result, err := tool.Run(t.Context(), call) require.NoError(t, err) require.Equal(t, "4", result.Content) require.False(t, result.IsError) } func TestEnumToolExample(t *testing.T) { + t.Parallel() type WeatherInput struct { Location string `json:"location" description:"City name"` Units string `json:"units" enum:"celsius,fahrenheit" description:"Temperature units"` @@ -78,34 +81,51 @@ func TestEnumToolExample(t *testing.T) { Input: `{"location": "San Francisco", "units": "fahrenheit"}`, } - result, err := tool.Run(context.Background(), call) + result, err := tool.Run(t.Context(), call) require.NoError(t, err) require.Contains(t, result.Content, "San Francisco") require.Contains(t, result.Content, "72°F") } -func TestNewImageResponse(t *testing.T) { - imageData := []byte{0x89, 0x50, 0x4E, 0x47} // PNG header bytes - mediaType := "image/png" - - resp := NewImageResponse(imageData, mediaType) - - require.Equal(t, "image", resp.Type) - require.Equal(t, imageData, resp.Data) - require.Equal(t, mediaType, resp.MediaType) - require.False(t, resp.IsError) - require.Empty(t, resp.Content) -} - func TestNewMediaResponse(t *testing.T) { - audioData := []byte{0x52, 0x49, 0x46, 0x46} // RIFF header bytes - mediaType := "audio/wav" + t.Parallel() + tests := []struct { + name string + mediaType string + data []byte + wantType string + }{ + { + name: "image response", + data: []byte{0x89, 0x50, 0x4E, 0x47}, + mediaType: "image/png", + wantType: "image", + }, + { + name: "audio response", + data: []byte{0x52, 0x49, 0x46, 0x46}, + mediaType: "audio/wav", + wantType: "media", + }, + } - resp := NewMediaResponse(audioData, mediaType) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var got ToolResponse + if tt.wantType == "image" { + got = NewImageResponse(tt.data, tt.mediaType) + } else { + got = NewMediaResponse(tt.data, tt.mediaType) + } - require.Equal(t, "media", resp.Type) - require.Equal(t, audioData, resp.Data) - require.Equal(t, mediaType, resp.MediaType) - require.False(t, resp.IsError) - require.Empty(t, resp.Content) + require.Empty(t, cmp.Diff(ToolResponse{ + Type: tt.wantType, + Data: tt.data, + MediaType: tt.mediaType, + IsError: false, + Content: "", + }, got)) + }) + } } diff --git a/pkg/agent/integration_test.go b/pkg/agent/integration_test.go index 86a03b19a..05f14aa98 100644 --- a/pkg/agent/integration_test.go +++ b/pkg/agent/integration_test.go @@ -59,8 +59,8 @@ func (m *toolCallingModel) Generate(_ context.Context, call fantasy.Call) (*fant }, nil } -func (m *toolCallingModel) Stream(_ context.Context, call fantasy.Call) (fantasy.StreamResponse, error) { - resp, err := m.Generate(context.Background(), call) +func (m *toolCallingModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.StreamResponse, error) { + resp, err := m.Generate(ctx, call) if err != nil { return nil, err } @@ -162,6 +162,7 @@ func (t *echoTool) Execute(_ context.Context, args map[string]interface{}) *tool // TestIntegration_FullAgentLoop_SimpleResponse tests the full agent loop // with a simple mock model that returns text directly (no tool calls). func TestIntegration_FullAgentLoop_SimpleResponse(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-integration-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -183,7 +184,7 @@ func TestIntegration_FullAgentLoop_SimpleResponse(t *testing.T) { model := newMockLanguageModel("Hello from Fantasy agent") al := mustNewAgentLoop(t, cfg, msgBus, model) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) defer cancel() msg := bus.InboundMessage{ @@ -225,6 +226,7 @@ func TestIntegration_FullAgentLoop_SimpleResponse(t *testing.T) { // TestIntegration_FullAgentLoop_WithToolCalls tests the full agent loop // including tool call execution and response incorporation. func TestIntegration_FullAgentLoop_WithToolCalls(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-integration-tools-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -249,7 +251,7 @@ func TestIntegration_FullAgentLoop_WithToolCalls(t *testing.T) { // Register the echo tool al.RegisterTool(&echoTool{}) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) defer cancel() msg := bus.InboundMessage{ @@ -279,6 +281,7 @@ func TestIntegration_FullAgentLoop_WithToolCalls(t *testing.T) { // TestIntegration_ProcessDirect tests the ProcessDirect method // which is used by CLI mode for one-shot message processing. func TestIntegration_ProcessDirect(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-integration-direct-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -300,7 +303,7 @@ func TestIntegration_ProcessDirect(t *testing.T) { model := newMockLanguageModel("Direct CLI response") al := mustNewAgentLoop(t, cfg, msgBus, model) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) defer cancel() response, err := al.ProcessDirect(ctx, "Direct message", "direct-session") @@ -374,6 +377,7 @@ func (m *streamingModel) Model() string { return "streaming-mock" } // TestIntegration_Streaming_TextDeltas tests that the streaming agent loop // publishes text deltas to the bus and returns the complete text. func TestIntegration_Streaming_TextDeltas(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-integration-stream-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -395,7 +399,7 @@ func TestIntegration_Streaming_TextDeltas(t *testing.T) { model := newStreamingModel("Hello from streaming agent response") al := mustNewAgentLoop(t, cfg, msgBus, model) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) defer cancel() // Collect stream deltas in background @@ -450,6 +454,7 @@ func TestIntegration_Streaming_TextDeltas(t *testing.T) { // TestIntegration_Streaming_WithToolCalls tests streaming with a model // that requests tool calls before producing a final streamed response. func TestIntegration_Streaming_WithToolCalls(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-integration-stream-tools-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -472,7 +477,7 @@ func TestIntegration_Streaming_WithToolCalls(t *testing.T) { al := mustNewAgentLoop(t, cfg, msgBus, model) al.RegisterTool(&echoTool{}) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) defer cancel() // Collect deltas @@ -512,6 +517,7 @@ func TestIntegration_Streaming_WithToolCalls(t *testing.T) { // TestIntegration_MultipleMessages tests sequential message processing // to verify session history accumulation. func TestIntegration_MultipleMessages(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-integration-multi-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -534,7 +540,7 @@ func TestIntegration_MultipleMessages(t *testing.T) { al := mustNewAgentLoop(t, cfg, msgBus, model) sessionKey := "multi-msg-session" - ctx := context.Background() + ctx := t.Context() // Send 3 messages for i := 0; i < 3; i++ { diff --git a/pkg/agent/kv_delegate_test.go b/pkg/agent/kv_delegate_test.go index b25bdd6f8..4af889f1a 100644 --- a/pkg/agent/kv_delegate_test.go +++ b/pkg/agent/kv_delegate_test.go @@ -1,7 +1,6 @@ package agent_test import ( - "context" "testing" "github.com/stretchr/testify/assert" @@ -17,8 +16,9 @@ func newDelegateKV(t *testing.T, agentID string) *agent.DelegateKV { } func TestDelegateKV_PutAndGet(t *testing.T) { + t.Parallel() kv := newDelegateKV(t, "agent-1") - ctx := context.Background() + ctx := t.Context() require.NoError(t, kv.Put(ctx, "key1", []byte("hello world"))) @@ -28,8 +28,9 @@ func TestDelegateKV_PutAndGet(t *testing.T) { } func TestDelegateKV_GetMissingKey(t *testing.T) { + t.Parallel() kv := newDelegateKV(t, "agent-1") - ctx := context.Background() + ctx := t.Context() got, err := kv.Get(ctx, "nonexistent") require.NoError(t, err) @@ -37,8 +38,9 @@ func TestDelegateKV_GetMissingKey(t *testing.T) { } func TestDelegateKV_PutOverwrite(t *testing.T) { + t.Parallel() kv := newDelegateKV(t, "agent-1") - ctx := context.Background() + ctx := t.Context() require.NoError(t, kv.Put(ctx, "k", []byte("v1"))) require.NoError(t, kv.Put(ctx, "k", []byte("v2"))) @@ -49,8 +51,9 @@ func TestDelegateKV_PutOverwrite(t *testing.T) { } func TestDelegateKV_BinaryValues(t *testing.T) { + t.Parallel() kv := newDelegateKV(t, "agent-bin") - ctx := context.Background() + ctx := t.Context() // Include bytes that need base64 encoding (null bytes, high bytes) data := []byte{0x00, 0xFF, 0x1F, 0x7E, 0x80, 0xAB} @@ -62,8 +65,9 @@ func TestDelegateKV_BinaryValues(t *testing.T) { } func TestDelegateKV_Scan(t *testing.T) { + t.Parallel() kv := newDelegateKV(t, "agent-scan") - ctx := context.Background() + ctx := t.Context() keys := []string{"prefix/a", "prefix/b", "prefix/c", "other/x"} for _, k := range keys { @@ -76,8 +80,9 @@ func TestDelegateKV_Scan(t *testing.T) { } func TestDelegateKV_ScanEmpty(t *testing.T) { + t.Parallel() kv := newDelegateKV(t, "agent-scan-empty") - ctx := context.Background() + ctx := t.Context() got, err := kv.Scan(ctx, "nothing/") require.NoError(t, err) @@ -85,8 +90,9 @@ func TestDelegateKV_ScanEmpty(t *testing.T) { } func TestDelegateKV_ScanSorted(t *testing.T) { + t.Parallel() kv := newDelegateKV(t, "agent-sorted") - ctx := context.Background() + ctx := t.Context() // Insert out of order for _, k := range []string{"z/c", "z/a", "z/b"} { @@ -99,10 +105,11 @@ func TestDelegateKV_ScanSorted(t *testing.T) { } func TestDelegateKV_AgentIsolation(t *testing.T) { + t.Parallel() db := newTestQueries(t) kv1 := agent.NewDelegateKV(db.delegate, "agent-A") kv2 := agent.NewDelegateKV(db.delegate, "agent-B") - ctx := context.Background() + ctx := t.Context() require.NoError(t, kv1.Put(ctx, "shared-key", []byte("from-A"))) require.NoError(t, kv2.Put(ctx, "shared-key", []byte("from-B"))) @@ -117,13 +124,15 @@ func TestDelegateKV_AgentIsolation(t *testing.T) { } func TestDelegateKV_EmptyKey_PutErrors(t *testing.T) { + t.Parallel() kv := newDelegateKV(t, "agent-1") - err := kv.Put(context.Background(), "", []byte("val")) + err := kv.Put(t.Context(), "", []byte("val")) assert.Error(t, err, "empty key should be rejected") } func TestDelegateKV_EmptyKey_GetErrors(t *testing.T) { + t.Parallel() kv := newDelegateKV(t, "agent-1") - _, err := kv.Get(context.Background(), "") + _, err := kv.Get(t.Context(), "") assert.Error(t, err, "empty key should be rejected") } diff --git a/pkg/agent/loop_test.go b/pkg/agent/loop_test.go index 11d9410cd..d3341c7ee 100644 --- a/pkg/agent/loop_test.go +++ b/pkg/agent/loop_test.go @@ -26,7 +26,7 @@ func mustNewAgentLoop(t *testing.T, cfg *config.Config, msgBus *bus.MessageBus, if cfg != nil && cfg.Memory.DBPath == "" && strings.TrimSpace(cfg.Agents.Defaults.Workspace) != "" { cfg.Memory.DBPath = filepath.Join(cfg.Agents.Defaults.Workspace, "agent-loop-test.db") } - al, err := NewAgentLoop(context.Background(), cfg, msgBus, model) + al, err := NewAgentLoop(t.Context(), cfg, msgBus, model) if err != nil { t.Fatalf("NewAgentLoop: %v", err) } @@ -73,6 +73,7 @@ func (m *mockLanguageModel) Provider() string { return "mock" } func (m *mockLanguageModel) Model() string { return "mock-model" } func TestContinuityKeepCount_UsesConfiguredPolicy(t *testing.T) { + t.Parallel() buildHistory := func(n int, content string) []messages.Message { history := make([]messages.Message, 0, n) for i := 0; i < n; i++ { @@ -113,6 +114,7 @@ func TestContinuityKeepCount_UsesConfiguredPolicy(t *testing.T) { } func TestPrepareRuntimeState_ConcurrentSameSessionUsesSingleConversation(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -133,7 +135,7 @@ func TestPrepareRuntimeState_ConcurrentSameSessionUsesSingleConversation(t *test msgBus := bus.NewMessageBus() model := newMockLanguageModel("") al := mustNewAgentLoop(t, cfg, msgBus, model) - beforeConversations, err := al.queries.ListAgentConversations(context.Background(), memsqlc.ListAgentConversationsParams{ + beforeConversations, err := al.queries.ListAgentConversations(t.Context(), memsqlc.ListAgentConversationsParams{ Limit: 10000, }) if err != nil { @@ -152,7 +154,7 @@ func TestPrepareRuntimeState_ConcurrentSameSessionUsesSingleConversation(t *test go func() { defer wg.Done() <-start - conversationID, _, prepareErr := al.prepareRuntimeState(context.Background(), "race-session") + conversationID, _, prepareErr := al.prepareRuntimeState(t.Context(), "race-session") if prepareErr != nil { errorsCh <- prepareErr return @@ -180,7 +182,7 @@ func TestPrepareRuntimeState_ConcurrentSameSessionUsesSingleConversation(t *test t.Fatalf("expected one conversation id, got %d (%v)", len(uniqueConversationIDs), uniqueConversationIDs) } - conversations, err := al.queries.ListAgentConversations(context.Background(), memsqlc.ListAgentConversationsParams{ + conversations, err := al.queries.ListAgentConversations(t.Context(), memsqlc.ListAgentConversationsParams{ Limit: 10000, }) if err != nil { @@ -192,7 +194,10 @@ func TestPrepareRuntimeState_ConcurrentSameSessionUsesSingleConversation(t *test } func TestRecordLastChannel(t *testing.T) { + t.Parallel( // Create temp workspace + ) + tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -218,7 +223,7 @@ func TestRecordLastChannel(t *testing.T) { // Test RecordLastChannel testChannel := "test-channel" - err = al.RecordLastChannel(context.Background(), testChannel) + err = al.RecordLastChannel(t.Context(), testChannel) if err != nil { t.Fatalf("RecordLastChannel failed: %v", err) } @@ -237,7 +242,10 @@ func TestRecordLastChannel(t *testing.T) { } func TestRecordLastChatID(t *testing.T) { + t.Parallel( // Create temp workspace + ) + tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -263,7 +271,7 @@ func TestRecordLastChatID(t *testing.T) { // Test RecordLastChatID testChatID := "test-chat-id-123" - err = al.RecordLastChatID(context.Background(), testChatID) + err = al.RecordLastChatID(t.Context(), testChatID) if err != nil { t.Fatalf("RecordLastChatID failed: %v", err) } @@ -282,7 +290,10 @@ func TestRecordLastChatID(t *testing.T) { } func TestNewAgentLoop_StateInitialized(t *testing.T) { + t.Parallel( // Create temp workspace + ) + tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -313,6 +324,7 @@ func TestNewAgentLoop_StateInitialized(t *testing.T) { } func TestNewAgentLoop_UnifiedKernelDependenciesInitialized(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -344,6 +356,7 @@ func TestNewAgentLoop_UnifiedKernelDependenciesInitialized(t *testing.T) { // TestToolRegistry_ToolRegistration verifies tools can be registered and retrieved func TestToolRegistry_ToolRegistration(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -390,6 +403,7 @@ func TestToolRegistry_ToolRegistration(t *testing.T) { // TestToolContext_Updates verifies tool context is updated with channel/chatID func TestToolContext_Updates(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -421,6 +435,7 @@ func TestToolContext_Updates(t *testing.T) { // TestToolRegistry_GetDefinitions verifies tool definitions can be retrieved func TestToolRegistry_GetDefinitions(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -465,6 +480,7 @@ func TestToolRegistry_GetDefinitions(t *testing.T) { // TestAgentLoop_GetStartupInfo verifies startup info contains tools func TestAgentLoop_GetStartupInfo(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -512,6 +528,7 @@ func TestAgentLoop_GetStartupInfo(t *testing.T) { // TestAgentLoop_Stop verifies Stop() sets running to false func TestAgentLoop_Stop(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -619,6 +636,7 @@ const responseTimeout = 3 * time.Second // TestToolResult_SilentToolDoesNotSendUserMessage verifies silent tools don't trigger outbound func TestToolResult_SilentToolDoesNotSendUserMessage(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -642,7 +660,7 @@ func TestToolResult_SilentToolDoesNotSendUserMessage(t *testing.T) { helper := testHelper{al: al} // ReadFileTool returns SilentResult, which should not send user message - ctx := context.Background() + ctx := t.Context() msg := bus.InboundMessage{ Channel: "test", SenderID: "user1", @@ -661,6 +679,7 @@ func TestToolResult_SilentToolDoesNotSendUserMessage(t *testing.T) { // TestToolResult_UserFacingToolDoesSendMessage verifies user-facing tools trigger outbound func TestToolResult_UserFacingToolDoesSendMessage(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -684,7 +703,7 @@ func TestToolResult_UserFacingToolDoesSendMessage(t *testing.T) { helper := testHelper{al: al} // ExecTool returns UserResult, which should send user message - ctx := context.Background() + ctx := t.Context() msg := bus.InboundMessage{ Channel: "test", SenderID: "user1", @@ -702,6 +721,7 @@ func TestToolResult_UserFacingToolDoesSendMessage(t *testing.T) { } func TestResolveFinalContent_RecoversFromPriorStepText(t *testing.T) { + t.Parallel() al := &AgentLoop{} steps := []fantasy.StepResult{ { @@ -730,6 +750,7 @@ func TestResolveFinalContent_RecoversFromPriorStepText(t *testing.T) { } func TestResolveFinalContent_ErrorsWhenNoTextExists(t *testing.T) { + t.Parallel() al := &AgentLoop{} steps := []fantasy.StepResult{ { @@ -748,6 +769,7 @@ func TestResolveFinalContent_ErrorsWhenNoTextExists(t *testing.T) { } func TestResolveFinalContent_RecoversFromToolResultText(t *testing.T) { + t.Parallel() al := &AgentLoop{} steps := []fantasy.StepResult{ { @@ -776,6 +798,7 @@ func TestResolveFinalContent_RecoversFromToolResultText(t *testing.T) { // TestForceCompression_PersistsProvenance verifies that emergency compression // cycles persist provenance metadata to the audit log for postmortem. func TestForceCompression_PersistsProvenance(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-provenance-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -827,7 +850,7 @@ func TestForceCompression_PersistsProvenance(t *testing.T) { t.Fatalf("test precondition failed: token_estimate=%d threshold=%d", tokenEstimate, criticalThreshold) } - ctx := context.Background() + ctx := t.Context() al.forceCompression(ctx, sessionKey, "", "") del := al.MemoryDelegate() @@ -872,6 +895,7 @@ func TestForceCompression_PersistsProvenance(t *testing.T) { } func TestPersistOversizedRecoveryRefs_CreatesRecoverableReferences(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "agent-recovery-ref-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -904,7 +928,7 @@ func TestPersistOversizedRecoveryRefs_CreatesRecoverableReferences(t *testing.T) }, } - refs, err := al.persistOversizedRecoveryRefs(context.Background(), "recovery-session", omitted) + refs, err := al.persistOversizedRecoveryRefs(t.Context(), "recovery-session", omitted) if err != nil { t.Fatalf("persistOversizedRecoveryRefs failed: %v", err) } @@ -919,7 +943,7 @@ func TestPersistOversizedRecoveryRefs_CreatesRecoverableReferences(t *testing.T) return "recovery-session" }, }) - res := dagTool.Execute(context.Background(), map[string]interface{}{ + res := dagTool.Execute(t.Context(), map[string]interface{}{ "node_id": refs[0], "session_key": "recovery-session", }) diff --git a/pkg/agent/offloading_runtime_test.go b/pkg/agent/offloading_runtime_test.go index fe9d42d66..fea02828e 100644 --- a/pkg/agent/offloading_runtime_test.go +++ b/pkg/agent/offloading_runtime_test.go @@ -40,7 +40,7 @@ func makeOffloader(t *testing.T, base fantasy.ToolRuntime, threshold, chunkChars q := db.delegate.Queries() convID := newConversation(t, q) s := agent.NewStateStore(q) - run, err := s.CreateRun(context.Background(), convID) + run, err := s.CreateRun(t.Context(), convID) require.NoError(t, err) kv := agent.NewDelegateKV(db.delegate, "offload-test") @@ -76,8 +76,9 @@ func makeCalls(n int) []fantasy.ToolCallContent { // TestOffloading_SmallResult_KeptInline verifies that results below the // threshold are stored in KV but the inline value is unchanged. func TestOffloading_SmallResult_KeptInline(t *testing.T) { + t.Parallel() r, _, _, kv := makeOffloader(t, nil, 1000, 500) - ctx := context.Background() + ctx := t.Context() calls := makeCalls(1) results, err := r.Execute(ctx, nil, calls, nil) @@ -95,6 +96,7 @@ func TestOffloading_SmallResult_KeptInline(t *testing.T) { // TestOffloading_LargeResult_Truncated verifies that results above the // threshold are truncated inline and chunked in KV. func TestOffloading_LargeResult_Truncated(t *testing.T) { + t.Parallel() longText := strings.Repeat("x", 200) base := staticToolRuntime{results: []fantasy.ToolResultContent{ @@ -106,7 +108,7 @@ func TestOffloading_LargeResult_Truncated(t *testing.T) { }} r, _, _, kv := makeOffloader(t, base, 50, 30) - ctx := context.Background() + ctx := t.Context() calls := makeCalls(1) calls[0].ToolCallID = "call-a" @@ -149,8 +151,9 @@ func TestOffloading_LargeResult_Truncated(t *testing.T) { // TestOffloading_DBMetadata_Inserted verifies that the DB record is created. func TestOffloading_DBMetadata_Inserted(t *testing.T) { + t.Parallel() r, convIDPtr, runIDPtr, _ := makeOffloader(t, nil, 1000, 500) - ctx := context.Background() + ctx := t.Context() calls := makeCalls(2) _, err := r.Execute(ctx, nil, calls, nil) @@ -165,11 +168,12 @@ func TestOffloading_DBMetadata_Inserted(t *testing.T) { // TestOffloading_NilKV_Errors verifies that a nil KVDelegate returns an error. func TestOffloading_NilKV_Errors(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() convID := newConversation(t, q) s := agent.NewStateStore(q) - run, err := s.CreateRun(context.Background(), convID) + run, err := s.CreateRun(t.Context(), convID) require.NoError(t, err) r := &agent.OffloadingToolRuntime{ @@ -178,12 +182,13 @@ func TestOffloading_NilKV_Errors(t *testing.T) { RunID: run.ID, } - _, err = r.Execute(context.Background(), nil, makeCalls(1), nil) + _, err = r.Execute(t.Context(), nil, makeCalls(1), nil) assert.Error(t, err, "nil KV should fail") } // TestOffloading_NilQueries_Errors verifies that a nil Queries returns an error. func TestOffloading_NilQueries_Errors(t *testing.T) { + t.Parallel() db := newTestQueries(t) kv := agent.NewDelegateKV(db.delegate, "a") @@ -193,15 +198,16 @@ func TestOffloading_NilQueries_Errors(t *testing.T) { RunID: ids.New(), } - _, err := r.Execute(context.Background(), nil, makeCalls(1), nil) + _, err := r.Execute(t.Context(), nil, makeCalls(1), nil) assert.Error(t, err, "nil queries should fail") } // TestOffloading_EmptyToolCalls_ReturnsNil verifies no work is done when // no tool calls are provided. func TestOffloading_EmptyToolCalls_ReturnsNil(t *testing.T) { + t.Parallel() r, _, _, _ := makeOffloader(t, nil, 1000, 500) - results, err := r.Execute(context.Background(), nil, nil, nil) + results, err := r.Execute(t.Context(), nil, nil, nil) require.NoError(t, err) assert.Nil(t, results, "no tool calls should produce no results") } @@ -209,8 +215,9 @@ func TestOffloading_EmptyToolCalls_ReturnsNil(t *testing.T) { // TestOffloading_MultipleCalls_AllStoredInKV verifies that each call gets // its own full-result KV entry. func TestOffloading_MultipleCalls_AllStoredInKV(t *testing.T) { + t.Parallel() r, _, _, kv := makeOffloader(t, nil, 1000, 500) - ctx := context.Background() + ctx := t.Context() calls := makeCalls(3) _, err := r.Execute(ctx, nil, calls, nil) @@ -230,6 +237,7 @@ func TestOffloading_MultipleCalls_AllStoredInKV(t *testing.T) { // TestChunkString verifies the internal chunking logic boundary conditions. func TestChunkString(t *testing.T) { + t.Parallel() tests := []struct { name string input string @@ -268,10 +276,10 @@ func TestChunkString(t *testing.T) { r, _, _, kv := makeOffloader(t, base, effectiveThreshold, tt.chunkSize) calls := []fantasy.ToolCallContent{{ToolCallID: "c", ToolName: "t"}} - _, err := r.Execute(context.Background(), nil, calls, nil) + _, err := r.Execute(t.Context(), nil, calls, nil) require.NoError(t, err) - keys, err := kv.Scan(context.Background(), "tool_results/") + keys, err := kv.Scan(t.Context(), "tool_results/") require.NoError(t, err) chunkCount := 0 diff --git a/pkg/agent/state_store_test.go b/pkg/agent/state_store_test.go index a1a8a8f95..aa5967d2c 100644 --- a/pkg/agent/state_store_test.go +++ b/pkg/agent/state_store_test.go @@ -1,11 +1,12 @@ package agent_test import ( - "context" "testing" "time" "charm.land/fantasy" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -19,7 +20,7 @@ func newTestQueries(t *testing.T) *testDB { t.Helper() d, err := delegate.NewLibSQLInMemory() require.NoError(t, err, "NewLibSQLInMemory") - require.NoError(t, d.Init(context.Background()), "Init") + require.NoError(t, d.Init(t.Context()), "Init") t.Cleanup(func() { _ = d.Close() }) return &testDB{delegate: d} } @@ -36,7 +37,7 @@ func newConversation(t *testing.T, q *sqlc.Queries) ids.UUID { t.Helper() id := ids.New() title := "test-conv" - _, err := q.CreateAgentConversation(context.Background(), sqlc.CreateAgentConversationParams{ + _, err := q.CreateAgentConversation(t.Context(), sqlc.CreateAgentConversationParams{ ID: id, Title: &title, }) @@ -45,34 +46,39 @@ func newConversation(t *testing.T, q *sqlc.Queries) ids.UUID { } func TestStateStore_CreateRun(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() s := agent.NewStateStore(q) - ctx := context.Background() + ctx := t.Context() convID := newConversation(t, q) run, err := s.CreateRun(ctx, convID) require.NoError(t, err) assert.False(t, run.ID.IsZero(), "run ID must be set") - assert.Equal(t, convID, run.ConversationID) - assert.Equal(t, "running", run.Status) + assert.Empty(t, cmp.Diff(sqlc.AgentRun{ + ConversationID: convID, + Status: "running", + }, run, cmpopts.IgnoreFields(sqlc.AgentRun{}, "ID", "MetadataJson", "CreatedAt", "UpdatedAt"))) } func TestStateStore_CreateRun_ZeroConversationID(t *testing.T) { + t.Parallel() db := newTestQueries(t) s := agent.NewStateStore(db.delegate.Queries()) - ctx := context.Background() + ctx := t.Context() _, err := s.CreateRun(ctx, ids.UUID{}) assert.Error(t, err, "zero conversation id should be rejected") } func TestStateStore_UpdateRunStatus(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() s := agent.NewStateStore(q) - ctx := context.Background() + ctx := t.Context() convID := newConversation(t, q) run, err := s.CreateRun(ctx, convID) @@ -81,15 +87,18 @@ func TestStateStore_UpdateRunStatus(t *testing.T) { updated, err := s.UpdateRunStatus(ctx, run.ID, "completed", map[string]any{"steps": 3}) require.NoError(t, err) - assert.Equal(t, run.ID, updated.ID) - assert.Equal(t, "completed", updated.Status) + assert.Empty(t, cmp.Diff(sqlc.AgentRun{ + ID: run.ID, + Status: "completed", + }, updated, cmpopts.IgnoreFields(sqlc.AgentRun{}, "ConversationID", "MetadataJson", "CreatedAt", "UpdatedAt"))) } func TestStateStore_UpdateRunStatus_EmptyStatus(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() s := agent.NewStateStore(q) - ctx := context.Background() + ctx := t.Context() convID := newConversation(t, q) run, err := s.CreateRun(ctx, convID) @@ -100,10 +109,11 @@ func TestStateStore_UpdateRunStatus_EmptyStatus(t *testing.T) { } func TestStateStore_AddRunState(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() s := agent.NewStateStore(q) - ctx := context.Background() + ctx := t.Context() convID := newConversation(t, q) run, err := s.CreateRun(ctx, convID) @@ -112,17 +122,19 @@ func TestStateStore_AddRunState(t *testing.T) { state, err := s.AddRunState(ctx, run.ID, 0, fantasy.ReActStateLLMCall, map[string]string{"model": "gpt-4o"}) require.NoError(t, err) - assert.False(t, state.ID.IsZero()) - assert.Equal(t, run.ID, state.RunID) - assert.Equal(t, int64(0), state.StepIndex) - assert.Equal(t, string(fantasy.ReActStateLLMCall), state.State) + assert.Empty(t, cmp.Diff(sqlc.AgentRunState{ + RunID: run.ID, + StepIndex: 0, + State: string(fantasy.ReActStateLLMCall), + }, state, cmpopts.IgnoreFields(sqlc.AgentRunState{}, "ID", "SnapshotJson", "CreatedAt", "UpdatedAt"))) } func TestStateStore_AddRunState_NegativeStep(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() s := agent.NewStateStore(q) - ctx := context.Background() + ctx := t.Context() convID := newConversation(t, q) run, err := s.CreateRun(ctx, convID) @@ -133,10 +145,11 @@ func TestStateStore_AddRunState_NegativeStep(t *testing.T) { } func TestStateStore_AddTransition(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() s := agent.NewStateStore(q) - ctx := context.Background() + ctx := t.Context() convID := newConversation(t, q) run, err := s.CreateRun(ctx, convID) @@ -152,15 +165,19 @@ func TestStateStore_AddTransition(t *testing.T) { row, err := s.AddTransition(ctx, run.ID, tr) require.NoError(t, err) - assert.False(t, row.ID.IsZero()) - assert.Equal(t, string(fantasy.ReActStateInit), row.FromState) - assert.Equal(t, string(fantasy.ReActStatePrepareStep), row.ToState) - assert.Equal(t, string(fantasy.ReActTriggerStart), row.Trigger) + assert.Empty(t, cmp.Diff(sqlc.AgentStateTransition{ + RunID: run.ID, + StepIndex: 0, + FromState: string(fantasy.ReActStateInit), + ToState: string(fantasy.ReActStatePrepareStep), + Trigger: string(fantasy.ReActTriggerStart), + }, row, cmpopts.IgnoreFields(sqlc.AgentStateTransition{}, "ID", "At", "MetaJson", "Error", "CreatedAt", "UpdatedAt"))) } func TestStateStore_NilStore(t *testing.T) { + t.Parallel() var s *agent.StateStore - ctx := context.Background() + ctx := t.Context() _, err := s.CreateRun(ctx, ids.New()) assert.Error(t, err, "nil store should error") @@ -169,11 +186,12 @@ func TestStateStore_NilStore(t *testing.T) { // --- CheckpointStore --- func TestCheckpointStore_CreateAndList(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() ss := agent.NewStateStore(q) cs := agent.NewCheckpointStore(q) - ctx := context.Background() + ctx := t.Context() convID := newConversation(t, q) run, err := ss.CreateRun(ctx, convID) @@ -195,11 +213,12 @@ func TestCheckpointStore_CreateAndList(t *testing.T) { } func TestCheckpointStore_GetByName(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() ss := agent.NewStateStore(q) cs := agent.NewCheckpointStore(q) - ctx := context.Background() + ctx := t.Context() convID := newConversation(t, q) run, err := ss.CreateRun(ctx, convID) @@ -217,10 +236,11 @@ func TestCheckpointStore_GetByName(t *testing.T) { } func TestCheckpointStore_EmptyName(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() cs := agent.NewCheckpointStore(q) - ctx := context.Background() + ctx := t.Context() convID := newConversation(t, q) _, err := cs.CreateCheckpoint(ctx, convID, " ", ids.New(), nil) diff --git a/pkg/agent/tool_result_search_test.go b/pkg/agent/tool_result_search_test.go index b07d31e83..703604657 100644 --- a/pkg/agent/tool_result_search_test.go +++ b/pkg/agent/tool_result_search_test.go @@ -1,11 +1,11 @@ package agent_test import ( - "context" - jsonv2 "github.com/go-json-experiment/json" "strings" "testing" + jsonv2 "github.com/go-json-experiment/json" + "charm.land/fantasy" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,7 +21,7 @@ func setupSearchFixture(t *testing.T) (fantasy.AgentTool, agent.KVDelegate, stri q := db.delegate.Queries() convID := newConversation(t, q) s := agent.NewStateStore(q) - run, err := s.CreateRun(context.Background(), convID) + run, err := s.CreateRun(t.Context(), convID) require.NoError(t, err) kv := agent.NewDelegateKV(db.delegate, "search-test") @@ -41,7 +41,7 @@ func setupSearchFixture(t *testing.T) (fantasy.AgentTool, agent.KVDelegate, stri {ToolCallID: "c2", ToolName: "beta"}, {ToolCallID: "c3", ToolName: "alpha"}, } - _, err = r.Execute(context.Background(), nil, calls, nil) + _, err = r.Execute(t.Context(), nil, calls, nil) require.NoError(t, err) tool := agent.NewToolResultSearchTool(q, kv) @@ -51,7 +51,7 @@ func setupSearchFixture(t *testing.T) (fantasy.AgentTool, agent.KVDelegate, stri // invokeSearch calls the search tool with the given input and parses the JSON response. func invokeSearch(t *testing.T, tool fantasy.AgentTool, input agent.ToolResultSearchInput) map[string]any { t.Helper() - resp, err := tool.Run(context.Background(), fantasy.ToolCall{ + resp, err := tool.Run(t.Context(), fantasy.ToolCall{ Input: marshalInput(t, input), }) require.NoError(t, err) @@ -69,89 +69,102 @@ func marshalInput(t *testing.T, v any) string { return string(b) } -func TestToolResultSearch_ByConversationID(t *testing.T) { - tool, _, convID, _ := setupSearchFixture(t) - ctx := context.Background() - _ = ctx +func TestToolResultSearch_QueryFilters(t *testing.T) { + t.Parallel() - out := invokeSearch(t, tool, agent.ToolResultSearchInput{ - ConversationID: convID, - Limit: 10, - }) + tests := []struct { + name string + build func(convID, runID string) agent.ToolResultSearchInput + wantTotal float64 + wantTCID string + }{ + { + name: "by conversation ID", + build: func(convID, _ string) agent.ToolResultSearchInput { + return agent.ToolResultSearchInput{ + ConversationID: convID, + Limit: 10, + } + }, + wantTotal: 3, + }, + { + name: "by run ID", + build: func(_, runID string) agent.ToolResultSearchInput { + return agent.ToolResultSearchInput{ + RunID: runID, + Limit: 10, + } + }, + wantTotal: 3, + }, + { + name: "by run ID and tool_call_id", + build: func(_, runID string) agent.ToolResultSearchInput { + return agent.ToolResultSearchInput{ + RunID: runID, + ToolCallID: "c2", + } + }, + wantTotal: 1, + wantTCID: "c2", + }, + { + name: "filter by tool_name", + build: func(convID, _ string) agent.ToolResultSearchInput { + return agent.ToolResultSearchInput{ + ConversationID: convID, + ToolName: "alpha", + Limit: 10, + } + }, + wantTotal: 2, + }, + { + name: "filter by query", + build: func(convID, _ string) agent.ToolResultSearchInput { + return agent.ToolResultSearchInput{ + ConversationID: convID, + Query: "beta", + Limit: 10, + } + }, + wantTotal: 1, + }, + { + name: "default limit", + build: func(convID, _ string) agent.ToolResultSearchInput { + return agent.ToolResultSearchInput{ + ConversationID: convID, + } + }, + wantTotal: 3, + }, + } - total, _ := out["total"].(float64) - assert.Equal(t, float64(3), total, "should find all 3 results by conversation_id") -} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tool, _, convID, runID := setupSearchFixture(t) + input := tt.build(convID, runID) + out := invokeSearch(t, tool, input) -func TestToolResultSearch_ByRunID(t *testing.T) { - tool, _, _, runID := setupSearchFixture(t) + total, _ := out["total"].(float64) + assert.Equal(t, tt.wantTotal, total) - out := invokeSearch(t, tool, agent.ToolResultSearchInput{ - RunID: runID, - Limit: 10, - }) - - total, _ := out["total"].(float64) - assert.Equal(t, float64(3), total) -} - -func TestToolResultSearch_ByRunID_AndToolCallID(t *testing.T) { - tool, _, _, runID := setupSearchFixture(t) - - out := invokeSearch(t, tool, agent.ToolResultSearchInput{ - RunID: runID, - ToolCallID: "c2", - }) - - total, _ := out["total"].(float64) - assert.Equal(t, float64(1), total) - items := out["items"].([]any) - item := items[0].(map[string]any) - assert.Equal(t, "c2", item["tool_call_id"]) -} - -func TestToolResultSearch_FilterByToolName(t *testing.T) { - tool, _, convID, _ := setupSearchFixture(t) - - out := invokeSearch(t, tool, agent.ToolResultSearchInput{ - ConversationID: convID, - ToolName: "alpha", - Limit: 10, - }) - - total, _ := out["total"].(float64) - assert.Equal(t, float64(2), total, "filter by tool_name should return only 'alpha' results") -} - -func TestToolResultSearch_FilterByQuery(t *testing.T) { - tool, _, convID, _ := setupSearchFixture(t) - - out := invokeSearch(t, tool, agent.ToolResultSearchInput{ - ConversationID: convID, - Query: "beta", - Limit: 10, - }) - - total, _ := out["total"].(float64) - assert.Equal(t, float64(1), total) -} - -func TestToolResultSearch_DefaultLimit(t *testing.T) { - tool, _, convID, _ := setupSearchFixture(t) - - // No limit specified -- default is 5, but we only have 3 items - out := invokeSearch(t, tool, agent.ToolResultSearchInput{ - ConversationID: convID, - }) - - total, _ := out["total"].(float64) - assert.Equal(t, float64(3), total) + if tt.wantTCID != "" { + items := out["items"].([]any) + item := items[0].(map[string]any) + assert.Equal(t, tt.wantTCID, item["tool_call_id"]) + } + }) + } } func TestToolResultSearch_MissingConvAndRunID_ErrorResponse(t *testing.T) { + t.Parallel() tool, _, _, _ := setupSearchFixture(t) - resp, err := tool.Run(context.Background(), fantasy.ToolCall{ + resp, err := tool.Run(t.Context(), fantasy.ToolCall{ Input: marshalInput(t, agent.ToolResultSearchInput{}), }) require.NoError(t, err) @@ -163,11 +176,12 @@ func TestToolResultSearch_MissingConvAndRunID_ErrorResponse(t *testing.T) { // TestToolResultSearch_LineView verifies that the KV full-result is sliced // into the requested line range. func TestToolResultSearch_LineView(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() convID := newConversation(t, q) s := agent.NewStateStore(q) - run, err := s.CreateRun(context.Background(), convID) + run, err := s.CreateRun(t.Context(), convID) require.NoError(t, err) kv := agent.NewDelegateKV(db.delegate, "line-view-test") @@ -189,7 +203,7 @@ func TestToolResultSearch_LineView(t *testing.T) { RunID: run.ID, ThresholdChars: 100000, } - _, err = r.Execute(context.Background(), nil, []fantasy.ToolCallContent{{ToolCallID: "lv1", ToolName: "liner"}}, nil) + _, err = r.Execute(t.Context(), nil, []fantasy.ToolCallContent{{ToolCallID: "lv1", ToolName: "liner"}}, nil) require.NoError(t, err) tool := agent.NewToolResultSearchTool(q, kv) @@ -220,11 +234,12 @@ func TestToolResultSearch_LineView(t *testing.T) { // TestToolResultSearch_ChunkView verifies chunk-range retrieval from KV. func TestToolResultSearch_ChunkView(t *testing.T) { + t.Parallel() db := newTestQueries(t) q := db.delegate.Queries() convID := newConversation(t, q) s := agent.NewStateStore(q) - run, err := s.CreateRun(context.Background(), convID) + run, err := s.CreateRun(t.Context(), convID) require.NoError(t, err) kv := agent.NewDelegateKV(db.delegate, "chunk-view-test") @@ -247,7 +262,7 @@ func TestToolResultSearch_ChunkView(t *testing.T) { ThresholdChars: 10, ChunkChars: 20, } - _, err = r.Execute(context.Background(), nil, []fantasy.ToolCallContent{{ToolCallID: "cv1", ToolName: "chunker"}}, nil) + _, err = r.Execute(t.Context(), nil, []fantasy.ToolCallContent{{ToolCallID: "cv1", ToolName: "chunker"}}, nil) require.NoError(t, err) tool := agent.NewToolResultSearchTool(q, kv) diff --git a/pkg/auth/oauth_test.go b/pkg/auth/oauth_test.go index c3eb0e9dc..9be6ef9cd 100644 --- a/pkg/auth/oauth_test.go +++ b/pkg/auth/oauth_test.go @@ -24,6 +24,7 @@ func makeJWTForClaims(t *testing.T, claims map[string]interface{}) string { } func TestBuildAuthorizeURL(t *testing.T) { + t.Parallel() cfg := OAuthProviderConfig{ Issuer: "https://auth.example.com", ClientID: "test-client-id", @@ -68,6 +69,7 @@ func TestBuildAuthorizeURL(t *testing.T) { } func TestBuildAuthorizeURLOpenAIExtras(t *testing.T) { + t.Parallel() cfg := OpenAIOAuthConfig() pkce := PKCECodes{CodeVerifier: "test-verifier", CodeChallenge: "test-challenge"} @@ -90,6 +92,7 @@ func TestBuildAuthorizeURLOpenAIExtras(t *testing.T) { } func TestParseTokenResponse(t *testing.T) { + t.Parallel() resp := map[string]interface{}{ "access_token": "test-access-token", "refresh_token": "test-refresh-token", @@ -121,6 +124,7 @@ func TestParseTokenResponse(t *testing.T) { } func TestParseTokenResponseExtractsAccountIDFromIDToken(t *testing.T) { + t.Parallel() idToken := makeJWTForClaims(t, map[string]interface{}{"chatgpt_account_id": "acc-id-from-id-token"}) resp := map[string]interface{}{ "access_token": "opaque-access-token", @@ -140,6 +144,7 @@ func TestParseTokenResponseExtractsAccountIDFromIDToken(t *testing.T) { } func TestExtractAccountIDFromOrganizationsFallback(t *testing.T) { + t.Parallel() token := makeJWTForClaims(t, map[string]interface{}{ "organizations": []interface{}{ map[string]interface{}{"id": "org_from_orgs"}, @@ -152,6 +157,7 @@ func TestExtractAccountIDFromOrganizationsFallback(t *testing.T) { } func TestParseTokenResponseNoAccessToken(t *testing.T) { + t.Parallel() body := []byte(`{"refresh_token": "test"}`) _, err := parseTokenResponse(body, "openai") if err == nil { @@ -160,6 +166,7 @@ func TestParseTokenResponseNoAccessToken(t *testing.T) { } func TestParseTokenResponseAccountIDFromIDToken(t *testing.T) { + t.Parallel() idToken := makeJWTWithAccountID("acc-from-id") resp := map[string]interface{}{ "access_token": "not-a-jwt", @@ -186,6 +193,7 @@ func makeJWTWithAccountID(accountID string) string { } func TestExchangeCodeForTokens(t *testing.T) { + t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/oauth/token" { http.Error(w, "not found", http.StatusNotFound) @@ -229,6 +237,7 @@ func TestExchangeCodeForTokens(t *testing.T) { } func TestRefreshAccessToken(t *testing.T) { + t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/oauth/token" { http.Error(w, "not found", http.StatusNotFound) @@ -276,6 +285,7 @@ func TestRefreshAccessToken(t *testing.T) { } func TestRefreshAccessTokenNoRefreshToken(t *testing.T) { + t.Parallel() cfg := OpenAIOAuthConfig() cred := &AuthCredential{ AccessToken: "old-token", @@ -290,6 +300,7 @@ func TestRefreshAccessTokenNoRefreshToken(t *testing.T) { } func TestRefreshAccessTokenPreservesRefreshAndAccountID(t *testing.T) { + t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { resp := map[string]interface{}{ "access_token": "new-access-token-only", @@ -321,6 +332,7 @@ func TestRefreshAccessTokenPreservesRefreshAndAccountID(t *testing.T) { } func TestOpenAIOAuthConfig(t *testing.T) { + t.Parallel() cfg := OpenAIOAuthConfig() if cfg.Issuer != "https://auth.openai.com" { t.Errorf("Issuer = %q, want %q", cfg.Issuer, "https://auth.openai.com") @@ -334,6 +346,7 @@ func TestOpenAIOAuthConfig(t *testing.T) { } func TestParseDeviceCodeResponseIntervalAsNumber(t *testing.T) { + t.Parallel() body := []byte(`{"device_auth_id":"abc","user_code":"DEF-1234","interval":5}`) resp, err := parseDeviceCodeResponse(body) @@ -353,6 +366,7 @@ func TestParseDeviceCodeResponseIntervalAsNumber(t *testing.T) { } func TestParseDeviceCodeResponseIntervalAsString(t *testing.T) { + t.Parallel() body := []byte(`{"device_auth_id":"abc","user_code":"DEF-1234","interval":"5"}`) resp, err := parseDeviceCodeResponse(body) @@ -366,6 +380,7 @@ func TestParseDeviceCodeResponseIntervalAsString(t *testing.T) { } func TestParseDeviceCodeResponseInvalidInterval(t *testing.T) { + t.Parallel() body := []byte(`{"device_auth_id":"abc","user_code":"DEF-1234","interval":"abc"}`) if _, err := parseDeviceCodeResponse(body); err == nil { diff --git a/pkg/auth/pkce_test.go b/pkg/auth/pkce_test.go index 74ed573f1..c53149063 100644 --- a/pkg/auth/pkce_test.go +++ b/pkg/auth/pkce_test.go @@ -7,6 +7,7 @@ import ( ) func TestGeneratePKCE(t *testing.T) { + t.Parallel() codes, err := GeneratePKCE() if err != nil { t.Fatalf("GeneratePKCE() error: %v", err) @@ -35,6 +36,7 @@ func TestGeneratePKCE(t *testing.T) { } func TestGeneratePKCEUniqueness(t *testing.T) { + t.Parallel() codes1, err := GeneratePKCE() if err != nil { t.Fatalf("GeneratePKCE() error: %v", err) diff --git a/pkg/auth/store_test.go b/pkg/auth/store_test.go index e914bbee0..15f7a3742 100644 --- a/pkg/auth/store_test.go +++ b/pkg/auth/store_test.go @@ -8,6 +8,7 @@ import ( ) func TestAuthCredentialIsExpired(t *testing.T) { + t.Parallel() tests := []struct { name string expiresAt time.Time @@ -29,6 +30,7 @@ func TestAuthCredentialIsExpired(t *testing.T) { } func TestAuthCredentialNeedsRefresh(t *testing.T) { + t.Parallel() tests := []struct { name string expiresAt time.Time @@ -51,6 +53,7 @@ func TestAuthCredentialNeedsRefresh(t *testing.T) { } func TestStoreRoundtrip(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() origHome := os.Getenv("HOME") t.Setenv("HOME", tmpDir) @@ -114,6 +117,7 @@ func TestStoreFilePermissions(t *testing.T) { } func TestStoreMultiProvider(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() origHome := os.Getenv("HOME") t.Setenv("HOME", tmpDir) @@ -147,6 +151,7 @@ func TestStoreMultiProvider(t *testing.T) { } func TestDeleteCredential(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() origHome := os.Getenv("HOME") t.Setenv("HOME", tmpDir) @@ -171,6 +176,7 @@ func TestDeleteCredential(t *testing.T) { } func TestLoadStoreEmpty(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() origHome := os.Getenv("HOME") t.Setenv("HOME", tmpDir) diff --git a/pkg/cache/lru_test.go b/pkg/cache/lru_test.go index 7541245f5..b08e4c57e 100644 --- a/pkg/cache/lru_test.go +++ b/pkg/cache/lru_test.go @@ -33,6 +33,7 @@ func (c *mockClock) Advance(d time.Duration) { // --- Basic LRU Tests --- func TestLRU_SetAndGet(t *testing.T) { + t.Parallel() c := New[string, int](Options[string, int]{MaxSize: 10}) c.Set("a", 1) @@ -55,6 +56,7 @@ func TestLRU_SetAndGet(t *testing.T) { } func TestLRU_Update(t *testing.T) { + t.Parallel() c := New[string, string](Options[string, string]{MaxSize: 10}) c.Set("k", "v1") @@ -71,6 +73,7 @@ func TestLRU_Update(t *testing.T) { } func TestLRU_EvictionOrder(t *testing.T) { + t.Parallel() var evicted []string c := New[string, int](Options[string, int]{ MaxSize: 3, @@ -110,6 +113,7 @@ func TestLRU_EvictionOrder(t *testing.T) { } func TestLRU_MaxSizeZero_Unlimited(t *testing.T) { + t.Parallel() c := New[int, int](Options[int, int]{}) for i := 0; i < 1000; i++ { @@ -122,6 +126,7 @@ func TestLRU_MaxSizeZero_Unlimited(t *testing.T) { } func TestLRU_Delete(t *testing.T) { + t.Parallel() var evictCalled bool c := New[string, int](Options[string, int]{ MaxSize: 10, @@ -148,6 +153,7 @@ func TestLRU_Delete(t *testing.T) { } func TestLRU_Clear(t *testing.T) { + t.Parallel() var evictCount int c := New[string, int](Options[string, int]{ MaxSize: 10, @@ -171,6 +177,7 @@ func TestLRU_Clear(t *testing.T) { } func TestLRU_Peek_DoesNotPromote(t *testing.T) { + t.Parallel() c := New[string, int](Options[string, int]{MaxSize: 3}) c.Set("a", 1) @@ -196,6 +203,7 @@ func TestLRU_Peek_DoesNotPromote(t *testing.T) { // --- TTL Tests --- func TestLRU_TTL_Expiry(t *testing.T) { + t.Parallel() clock := newMockClock(time.Now()) c := New[string, int](Options[string, int]{ MaxSize: 10, @@ -225,6 +233,7 @@ func TestLRU_TTL_Expiry(t *testing.T) { } func TestLRU_TTL_PerEntry(t *testing.T) { + t.Parallel() clock := newMockClock(time.Now()) c := New[string, int](Options[string, int]{ MaxSize: 10, @@ -251,6 +260,7 @@ func TestLRU_TTL_PerEntry(t *testing.T) { } func TestLRU_Peek_ExpiresEntries(t *testing.T) { + t.Parallel() clock := newMockClock(time.Now()) c := New[string, int](Options[string, int]{ MaxSize: 10, @@ -268,6 +278,7 @@ func TestLRU_Peek_ExpiresEntries(t *testing.T) { } func TestLRU_Purge(t *testing.T) { + t.Parallel() clock := newMockClock(time.Now()) c := New[string, int](Options[string, int]{ MaxSize: 10, @@ -300,6 +311,7 @@ func TestLRU_Purge(t *testing.T) { // --- Stale-While-Revalidate Tests --- func TestLRU_SWR_ReturnsStaleAndRefreshes(t *testing.T) { + t.Parallel() clock := newMockClock(time.Now()) var fetchCalls atomic.Int32 refreshDone := make(chan struct{}, 1) @@ -347,6 +359,7 @@ func TestLRU_SWR_ReturnsStaleAndRefreshes(t *testing.T) { } func TestLRU_SWR_HardExpiry(t *testing.T) { + t.Parallel() clock := newMockClock(time.Now()) c := New[string, int](Options[string, int]{ MaxSize: 10, @@ -367,6 +380,7 @@ func TestLRU_SWR_HardExpiry(t *testing.T) { } func TestLRU_SWR_NoFetchFunc_StaleStillReturned(t *testing.T) { + t.Parallel() clock := newMockClock(time.Now()) c := New[string, int](Options[string, int]{ MaxSize: 10, @@ -388,6 +402,7 @@ func TestLRU_SWR_NoFetchFunc_StaleStillReturned(t *testing.T) { // --- Tag-Based Invalidation Tests --- func TestLRU_Tags_InvalidateByTag(t *testing.T) { + t.Parallel() c := New[string, int](Options[string, int]{MaxSize: 10}) c.SetWithTags("user:1", 1, []string{"users"}) @@ -423,6 +438,7 @@ func TestLRU_Tags_InvalidateByTag(t *testing.T) { } func TestLRU_Tags_InvalidateNonexistentTag(t *testing.T) { + t.Parallel() c := New[string, int](Options[string, int]{MaxSize: 10}) c.Set("a", 1) @@ -435,6 +451,7 @@ func TestLRU_Tags_InvalidateNonexistentTag(t *testing.T) { } func TestLRU_Tags_UpdateRemovesOldTags(t *testing.T) { + t.Parallel() c := New[string, int](Options[string, int]{MaxSize: 10}) c.SetWithTags("k", 1, []string{"tag-a"}) @@ -458,6 +475,7 @@ func TestLRU_Tags_UpdateRemovesOldTags(t *testing.T) { // --- Concurrent Access Tests --- func TestLRU_ConcurrentAccess(t *testing.T) { + t.Parallel() c := New[int, int](Options[int, int]{MaxSize: 100}) var wg sync.WaitGroup @@ -493,6 +511,7 @@ func TestLRU_ConcurrentAccess(t *testing.T) { } func TestLRU_ConcurrentTags(t *testing.T) { + t.Parallel() c := New[string, int](Options[string, int]{MaxSize: 100}) var wg sync.WaitGroup @@ -524,6 +543,7 @@ func TestLRU_ConcurrentTags(t *testing.T) { // --- Edge Cases --- func TestLRU_ZeroTTL_NoExpiry(t *testing.T) { + t.Parallel() clock := newMockClock(time.Now()) c := New[string, int](Options[string, int]{ MaxSize: 10, @@ -540,6 +560,7 @@ func TestLRU_ZeroTTL_NoExpiry(t *testing.T) { } func TestLRU_MaxSizeOne(t *testing.T) { + t.Parallel() c := New[string, int](Options[string, int]{MaxSize: 1}) c.Set("a", 1) @@ -557,6 +578,7 @@ func TestLRU_MaxSizeOne(t *testing.T) { } func TestLRU_Keys_Order(t *testing.T) { + t.Parallel() c := New[string, int](Options[string, int]{MaxSize: 10}) c.Set("a", 1) diff --git a/pkg/channels/base_test.go b/pkg/channels/base_test.go index 78c6d1d66..2e1f5d2a3 100644 --- a/pkg/channels/base_test.go +++ b/pkg/channels/base_test.go @@ -3,6 +3,7 @@ package channels import "testing" func TestBaseChannelIsAllowed(t *testing.T) { + t.Parallel() tests := []struct { name string allowList []string diff --git a/pkg/channels/slack_test.go b/pkg/channels/slack_test.go index 3b51c2ca0..d537bc9ad 100644 --- a/pkg/channels/slack_test.go +++ b/pkg/channels/slack_test.go @@ -8,6 +8,7 @@ import ( ) func TestParseSlackChatID(t *testing.T) { + t.Parallel() tests := []struct { name string chatID string @@ -54,6 +55,7 @@ func TestParseSlackChatID(t *testing.T) { } func TestStripBotMention(t *testing.T) { + t.Parallel() ch := &SlackChannel{botUserID: "U12345BOT"} tests := []struct { @@ -99,6 +101,7 @@ func TestStripBotMention(t *testing.T) { } func TestNewSlackChannel(t *testing.T) { + t.Parallel() msgBus := bus.NewMessageBus() t.Run("missing bot token", func(t *testing.T) { @@ -143,6 +146,7 @@ func TestNewSlackChannel(t *testing.T) { } func TestSlackChannelIsAllowed(t *testing.T) { + t.Parallel() msgBus := bus.NewMessageBus() t.Run("empty allowlist allows all", func(t *testing.T) { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 3a29efd79..b02fff230 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -10,6 +10,7 @@ import ( // TestDefaultConfig_HeartbeatEnabled verifies heartbeat is enabled by default func TestDefaultConfig_HeartbeatEnabled(t *testing.T) { + t.Parallel() cfg := DefaultConfig() if !cfg.Heartbeat.Enabled { @@ -19,6 +20,7 @@ func TestDefaultConfig_HeartbeatEnabled(t *testing.T) { // TestDefaultConfig_SandboxPath verifies sandbox path is resolvable func TestDefaultConfig_SandboxPath(t *testing.T) { + t.Parallel() cfg := DefaultConfig() path := cfg.SandboxPath() if path == "" { @@ -28,6 +30,7 @@ func TestDefaultConfig_SandboxPath(t *testing.T) { // TestDefaultConfig_Model verifies model is set func TestDefaultConfig_Model(t *testing.T) { + t.Parallel() cfg := DefaultConfig() if cfg.Agents.Defaults.Model == "" { @@ -37,6 +40,7 @@ func TestDefaultConfig_Model(t *testing.T) { // TestDefaultConfig_MaxTokens verifies max tokens has default value func TestDefaultConfig_MaxTokens(t *testing.T) { + t.Parallel() cfg := DefaultConfig() if cfg.Agents.Defaults.MaxTokens == 0 { @@ -46,6 +50,7 @@ func TestDefaultConfig_MaxTokens(t *testing.T) { // TestDefaultConfig_MaxToolIterations verifies max tool iterations has default value func TestDefaultConfig_MaxToolIterations(t *testing.T) { + t.Parallel() cfg := DefaultConfig() if cfg.Agents.Defaults.MaxToolIterations == 0 { @@ -54,6 +59,7 @@ func TestDefaultConfig_MaxToolIterations(t *testing.T) { } func TestDefaultConfig_ContinuityRetention(t *testing.T) { + t.Parallel() cfg := DefaultConfig() if cfg.Agents.Defaults.ContinuityRetention.MinMessages <= 0 { @@ -72,6 +78,7 @@ func TestDefaultConfig_ContinuityRetention(t *testing.T) { // TestDefaultConfig_Temperature verifies temperature has default value func TestDefaultConfig_Temperature(t *testing.T) { + t.Parallel() cfg := DefaultConfig() if cfg.Agents.Defaults.Temperature == 0 { @@ -81,6 +88,7 @@ func TestDefaultConfig_Temperature(t *testing.T) { // TestDefaultConfig_Gateway verifies gateway defaults func TestDefaultConfig_Gateway(t *testing.T) { + t.Parallel() cfg := DefaultConfig() if cfg.Gateway.Host != "0.0.0.0" { @@ -93,6 +101,7 @@ func TestDefaultConfig_Gateway(t *testing.T) { // TestDefaultConfig_Providers verifies provider structure func TestDefaultConfig_Providers(t *testing.T) { + t.Parallel() cfg := DefaultConfig() // Verify all providers are empty by default @@ -121,6 +130,7 @@ func TestDefaultConfig_Providers(t *testing.T) { // TestDefaultConfig_Channels verifies channels are disabled by default func TestDefaultConfig_Channels(t *testing.T) { + t.Parallel() cfg := DefaultConfig() // Verify all channels are disabled by default @@ -152,6 +162,7 @@ func TestDefaultConfig_Channels(t *testing.T) { // TestDefaultConfig_WebTools verifies web tools config func TestDefaultConfig_WebTools(t *testing.T) { + t.Parallel() cfg := DefaultConfig() // Verify web tools defaults @@ -167,6 +178,7 @@ func TestDefaultConfig_WebTools(t *testing.T) { } func TestSaveConfig_FilePermissions(t *testing.T) { + t.Parallel() if runtime.GOOS == "windows" { t.Skip("file permission bits are not enforced on Windows") } @@ -192,6 +204,7 @@ func TestSaveConfig_FilePermissions(t *testing.T) { // TestConfig_Complete verifies all config fields are set func TestConfig_Complete(t *testing.T) { + t.Parallel() cfg := DefaultConfig() // Verify complete config structure @@ -222,6 +235,7 @@ func TestConfig_Complete(t *testing.T) { } func TestDefaultConfig_OpenAIWebSearchEnabled(t *testing.T) { + t.Parallel() cfg := DefaultConfig() if !cfg.Providers.OpenAI.WebSearch { t.Fatal("DefaultConfig().Providers.OpenAI.WebSearch should be true") @@ -229,6 +243,7 @@ func TestDefaultConfig_OpenAIWebSearchEnabled(t *testing.T) { } func TestLoadConfig_OpenAIWebSearchDefaultsTrueWhenUnset(t *testing.T) { + t.Parallel() dir := t.TempDir() configPath := filepath.Join(dir, "config.json") if err := os.WriteFile(configPath, []byte(`{"providers":{"openai":{"api_base":""}}}`), 0o600); err != nil { @@ -245,6 +260,7 @@ func TestLoadConfig_OpenAIWebSearchDefaultsTrueWhenUnset(t *testing.T) { } func TestValidate_MemoryConfig(t *testing.T) { + t.Parallel() tests := []struct { name string mutate func(*Config) @@ -318,6 +334,7 @@ func TestValidate_MemoryConfig(t *testing.T) { } func TestValidate_ContinuityRetentionConfig(t *testing.T) { + t.Parallel() cfg := DefaultConfig() cfg.Agents.Defaults.ContinuityRetention.MinMessages = 8 cfg.Agents.Defaults.ContinuityRetention.MaxMessages = 4 @@ -342,6 +359,7 @@ func containsMemoryWarning(s string) bool { } func TestLoadConfig_OpenAIWebSearchCanBeDisabled(t *testing.T) { + t.Parallel() dir := t.TempDir() configPath := filepath.Join(dir, "config.json") if err := os.WriteFile(configPath, []byte(`{"providers":{"openai":{"web_search":false}}}`), 0o600); err != nil { diff --git a/pkg/cron/service_test.go b/pkg/cron/service_test.go index 53d69f6a9..e6627a430 100644 --- a/pkg/cron/service_test.go +++ b/pkg/cron/service_test.go @@ -8,6 +8,7 @@ import ( ) func TestSaveStore_FilePermissions(t *testing.T) { + t.Parallel() if runtime.GOOS == "windows" { t.Skip("file permission bits are not enforced on Windows") } diff --git a/pkg/fantasy/adapter_test.go b/pkg/fantasy/adapter_test.go index eb78c569c..20aad5925 100644 --- a/pkg/fantasy/adapter_test.go +++ b/pkg/fantasy/adapter_test.go @@ -103,6 +103,7 @@ func (t *mockNilResultTool) Execute(_ context.Context, _ map[string]interface{}) // --- PicoToolAdapter.Info() Tests --- func TestAdapter_Info(t *testing.T) { + t.Parallel() adapter := &PicoToolAdapter{inner: &mockDualChannelTool{}} info := adapter.Info() @@ -127,6 +128,7 @@ func TestAdapter_Info(t *testing.T) { } func TestAdapter_Info_UnwrapsSchemaWithRequired(t *testing.T) { + t.Parallel() mock := &mockToolWithRequired{} adapter := &PicoToolAdapter{inner: mock} info := adapter.Info() @@ -159,6 +161,7 @@ func (t *mockToolWithRequired) Execute(_ context.Context, _ map[string]interface // --- PicoToolAdapter.Run() Tests --- func TestAdapter_Run_SilentTool_NoPublish(t *testing.T) { + t.Parallel() msgBus := bus.NewMessageBus() adapter := &PicoToolAdapter{ inner: &mockSilentTool{}, @@ -173,7 +176,7 @@ func TestAdapter_Run_SilentTool_NoPublish(t *testing.T) { Input: "{}", } - resp, err := adapter.Run(context.Background(), call) + resp, err := adapter.Run(t.Context(), call) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -187,6 +190,7 @@ func TestAdapter_Run_SilentTool_NoPublish(t *testing.T) { } func TestAdapter_Run_DualChannel_PublishesForUser(t *testing.T) { + t.Parallel() msgBus := bus.NewMessageBus() adapter := &PicoToolAdapter{ inner: &mockDualChannelTool{}, @@ -201,7 +205,7 @@ func TestAdapter_Run_DualChannel_PublishesForUser(t *testing.T) { Input: `{"input": "hello"}`, } - resp, err := adapter.Run(context.Background(), call) + resp, err := adapter.Run(t.Context(), call) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -216,6 +220,7 @@ func TestAdapter_Run_DualChannel_PublishesForUser(t *testing.T) { } func TestAdapter_Run_ErrorTool_ReturnsErrorResponse(t *testing.T) { + t.Parallel() adapter := &PicoToolAdapter{ inner: &mockErrorTool{}, } @@ -226,7 +231,7 @@ func TestAdapter_Run_ErrorTool_ReturnsErrorResponse(t *testing.T) { Input: "{}", } - resp, err := adapter.Run(context.Background(), call) + resp, err := adapter.Run(t.Context(), call) if err != nil { t.Fatalf("Unexpected error (adapter should not return Go errors): %v", err) } @@ -240,6 +245,7 @@ func TestAdapter_Run_ErrorTool_ReturnsErrorResponse(t *testing.T) { } func TestAdapter_Run_NilResult_ReturnsError(t *testing.T) { + t.Parallel() adapter := &PicoToolAdapter{ inner: &mockNilResultTool{}, } @@ -250,7 +256,7 @@ func TestAdapter_Run_NilResult_ReturnsError(t *testing.T) { Input: "{}", } - resp, err := adapter.Run(context.Background(), call) + resp, err := adapter.Run(t.Context(), call) if err != nil { t.Fatalf("Unexpected Go error: %v", err) } @@ -264,6 +270,7 @@ func TestAdapter_Run_NilResult_ReturnsError(t *testing.T) { } func TestAdapter_Run_ContextualTool_SetsContext(t *testing.T) { + t.Parallel() ctxTool := &mockContextualTool{} adapter := &PicoToolAdapter{ inner: ctxTool, @@ -277,7 +284,7 @@ func TestAdapter_Run_ContextualTool_SetsContext(t *testing.T) { Input: "{}", } - resp, err := adapter.Run(context.Background(), call) + resp, err := adapter.Run(t.Context(), call) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -289,6 +296,7 @@ func TestAdapter_Run_ContextualTool_SetsContext(t *testing.T) { } func TestAdapter_Run_InvalidJSON_ReturnsError(t *testing.T) { + t.Parallel() adapter := &PicoToolAdapter{ inner: &mockSilentTool{}, } @@ -299,7 +307,7 @@ func TestAdapter_Run_InvalidJSON_ReturnsError(t *testing.T) { Input: "not valid json{{{", } - resp, err := adapter.Run(context.Background(), call) + resp, err := adapter.Run(t.Context(), call) if err != nil { t.Fatalf("Unexpected Go error: %v", err) } @@ -312,6 +320,7 @@ func TestAdapter_Run_InvalidJSON_ReturnsError(t *testing.T) { // --- BuildAdaptedTools Tests --- func TestBuildAdaptedTools_NilRegistry(t *testing.T) { + t.Parallel() result := BuildAdaptedTools(nil, nil, "", "") if result != nil { t.Error("Expected nil for nil registry") @@ -319,6 +328,7 @@ func TestBuildAdaptedTools_NilRegistry(t *testing.T) { } func TestBuildAdaptedTools_WrapsAllTools(t *testing.T) { + t.Parallel() registry := tools.NewToolRegistry() registry.Register(&mockSilentTool{}) registry.Register(&mockDualChannelTool{}) @@ -348,6 +358,7 @@ func TestBuildAdaptedTools_WrapsAllTools(t *testing.T) { // --- parseToolArgs Tests --- func TestParseToolArgs_EmptyInput(t *testing.T) { + t.Parallel() args, err := parseToolArgs("") if err != nil { t.Fatalf("Unexpected error: %v", err) @@ -358,6 +369,7 @@ func TestParseToolArgs_EmptyInput(t *testing.T) { } func TestParseToolArgs_EmptyObject(t *testing.T) { + t.Parallel() args, err := parseToolArgs("{}") if err != nil { t.Fatalf("Unexpected error: %v", err) @@ -368,6 +380,7 @@ func TestParseToolArgs_EmptyObject(t *testing.T) { } func TestParseToolArgs_ValidJSON(t *testing.T) { + t.Parallel() args, err := parseToolArgs(`{"key": "value", "num": 42}`) if err != nil { t.Fatalf("Unexpected error: %v", err) @@ -378,6 +391,7 @@ func TestParseToolArgs_ValidJSON(t *testing.T) { } func TestParseToolArgs_InvalidJSON(t *testing.T) { + t.Parallel() _, err := parseToolArgs("not json") if err == nil { t.Error("Expected error for invalid JSON") diff --git a/pkg/fantasy/convert_test.go b/pkg/fantasy/convert_test.go index ad2346f93..1989754f4 100644 --- a/pkg/fantasy/convert_test.go +++ b/pkg/fantasy/convert_test.go @@ -11,6 +11,7 @@ import ( // --- MessagesToFantasy Tests --- func TestMessagesToFantasy_EmptySlice(t *testing.T) { + t.Parallel() result := MessagesToFantasy(nil) if len(result) != 0 { t.Errorf("Expected empty slice for nil input, got %d", len(result)) @@ -23,6 +24,7 @@ func TestMessagesToFantasy_EmptySlice(t *testing.T) { } func TestMessageToFantasy_SimpleTextMessage(t *testing.T) { + t.Parallel() msg := messages.Message{ Role: "user", Content: "Hello, world", @@ -47,6 +49,7 @@ func TestMessageToFantasy_SimpleTextMessage(t *testing.T) { } func TestMessageToFantasy_AssistantWithMultipleToolCalls(t *testing.T) { + t.Parallel() msg := messages.Message{ Role: "assistant", Content: "Let me run both tools.", @@ -113,6 +116,7 @@ func TestMessageToFantasy_AssistantWithMultipleToolCalls(t *testing.T) { } func TestMessageToFantasy_ToolCallWithMapArgsFallback(t *testing.T) { + t.Parallel() msg := messages.Message{ Role: "assistant", ToolCalls: []messages.ToolCall{ @@ -145,6 +149,7 @@ func TestMessageToFantasy_ToolCallWithMapArgsFallback(t *testing.T) { } func TestMessageToFantasy_ToolResultMessage(t *testing.T) { + t.Parallel() msg := messages.Message{ Role: "tool", Content: "file contents here", @@ -177,6 +182,7 @@ func TestMessageToFantasy_ToolResultMessage(t *testing.T) { } func TestMessageToFantasy_EmptyContent(t *testing.T) { + t.Parallel() msg := messages.Message{ Role: "assistant", Content: "", @@ -193,6 +199,7 @@ func TestMessageToFantasy_EmptyContent(t *testing.T) { // --- StepToMessages Tests --- func TestStepToMessages_TextOnly(t *testing.T) { + t.Parallel() step := fantasy.StepResult{ Response: fantasy.Response{ Content: fantasy.ResponseContent{ @@ -216,6 +223,7 @@ func TestStepToMessages_TextOnly(t *testing.T) { } func TestStepToMessages_MultipleToolCalls(t *testing.T) { + t.Parallel() step := fantasy.StepResult{ Response: fantasy.Response{ Content: fantasy.ResponseContent{ @@ -280,6 +288,7 @@ func TestStepToMessages_MultipleToolCalls(t *testing.T) { } func TestStepToMessages_ErrorToolResult(t *testing.T) { + t.Parallel() testErr := errors.New("permission denied") step := fantasy.StepResult{ Response: fantasy.Response{ @@ -315,6 +324,7 @@ func TestStepToMessages_ErrorToolResult(t *testing.T) { } func TestStepToMessages_ErrorToolResult_NilError(t *testing.T) { + t.Parallel() step := fantasy.StepResult{ Response: fantasy.Response{ Content: fantasy.ResponseContent{ @@ -339,6 +349,7 @@ func TestStepToMessages_ErrorToolResult_NilError(t *testing.T) { } func TestStepToMessages_ToolCallWithoutText(t *testing.T) { + t.Parallel() step := fantasy.StepResult{ Response: fantasy.Response{ Content: fantasy.ResponseContent{ @@ -372,6 +383,7 @@ func TestStepToMessages_ToolCallWithoutText(t *testing.T) { // --- AgentResultToMessages Tests --- func TestAgentResultToMessages_MultipleSteps(t *testing.T) { + t.Parallel() result := &fantasy.AgentResult{ Steps: []fantasy.StepResult{ { @@ -417,7 +429,10 @@ func TestAgentResultToMessages_MultipleSteps(t *testing.T) { // --- Round-trip fidelity test --- func TestRoundTrip_PicoClawToFantasyAndBack(t *testing.T) { + t.Parallel( // Start with DragonScale messages representing a typical conversation + ) + original := []messages.Message{ {Role: "user", Content: "Read the file"}, { diff --git a/pkg/heartbeat/service_test.go b/pkg/heartbeat/service_test.go index a6e062b22..a73873db5 100644 --- a/pkg/heartbeat/service_test.go +++ b/pkg/heartbeat/service_test.go @@ -11,6 +11,7 @@ import ( ) func TestExecuteHeartbeat_Async(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "heartbeat-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -49,6 +50,7 @@ func TestExecuteHeartbeat_Async(t *testing.T) { } func TestExecuteHeartbeat_Error(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "heartbeat-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -87,6 +89,7 @@ func TestExecuteHeartbeat_Error(t *testing.T) { } func TestExecuteHeartbeat_Silent(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "heartbeat-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -125,6 +128,7 @@ func TestExecuteHeartbeat_Silent(t *testing.T) { } func TestHeartbeatService_StartStop(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "heartbeat-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -144,6 +148,7 @@ func TestHeartbeatService_StartStop(t *testing.T) { } func TestHeartbeatService_Disabled(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "heartbeat-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -161,6 +166,7 @@ func TestHeartbeatService_Disabled(t *testing.T) { } func TestExecuteHeartbeat_NilResult(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "heartbeat-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -183,6 +189,7 @@ func TestExecuteHeartbeat_NilResult(t *testing.T) { // TestLogPath verifies heartbeat log is written to workspace directory func TestLogPath(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "heartbeat-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -203,6 +210,7 @@ func TestLogPath(t *testing.T) { // TestHeartbeatFilePath verifies HEARTBEAT.md is at workspace root func TestHeartbeatFilePath(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "heartbeat-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -222,6 +230,7 @@ func TestHeartbeatFilePath(t *testing.T) { } func TestExecuteHeartbeat_UsesDueContextWithoutHeartbeatFile(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "heartbeat-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) diff --git a/pkg/ids/ids_test.go b/pkg/ids/ids_test.go index 40c31f1ab..6e2b294c2 100644 --- a/pkg/ids/ids_test.go +++ b/pkg/ids/ids_test.go @@ -7,6 +7,7 @@ import ( ) func TestNew_IsV7(t *testing.T) { + t.Parallel() u := New() if u.IsZero() { @@ -24,6 +25,7 @@ func TestNew_IsV7(t *testing.T) { } func TestNew_Unique(t *testing.T) { + t.Parallel() seen := make(map[UUID]bool, 1000) for i := 0; i < 1000; i++ { u := New() @@ -35,6 +37,7 @@ func TestNew_Unique(t *testing.T) { } func TestNew_Monotonic(t *testing.T) { + t.Parallel() a := New() b := New() // UUIDv7 embeds ms timestamp in first 6 bytes. b >= a in timestamp. @@ -50,6 +53,7 @@ func TestNew_Monotonic(t *testing.T) { } func TestParse_RoundTrip(t *testing.T) { + t.Parallel() u := New() s := u.String() @@ -63,6 +67,7 @@ func TestParse_RoundTrip(t *testing.T) { } func TestParse_Errors(t *testing.T) { + t.Parallel() cases := []string{ "", "not-a-uuid", @@ -78,6 +83,7 @@ func TestParse_Errors(t *testing.T) { } func TestIsZero(t *testing.T) { + t.Parallel() var zero UUID if !zero.IsZero() { t.Fatal("zero UUID should be zero") @@ -89,6 +95,7 @@ func TestIsZero(t *testing.T) { } func TestValue_BlobRoundTrip(t *testing.T) { + t.Parallel() u := New() v, err := u.Value() if err != nil { @@ -112,6 +119,7 @@ func TestValue_BlobRoundTrip(t *testing.T) { } func TestValue_ZeroIsNil(t *testing.T) { + t.Parallel() var zero UUID v, err := zero.Value() if err != nil { @@ -123,6 +131,7 @@ func TestValue_ZeroIsNil(t *testing.T) { } func TestScan_NilLeavesZero(t *testing.T) { + t.Parallel() var u UUID if err := u.Scan(nil); err != nil { t.Fatalf("Scan(nil): %v", err) @@ -133,6 +142,7 @@ func TestScan_NilLeavesZero(t *testing.T) { } func TestScan_String(t *testing.T) { + t.Parallel() orig := New() var u UUID if err := u.Scan(orig.String()); err != nil { @@ -144,6 +154,7 @@ func TestScan_String(t *testing.T) { } func TestScan_InvalidBlob(t *testing.T) { + t.Parallel() var u UUID if err := u.Scan([]byte{1, 2, 3}); err == nil { t.Fatal("Scan(3-byte blob) should fail") @@ -151,6 +162,7 @@ func TestScan_InvalidBlob(t *testing.T) { } func TestScan_InvalidType(t *testing.T) { + t.Parallel() var u UUID if err := u.Scan(42); err == nil { t.Fatal("Scan(int) should fail") @@ -158,6 +170,7 @@ func TestScan_InvalidType(t *testing.T) { } func TestJSON_RoundTrip(t *testing.T) { + t.Parallel() u := New() b, err := jsonv2.Marshal(u) @@ -184,6 +197,7 @@ func TestJSON_RoundTrip(t *testing.T) { } func TestJSON_ZeroUUID(t *testing.T) { + t.Parallel() var zero UUID b, err := jsonv2.Marshal(zero) if err != nil { @@ -196,6 +210,7 @@ func TestJSON_ZeroUUID(t *testing.T) { } func TestJSON_InStruct(t *testing.T) { + t.Parallel() type record struct { ID UUID `json:"id"` Name string `json:"name"` @@ -217,6 +232,7 @@ func TestJSON_InStruct(t *testing.T) { } func TestFromBytes(t *testing.T) { + t.Parallel() u := New() b := u.Bytes() restored := FromBytes(b) @@ -226,6 +242,7 @@ func TestFromBytes(t *testing.T) { } func TestMustParse_Panics(t *testing.T) { + t.Parallel() defer func() { if r := recover(); r == nil { t.Fatal("MustParse should panic on bad input") @@ -235,6 +252,7 @@ func TestMustParse_Panics(t *testing.T) { } func TestString_Format(t *testing.T) { + t.Parallel() u := New() s := u.String() if len(s) != 36 { @@ -246,6 +264,7 @@ func TestString_Format(t *testing.T) { } func TestUUIDComparable(t *testing.T) { + t.Parallel() a := New() b := a // copy if a != b { diff --git a/pkg/itr/commands_test.go b/pkg/itr/commands_test.go index ed9712ae7..219dc4625 100644 --- a/pkg/itr/commands_test.go +++ b/pkg/itr/commands_test.go @@ -1,14 +1,17 @@ package itr import ( - jsonv2 "github.com/go-json-experiment/json" "testing" + jsonv2 "github.com/go-json-experiment/json" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestToolRequestMarshalRoundTrip(t *testing.T) { + t.Parallel() tests := []struct { name string req ToolRequest @@ -68,16 +71,13 @@ func TestToolRequestMarshalRoundTrip(t *testing.T) { decoded, err := UnmarshalRequest(data) require.NoError(t, err) - assert.Equal(t, tt.req.ID, decoded.ID) - assert.Equal(t, tt.req.Type, decoded.Type) - assert.Equal(t, tt.req.SessionKey, decoded.SessionKey) - assert.Equal(t, tt.req.Depth, decoded.Depth) - assert.Equal(t, tt.req.ToolCallID, decoded.ToolCallID) + assert.Empty(t, cmp.Diff(tt.req, decoded)) }) } } func TestToolExecPayloadPreservation(t *testing.T) { + t.Parallel() req := NewToolExecRequest("id-1", "s", "tc", "shell", `{"cmd":"ls -la"}`) data, err := req.Marshal() require.NoError(t, err) @@ -92,6 +92,7 @@ func TestToolExecPayloadPreservation(t *testing.T) { } func TestGrepPayloadPreservation(t *testing.T) { + t.Parallel() req := NewGrepRequest("g1", "s", 2, "error.*fatal", 25, true) data, err := req.Marshal() require.NoError(t, err) @@ -107,6 +108,7 @@ func TestGrepPayloadPreservation(t *testing.T) { } func TestDAGPlanPayloadPreservation(t *testing.T) { + t.Parallel() plan := DAGPlan{ Nodes: []DAGNode{ {ID: "a", Type: CmdToolSearch, Payload: ToolSearch{Query: "files", MaxResults: 5}, DependsOn: nil}, @@ -143,6 +145,7 @@ func TestDAGPlanPayloadPreservation(t *testing.T) { } func TestToolResponseMarshalRoundTrip(t *testing.T) { + t.Parallel() tests := []struct { name string resp ToolResponse @@ -169,17 +172,13 @@ func TestToolResponseMarshalRoundTrip(t *testing.T) { decoded, err := UnmarshalResponse(data) require.NoError(t, err) - assert.Equal(t, tt.resp.ID, decoded.ID) - assert.Equal(t, tt.resp.Result, decoded.Result) - assert.Equal(t, tt.resp.IsError, decoded.IsError) - assert.Equal(t, tt.resp.LeakDetected, decoded.LeakDetected) - assert.Equal(t, tt.resp.CostTokens, decoded.CostTokens) - assert.Equal(t, tt.resp.RedactedKeys, decoded.RedactedKeys) + assert.Empty(t, cmp.Diff(tt.resp, decoded)) }) } } func TestUnmarshalRequestJSON_UnknownType(t *testing.T) { + t.Parallel() data, _ := jsonv2.Marshal(map[string]interface{}{ "id": "bad", "type": "nonexistent_command", @@ -190,21 +189,25 @@ func TestUnmarshalRequestJSON_UnknownType(t *testing.T) { } func TestUnmarshalRequestJSON_InvalidJSON(t *testing.T) { + t.Parallel() _, err := UnmarshalRequestJSON([]byte(`{invalid`)) assert.Error(t, err) } func TestMarshalRequestFB_UnknownType(t *testing.T) { + t.Parallel() _, err := MarshalRequestFB(ToolRequest{ID: "bad", Type: CommandType("bogus")}) assert.Error(t, err) } func TestUnmarshalRequestFB_Garbage(t *testing.T) { + t.Parallel() _, err := UnmarshalRequestFB([]byte{0, 0, 0, 0}) assert.Error(t, err) } func TestRequestJSON_Roundtrip(t *testing.T) { + t.Parallel() orig := NewToolExecRequest("j1", "s", "tc", "shell", `{"cmd":"ls"}`) data, err := jsonv2.Marshal(orig) require.NoError(t, err) @@ -217,6 +220,7 @@ func TestRequestJSON_Roundtrip(t *testing.T) { } func TestResponseJSON_Roundtrip(t *testing.T) { + t.Parallel() orig := NewSuccessResponse("j2", "ok", 10) data, err := jsonv2.Marshal(orig) require.NoError(t, err) diff --git a/pkg/itr/dag/executor_test.go b/pkg/itr/dag/executor_test.go index cca541b2d..3020515fb 100644 --- a/pkg/itr/dag/executor_test.go +++ b/pkg/itr/dag/executor_test.go @@ -3,9 +3,10 @@ package dag_test import ( "context" "fmt" - jsonv2 "github.com/go-json-experiment/json" "testing" + jsonv2 "github.com/go-json-experiment/json" + "github.com/ZanzyTHEbar/dragonscale/pkg/itr" "github.com/ZanzyTHEbar/dragonscale/pkg/itr/dag" "github.com/ZanzyTHEbar/dragonscale/pkg/security/securebus" @@ -51,6 +52,7 @@ func (s *staticTool) Execute(_ context.Context, args map[string]interface{}) *to } func TestExecutor_LinearDependencyChain(t *testing.T) { + t.Parallel() toolMap := map[string]tools.Tool{ "step1": &staticTool{name: "step1", result: "r1"}, "step2": &staticTool{name: "step2", result: "r2"}, @@ -67,13 +69,14 @@ func TestExecutor_LinearDependencyChain(t *testing.T) { }, } - result, err := executor.Execute(context.Background(), "test-sess", plan) + result, err := executor.Execute(t.Context(), "test-sess", plan) require.NoError(t, err) assert.Contains(t, result.NodeResults["a"], "r1") assert.Contains(t, result.NodeResults["b"], "r2") } func TestExecutor_ParallelNodes(t *testing.T) { + t.Parallel() toolMap := map[string]tools.Tool{ "alpha": &staticTool{name: "alpha", result: "a-result"}, "beta": &staticTool{name: "beta", result: "b-result"}, @@ -90,13 +93,14 @@ func TestExecutor_ParallelNodes(t *testing.T) { }, } - result, err := executor.Execute(context.Background(), "test-sess", plan) + result, err := executor.Execute(t.Context(), "test-sess", plan) require.NoError(t, err) assert.Equal(t, "a-result", result.NodeResults["n1"]) assert.Equal(t, "b-result", result.NodeResults["n2"]) } func TestExecutor_CycleDetection(t *testing.T) { + t.Parallel() bus := makeBus(t, map[string]tools.Tool{}) defer bus.Close() @@ -109,12 +113,13 @@ func TestExecutor_CycleDetection(t *testing.T) { }, } - _, err := executor.Execute(context.Background(), "test-sess", plan) + _, err := executor.Execute(t.Context(), "test-sess", plan) require.Error(t, err) assert.Contains(t, err.Error(), "cycle") } func TestExecutor_WithJoiner(t *testing.T) { + t.Parallel() toolMap := map[string]tools.Tool{ "tool1": &staticTool{name: "tool1", result: "data-A"}, "tool2": &staticTool{name: "tool2", result: "data-B"}, @@ -140,24 +145,26 @@ func TestExecutor_WithJoiner(t *testing.T) { JoinerQuery: "Combine the results into a summary", } - result, err := executor.Execute(context.Background(), "test-sess", plan) + result, err := executor.Execute(t.Context(), "test-sess", plan) require.NoError(t, err) assert.Contains(t, result.FinalAnswer, "synthesized:") assert.Equal(t, uint32(50), result.TotalTokens) } func TestExecutor_EmptyPlan(t *testing.T) { + t.Parallel() bus := makeBus(t, map[string]tools.Tool{}) defer bus.Close() executor := dag.NewExecutor(bus, nil) - result, err := executor.Execute(context.Background(), "test-sess", &itr.DAGPlan{}) + result, err := executor.Execute(t.Context(), "test-sess", &itr.DAGPlan{}) require.NoError(t, err) assert.Empty(t, result.NodeResults) } func TestResolver_NodeRefSubstitution(t *testing.T) { + t.Parallel() argsJSON := `{"query": "search for #nodeprev results"}` toolMap := map[string]tools.Tool{ "search": &staticTool{name: "search", result: "found"}, @@ -175,31 +182,35 @@ func TestResolver_NodeRefSubstitution(t *testing.T) { }, } - result, err := executor.Execute(context.Background(), "test-sess", plan) + result, err := executor.Execute(t.Context(), "test-sess", plan) require.NoError(t, err) assert.Contains(t, result.NodeResults["prev"], "previous-output") assert.Contains(t, result.NodeResults["search"], "found") } func TestRouter_SimpleQuerySelectsReAct(t *testing.T) { + t.Parallel() cfg := dag.DefaultRouterConfig() mode := dag.Route(dag.ModeAuto, "What is the weather?", cfg) assert.Equal(t, dag.ModeReAct, mode) } func TestRouter_ComplexQuerySelectsDAG(t *testing.T) { + t.Parallel() cfg := dag.DefaultRouterConfig() mode := dag.Route(dag.ModeAuto, "Search for the latest news about AI, read the top 3 articles, and compare their viewpoints to create a summary report with aggregate statistics", cfg) assert.Equal(t, dag.ModeDAG, mode) } func TestRouter_ExplicitModeOverridesAuto(t *testing.T) { + t.Parallel() cfg := dag.DefaultRouterConfig() mode := dag.Route(dag.ModeReAct, "Do many complex parallel things simultaneously", cfg) assert.Equal(t, dag.ModeReAct, mode) } func TestPlanner_ValidatePlan(t *testing.T) { + t.Parallel() tests := []struct { name string plan string @@ -238,7 +249,7 @@ func TestPlanner_ValidatePlan(t *testing.T) { return tt.plan, 10, nil } planner := dag.NewPlanner(mockModel, nil, dag.DefaultPlannerConfig()) - _, _, planErr := planner.Plan(context.Background(), "test query", nil) + _, _, planErr := planner.Plan(t.Context(), "test query", nil) if tt.wantErr { assert.Error(t, planErr) } else { diff --git a/pkg/itr/dag/planner_test.go b/pkg/itr/dag/planner_test.go index fae836ad1..7fe7ba5f3 100644 --- a/pkg/itr/dag/planner_test.go +++ b/pkg/itr/dag/planner_test.go @@ -2,35 +2,41 @@ package dag import ( "context" - jsonv2 "github.com/go-json-experiment/json" "testing" + jsonv2 "github.com/go-json-experiment/json" + "github.com/ZanzyTHEbar/dragonscale/pkg/itr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestExtractJSON_PlainJSON(t *testing.T) { + t.Parallel() input := `{"nodes": [{"id": "n1"}]}` assert.Equal(t, input, extractJSON(input)) } func TestExtractJSON_MarkdownFenced(t *testing.T) { + t.Parallel() input := "Here is the plan:\n```json\n{\"nodes\": [{\"id\": \"n1\"}]}\n```\nDone." assert.Equal(t, `{"nodes": [{"id": "n1"}]}`, extractJSON(input)) } func TestExtractJSON_GenericFenced(t *testing.T) { + t.Parallel() input := "```\n{\"nodes\": []}\n```" assert.Equal(t, `{"nodes": []}`, extractJSON(input)) } func TestExtractJSON_LeadingText(t *testing.T) { + t.Parallel() input := "The plan is: {\"nodes\":[]}" assert.Equal(t, `{"nodes":[]}`, extractJSON(input)) } func TestFindIndex(t *testing.T) { + t.Parallel() assert.Equal(t, 0, findIndex("abc", "a")) assert.Equal(t, 2, findIndex("abc", "c")) assert.Equal(t, -1, findIndex("abc", "z")) @@ -39,6 +45,7 @@ func TestFindIndex(t *testing.T) { } func TestValidatePlan_Valid(t *testing.T) { + t.Parallel() plan := &itr.DAGPlan{ Nodes: []itr.DAGNode{ {ID: "a", Type: itr.CmdToolExec}, @@ -49,6 +56,7 @@ func TestValidatePlan_Valid(t *testing.T) { } func TestValidatePlan_Empty(t *testing.T) { + t.Parallel() plan := &itr.DAGPlan{Nodes: nil} err := validatePlan(plan) require.Error(t, err) @@ -56,6 +64,7 @@ func TestValidatePlan_Empty(t *testing.T) { } func TestValidatePlan_DuplicateID(t *testing.T) { + t.Parallel() plan := &itr.DAGPlan{ Nodes: []itr.DAGNode{ {ID: "x", Type: itr.CmdToolExec}, @@ -68,6 +77,7 @@ func TestValidatePlan_DuplicateID(t *testing.T) { } func TestValidatePlan_EmptyID(t *testing.T) { + t.Parallel() plan := &itr.DAGPlan{ Nodes: []itr.DAGNode{{ID: "", Type: itr.CmdToolExec}}, } @@ -77,6 +87,7 @@ func TestValidatePlan_EmptyID(t *testing.T) { } func TestValidatePlan_UnknownDependency(t *testing.T) { + t.Parallel() plan := &itr.DAGPlan{ Nodes: []itr.DAGNode{ {ID: "a", Type: itr.CmdToolExec, DependsOn: []string{"missing"}}, @@ -88,6 +99,7 @@ func TestValidatePlan_UnknownDependency(t *testing.T) { } func TestValidatePlan_SelfDependency(t *testing.T) { + t.Parallel() plan := &itr.DAGPlan{ Nodes: []itr.DAGNode{ {ID: "a", Type: itr.CmdToolExec, DependsOn: []string{"a"}}, @@ -99,6 +111,7 @@ func TestValidatePlan_SelfDependency(t *testing.T) { } func TestValidatePlan_CyclicDependency(t *testing.T) { + t.Parallel() plan := &itr.DAGPlan{ Nodes: []itr.DAGNode{ {ID: "a", Type: itr.CmdToolExec, DependsOn: []string{"b"}}, @@ -111,6 +124,7 @@ func TestValidatePlan_CyclicDependency(t *testing.T) { } func TestParsePlanResponse_ValidJSON(t *testing.T) { + t.Parallel() input := `{ "nodes": [ {"id": "n1", "type": "tool_exec", "payload": {"tool_name": "read_file", "args_json": "{\"path\":\"/tmp/a\"}"}} @@ -131,6 +145,7 @@ func TestParsePlanResponse_ValidJSON(t *testing.T) { } func TestParsePlanResponse_WithMarkdownFence(t *testing.T) { + t.Parallel() input := "```json\n" + `{"nodes": [{"id": "x", "type": "tool_search", "payload": {"query": "files"}}]}` + "\n```" plan, err := parsePlanResponse(input) @@ -143,11 +158,13 @@ func TestParsePlanResponse_WithMarkdownFence(t *testing.T) { } func TestParsePlanResponse_InvalidJSON(t *testing.T) { + t.Parallel() _, err := parsePlanResponse("not json at all") assert.Error(t, err) } func TestPlannerPlanE2E(t *testing.T) { + t.Parallel() mockLLM := func(ctx context.Context, systemPrompt, userQuery string) (string, uint32, error) { plan := itr.DAGPlan{ Nodes: []itr.DAGNode{ @@ -164,7 +181,7 @@ func TestPlannerPlanE2E(t *testing.T) { } planner := NewPlanner(mockLLM, nil, DefaultPlannerConfig()) - plan, tokens, err := planner.Plan(context.Background(), "search and read", nil) + plan, tokens, err := planner.Plan(t.Context(), "search and read", nil) require.NoError(t, err) assert.Equal(t, uint32(100), tokens) require.Len(t, plan.Nodes, 2) @@ -173,12 +190,13 @@ func TestPlannerPlanE2E(t *testing.T) { } func TestPlannerPlanLLMError(t *testing.T) { + t.Parallel() mockLLM := func(ctx context.Context, systemPrompt, userQuery string) (string, uint32, error) { return "", 50, assert.AnError } planner := NewPlanner(mockLLM, nil, DefaultPlannerConfig()) - _, tokens, err := planner.Plan(context.Background(), "anything", nil) + _, tokens, err := planner.Plan(t.Context(), "anything", nil) assert.Error(t, err) assert.Equal(t, uint32(50), tokens) } diff --git a/pkg/itr/dag/replan_test.go b/pkg/itr/dag/replan_test.go index c0358c145..a75568f9d 100644 --- a/pkg/itr/dag/replan_test.go +++ b/pkg/itr/dag/replan_test.go @@ -7,6 +7,7 @@ import ( ) func TestNeedsReplan(t *testing.T) { + t.Parallel() assert.True(t, needsReplan("The task is incomplete [NEEDS_MORE_STEPS]")) assert.True(t, needsReplan("[NEEDS_MORE_STEPS]")) assert.False(t, needsReplan("Task complete. Here is the answer.")) @@ -14,5 +15,6 @@ func TestNeedsReplan(t *testing.T) { } func TestReplanSentinelIsConsistent(t *testing.T) { + t.Parallel() assert.Equal(t, "[NEEDS_MORE_STEPS]", replanSentinel) } diff --git a/pkg/itr/dag/resolver_test.go b/pkg/itr/dag/resolver_test.go index da3cc7697..4a0ed5cec 100644 --- a/pkg/itr/dag/resolver_test.go +++ b/pkg/itr/dag/resolver_test.go @@ -8,6 +8,7 @@ import ( ) func TestTopologicalOrderLinear(t *testing.T) { + t.Parallel() states := map[string]*nodeState{ "a": newNodeState("a", nil), "b": newNodeState("b", []string{"a"}), @@ -23,6 +24,7 @@ func TestTopologicalOrderLinear(t *testing.T) { } func TestTopologicalOrderParallel(t *testing.T) { + t.Parallel() states := map[string]*nodeState{ "a": newNodeState("a", nil), "b": newNodeState("b", nil), @@ -40,6 +42,7 @@ func TestTopologicalOrderParallel(t *testing.T) { } func TestTopologicalOrderCycleDetection(t *testing.T) { + t.Parallel() states := map[string]*nodeState{ "a": newNodeState("a", []string{"c"}), "b": newNodeState("b", []string{"a"}), @@ -52,6 +55,7 @@ func TestTopologicalOrderCycleDetection(t *testing.T) { } func TestTopologicalOrderSingleNode(t *testing.T) { + t.Parallel() states := map[string]*nodeState{ "only": newNodeState("only", nil), } @@ -63,6 +67,7 @@ func TestTopologicalOrderSingleNode(t *testing.T) { } func TestResolveRefs(t *testing.T) { + t.Parallel() states := map[string]*nodeState{ "search": newNodeState("search", nil), } @@ -75,6 +80,7 @@ func TestResolveRefs(t *testing.T) { } func TestResolveRefsNoMatch(t *testing.T) { + t.Parallel() states := map[string]*nodeState{} input := `{"path":"#nodemissing"}` result := resolveRefs(input, states) @@ -82,6 +88,7 @@ func TestResolveRefsNoMatch(t *testing.T) { } func TestResolveToolExecArgsNoRefs(t *testing.T) { + t.Parallel() states := map[string]*nodeState{} input := `{"path":"/tmp/plain.txt"}` result := resolveToolExecArgs(input, states) @@ -89,6 +96,7 @@ func TestResolveToolExecArgsNoRefs(t *testing.T) { } func TestEscapeForJSON(t *testing.T) { + t.Parallel() tests := []struct { input string expected string @@ -106,6 +114,7 @@ func TestEscapeForJSON(t *testing.T) { } func TestNodeStateSetAndGetResult(t *testing.T) { + t.Parallel() ns := newNodeState("test", nil) go func() { diff --git a/pkg/itr/dag/router_test.go b/pkg/itr/dag/router_test.go index c66d08abc..0a2224c05 100644 --- a/pkg/itr/dag/router_test.go +++ b/pkg/itr/dag/router_test.go @@ -8,6 +8,7 @@ import ( ) func TestRouteExplicitModes(t *testing.T) { + t.Parallel() cfg := DefaultRouterConfig() assert.Equal(t, ModeReAct, Route(ModeReAct, "anything", cfg)) @@ -15,17 +16,20 @@ func TestRouteExplicitModes(t *testing.T) { } func TestRouteAutoSimpleQuery(t *testing.T) { + t.Parallel() cfg := DefaultRouterConfig() assert.Equal(t, ModeReAct, Route(ModeAuto, "what is the weather?", cfg)) } func TestRouteAutoComplexQuery(t *testing.T) { + t.Parallel() cfg := DefaultRouterConfig() longQuery := strings.Repeat("word ", 35) assert.Equal(t, ModeDAG, Route(ModeAuto, longQuery, cfg)) } func TestRouteAutoParallelKeywords(t *testing.T) { + t.Parallel() cfg := DefaultRouterConfig() keywords := []string{ @@ -42,12 +46,14 @@ func TestRouteAutoParallelKeywords(t *testing.T) { } func TestRouteAutoToolSignals(t *testing.T) { + t.Parallel() cfg := DefaultRouterConfig() q := "search the codebase, read the file, then execute the command" assert.Equal(t, ModeDAG, Route(ModeAuto, q, cfg)) } func TestToolLoopModeString(t *testing.T) { + t.Parallel() assert.Equal(t, "react", ModeReAct.String()) assert.Equal(t, "dag", ModeDAG.String()) assert.Equal(t, "auto", ModeAuto.String()) @@ -55,6 +61,7 @@ func TestToolLoopModeString(t *testing.T) { } func TestClassifyQueryDefault(t *testing.T) { + t.Parallel() cfg := DefaultRouterConfig() assert.Equal(t, ModeReAct, classifyQuery("hello", cfg)) } diff --git a/pkg/itr/fb_codec_test.go b/pkg/itr/fb_codec_test.go index 78e86d2b0..43c3d9652 100644 --- a/pkg/itr/fb_codec_test.go +++ b/pkg/itr/fb_codec_test.go @@ -5,281 +5,148 @@ import ( "time" "github.com/ZanzyTHEbar/dragonscale/pkg/itr" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestFBCodec_RequestRoundtrip_Peek(t *testing.T) { - orig := itr.NewPeekRequest("req-1", "sess-A", 2, 1024, 4096) +func TestFBCodec_RequestRoundtrip(t *testing.T) { + t.Parallel() - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - assert.Equal(t, orig.ID, got.ID) - assert.Equal(t, orig.Type, got.Type) - assert.Equal(t, orig.Depth, got.Depth) - assert.Equal(t, orig.SessionKey, got.SessionKey) - origP, ok := orig.Payload.(itr.Peek) - require.True(t, ok, "orig payload should be Peek") - gotP, ok := got.Payload.(itr.Peek) - require.True(t, ok, "got payload should be Peek") - assert.Equal(t, origP, gotP) -} - -func TestFBCodec_RequestRoundtrip_Grep(t *testing.T) { - orig := itr.NewGrepRequest("req-2", "sess-B", 1, "error.*fatal", 25, true) - - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - assert.Equal(t, orig.Type, got.Type) - p, ok := got.Payload.(itr.Grep) - require.True(t, ok, "payload should be Grep") - assert.Equal(t, "error.*fatal", p.Pattern) - assert.Equal(t, uint32(25), p.MaxMatches) - assert.True(t, p.CaseInsensitive) -} - -func TestFBCodec_RequestRoundtrip_Partition(t *testing.T) { - orig := itr.NewPartitionRequest("req-3", "sess-C", 0, 8, "semantic", 100, true) - - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - p, ok := got.Payload.(itr.Partition) - require.True(t, ok, "payload should be Partition") - assert.Equal(t, uint32(8), p.K) - assert.Equal(t, "semantic", p.Method) - assert.Equal(t, uint32(100), p.Overlap) - assert.True(t, p.Semantic) -} - -func TestFBCodec_RequestRoundtrip_Recurse(t *testing.T) { - orig := itr.NewRecurseRequest("req-4", "sess-D", 3, "summarize this", "ctx-key-7", 5) - - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - p, ok := got.Payload.(itr.Recurse) - require.True(t, ok, "payload should be Recurse") - assert.Equal(t, "summarize this", p.SubQuery) - assert.Equal(t, "ctx-key-7", p.ContextKey) - assert.Equal(t, uint8(5), p.DepthHint) -} - -func TestFBCodec_RequestRoundtrip_ToolExec(t *testing.T) { - orig := itr.NewToolExecRequest("req-5", "sess-E", "tc-1", "read_file", `{"path":"/etc/hosts"}`) - - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - p, ok := got.Payload.(itr.ToolExec) - require.True(t, ok, "payload should be ToolExec") - assert.Equal(t, "read_file", p.ToolName) - assert.Equal(t, `{"path":"/etc/hosts"}`, p.ArgsJSON) - assert.Equal(t, "tc-1", got.ToolCallID) -} - -func TestFBCodec_RequestRoundtrip_ExecWasm(t *testing.T) { - orig := itr.ToolRequest{ - ID: "req-6", - Type: itr.CmdExecWasm, - Payload: itr.ExecWasm{ModuleKey: "mod-1", Entry: "main", InputJSON: `{"x":1}`}, - Timestamp: time.Now().UnixNano(), - SessionKey: "sess-F", - } - - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - p, ok := got.Payload.(itr.ExecWasm) - require.True(t, ok, "payload should be ExecWasm") - assert.Equal(t, "mod-1", p.ModuleKey) - assert.Equal(t, "main", p.Entry) - assert.Equal(t, `{"x":1}`, p.InputJSON) -} - -func TestFBCodec_RequestRoundtrip_Final(t *testing.T) { - orig := itr.NewFinalRequest("req-7", "sess-G", 2, "The answer is 42", "ans_var") - - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - p, ok := got.Payload.(itr.Final) - require.True(t, ok, "payload should be Final") - assert.Equal(t, "The answer is 42", p.Answer) - assert.Equal(t, "ans_var", p.VarName) -} - -func TestFBCodec_RequestRoundtrip_ToolSearch(t *testing.T) { - orig := itr.NewToolSearchRequest("req-8", "sess-H", "find file tools", 5) - - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - p, ok := got.Payload.(itr.ToolSearch) - require.True(t, ok, "payload should be ToolSearch") - assert.Equal(t, "find file tools", p.Query) - assert.Equal(t, uint8(5), p.MaxResults) -} - -func TestFBCodec_RequestRoundtrip_CodeExec(t *testing.T) { - orig := itr.NewCodeExecRequest("req-9", "sess-I", "console.log('hi')", "javascript") - - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - p, ok := got.Payload.(itr.CodeExec) - require.True(t, ok, "payload should be CodeExec") - assert.Equal(t, "console.log('hi')", p.Code) - assert.Equal(t, "javascript", p.Language) -} - -func TestFBCodec_RequestRoundtrip_DAGPlan(t *testing.T) { - plan := itr.DAGPlan{ - Nodes: []itr.DAGNode{ - { - ID: "a", - Type: itr.CmdToolExec, - Payload: itr.ToolExec{ToolName: "read_file", ArgsJSON: `{"path":"x.txt"}`}, - }, - { - ID: "b", - Type: itr.CmdToolSearch, - Payload: itr.ToolSearch{Query: "search tools", MaxResults: 3}, - DependsOn: []string{"a"}, + tests := []struct { + name string + req itr.ToolRequest + }{ + { + name: "peek", + req: itr.NewPeekRequest("req-1", "sess-A", 2, 1024, 4096), + }, + { + name: "grep", + req: itr.NewGrepRequest("req-2", "sess-B", 1, "error.*fatal", 25, true), + }, + { + name: "partition", + req: itr.NewPartitionRequest("req-3", "sess-C", 0, 8, "semantic", 100, true), + }, + { + name: "recurse", + req: itr.NewRecurseRequest("req-4", "sess-D", 3, "summarize this", "ctx-key-7", 5), + }, + { + name: "tool exec", + req: itr.NewToolExecRequest("req-5", "sess-E", "tc-1", "read_file", `{"path":"/etc/hosts"}`), + }, + { + name: "exec wasm", + req: itr.ToolRequest{ + ID: "req-6", + Type: itr.CmdExecWasm, + Payload: itr.ExecWasm{ModuleKey: "mod-1", Entry: "main", InputJSON: `{"x":1}`}, + Timestamp: time.Now().UnixNano(), + SessionKey: "sess-F", + }, + }, + { + name: "final", + req: itr.NewFinalRequest("req-7", "sess-G", 2, "The answer is 42", "ans_var"), + }, + { + name: "tool search", + req: itr.NewToolSearchRequest("req-8", "sess-H", "find file tools", 5), + }, + { + name: "code exec", + req: itr.NewCodeExecRequest("req-9", "sess-I", "console.log('hi')", "javascript"), + }, + { + name: "dag plan", + req: itr.NewDAGPlanRequest("req-10", "sess-J", itr.DAGPlan{ + Nodes: []itr.DAGNode{ + { + ID: "a", + Type: itr.CmdToolExec, + Payload: itr.ToolExec{ToolName: "read_file", ArgsJSON: `{"path":"x.txt"}`}, + }, + { + ID: "b", + Type: itr.CmdToolSearch, + Payload: itr.ToolSearch{Query: "search tools", MaxResults: 3}, + DependsOn: []string{"a"}, + }, + }, + MaxParallel: 4, + TokenBudget: 10000, + JoinerQuery: "Summarize the results", + }), + }, + { + name: "timestamp preserved", + req: itr.ToolRequest{ + ID: "ts-test", + Type: itr.CmdPeek, + Payload: itr.Peek{Start: 0, Length: 10}, + Timestamp: time.Now().UnixNano(), }, }, - MaxParallel: 4, - TokenBudget: 10000, - JoinerQuery: "Summarize the results", - } - orig := itr.NewDAGPlanRequest("req-10", "sess-J", plan) - - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) - - assert.Equal(t, itr.CmdDAGPlan, got.Type) - - gotPlan, ok := got.Payload.(itr.DAGPlan) - require.True(t, ok) - - assert.Equal(t, uint8(4), gotPlan.MaxParallel) - assert.Equal(t, uint32(10000), gotPlan.TokenBudget) - assert.Equal(t, "Summarize the results", gotPlan.JoinerQuery) - require.Len(t, gotPlan.Nodes, 2) - - nodeA := gotPlan.Nodes[0] - assert.Equal(t, "a", nodeA.ID) - assert.Equal(t, itr.CmdToolExec, nodeA.Type) - te, ok := nodeA.Payload.(itr.ToolExec) - require.True(t, ok) - assert.Equal(t, "read_file", te.ToolName) - assert.Equal(t, `{"path":"x.txt"}`, te.ArgsJSON) - - nodeB := gotPlan.Nodes[1] - assert.Equal(t, "b", nodeB.ID) - assert.Equal(t, itr.CmdToolSearch, nodeB.Type) - ts, ok := nodeB.Payload.(itr.ToolSearch) - require.True(t, ok) - assert.Equal(t, "search tools", ts.Query) - assert.Equal(t, uint8(3), ts.MaxResults) - assert.Equal(t, []string{"a"}, nodeB.DependsOn) -} - -func TestFBCodec_ResponseRoundtrip_Success(t *testing.T) { - orig := itr.NewSuccessResponse("resp-1", "file contents here", 150) - - data, err := itr.MarshalResponseFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalResponseFB(data) - require.NoError(t, err) - - assert.Equal(t, "resp-1", got.ID) - assert.Equal(t, "file contents here", got.Result) - assert.False(t, got.IsError) - assert.Equal(t, uint32(150), got.CostTokens) -} - -func TestFBCodec_ResponseRoundtrip_Error(t *testing.T) { - orig := itr.NewErrorResponse("resp-2", "tool not found") - - data, err := itr.MarshalResponseFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalResponseFB(data) - require.NoError(t, err) - - assert.True(t, got.IsError) - assert.Equal(t, "tool not found", got.Result) -} - -func TestFBCodec_ResponseRoundtrip_Leak(t *testing.T) { - orig := itr.NewLeakResponse("resp-3", "redacted output", []string{"api_key", "password"}) - - data, err := itr.MarshalResponseFB(orig) - require.NoError(t, err) - - got, err := itr.UnmarshalResponseFB(data) - require.NoError(t, err) - - assert.True(t, got.LeakDetected) - assert.Equal(t, []string{"api_key", "password"}, got.RedactedKeys) -} - -func TestFBCodec_TimestampPreserved(t *testing.T) { - ts := time.Now().UnixNano() - orig := itr.ToolRequest{ - ID: "ts-test", - Type: itr.CmdPeek, - Payload: itr.Peek{Start: 0, Length: 10}, - Timestamp: ts, } - data, err := itr.MarshalRequestFB(orig) - require.NoError(t, err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() - got, err := itr.UnmarshalRequestFB(data) - require.NoError(t, err) + data, err := itr.MarshalRequestFB(tt.req) + require.NoError(t, err) - assert.Equal(t, ts, got.Timestamp) + got, err := itr.UnmarshalRequestFB(data) + require.NoError(t, err) + + assert.Empty(t, cmp.Diff(tt.req, got)) + }) + } +} + +func TestFBCodec_ResponseRoundtrip(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + res itr.ToolResponse + }{ + { + name: "success", + res: itr.NewSuccessResponse("resp-1", "file contents here", 150), + }, + { + name: "error", + res: itr.NewErrorResponse("resp-2", "tool not found"), + }, + { + name: "leak", + res: itr.NewLeakResponse("resp-3", "redacted output", []string{"api_key", "password"}), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + data, err := itr.MarshalResponseFB(tt.res) + require.NoError(t, err) + + got, err := itr.UnmarshalResponseFB(data) + require.NoError(t, err) + + assert.Empty(t, cmp.Diff(tt.res, got)) + }) + } } func TestFBCodec_UnknownCommandType(t *testing.T) { + t.Parallel() _, err := itr.MarshalRequestFB(itr.ToolRequest{ ID: "bad", Type: itr.CommandType("nonexistent"), }) - assert.Error(t, err) + require.Error(t, err) } diff --git a/pkg/itr/wasm/runtime_test.go b/pkg/itr/wasm/runtime_test.go index 2d58c79b5..a60d6c9b2 100644 --- a/pkg/itr/wasm/runtime_test.go +++ b/pkg/itr/wasm/runtime_test.go @@ -1,7 +1,6 @@ package wasm import ( - "context" "testing" "time" @@ -47,14 +46,16 @@ var minimalWASM = []byte{ } func TestNewRuntime(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() rt, err := NewRuntime(ctx, DefaultRuntimeConfig()) require.NoError(t, err) defer rt.Close(ctx) } func TestExecuteMinimalModule(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() rt, err := NewRuntime(ctx, DefaultRuntimeConfig()) require.NoError(t, err) defer rt.Close(ctx) @@ -67,7 +68,8 @@ func TestExecuteMinimalModule(t *testing.T) { } func TestExecuteTimeout(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() cfg := DefaultRuntimeConfig() cfg.ExecTimeout = 1 * time.Millisecond rt, err := NewRuntime(ctx, cfg) @@ -83,7 +85,8 @@ func TestExecuteTimeout(t *testing.T) { } func TestExecuteInvalidWASM(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() rt, err := NewRuntime(ctx, DefaultRuntimeConfig()) require.NoError(t, err) defer rt.Close(ctx) @@ -94,7 +97,8 @@ func TestExecuteInvalidWASM(t *testing.T) { } func TestRuntimeCloseIdempotent(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() rt, err := NewRuntime(ctx, DefaultRuntimeConfig()) require.NoError(t, err) @@ -103,7 +107,8 @@ func TestRuntimeCloseIdempotent(t *testing.T) { } func TestExecuteAfterClose(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() rt, err := NewRuntime(ctx, DefaultRuntimeConfig()) require.NoError(t, err) rt.Close(ctx) @@ -114,6 +119,7 @@ func TestExecuteAfterClose(t *testing.T) { } func TestLimitedBuffer(t *testing.T) { + t.Parallel() lb := &limitedBuffer{max: 5} n, err := lb.Write([]byte("hello world")) assert.NoError(t, err) @@ -122,6 +128,7 @@ func TestLimitedBuffer(t *testing.T) { } func TestLimitedBufferExactFit(t *testing.T) { + t.Parallel() lb := &limitedBuffer{max: 5} n, err := lb.Write([]byte("hello")) assert.NoError(t, err) diff --git a/pkg/itr/wasm/transport_test.go b/pkg/itr/wasm/transport_test.go index 86bd4c9fe..0327c1314 100644 --- a/pkg/itr/wasm/transport_test.go +++ b/pkg/itr/wasm/transport_test.go @@ -10,9 +10,10 @@ import ( ) func TestTransportNonCodeExecForwarded(t *testing.T) { - rt, err := NewRuntime(context.Background(), DefaultRuntimeConfig()) + t.Parallel() + rt, err := NewRuntime(t.Context(), DefaultRuntimeConfig()) require.NoError(t, err) - defer rt.Close(context.Background()) + defer rt.Close(t.Context()) forwarded := false transport := NewTransport(rt, func(ctx context.Context, req itr.ToolRequest) (itr.ToolResponse, error) { @@ -21,7 +22,7 @@ func TestTransportNonCodeExecForwarded(t *testing.T) { }) req := itr.NewToolExecRequest("id-1", "sess", "tc", "read_file", `{"path":"/tmp"}`) - resp, err := transport.Send(context.Background(), req) + resp, err := transport.Send(t.Context(), req) require.NoError(t, err) assert.True(t, forwarded) assert.Equal(t, "forwarded", resp.Result) @@ -29,35 +30,38 @@ func TestTransportNonCodeExecForwarded(t *testing.T) { } func TestTransportNonCodeExecNoFallback(t *testing.T) { - rt, err := NewRuntime(context.Background(), DefaultRuntimeConfig()) + t.Parallel() + rt, err := NewRuntime(t.Context(), DefaultRuntimeConfig()) require.NoError(t, err) - defer rt.Close(context.Background()) + defer rt.Close(t.Context()) transport := NewTransport(rt, nil) req := itr.NewToolExecRequest("id-1", "sess", "tc", "read_file", `{"path":"/tmp"}`) - resp, err := transport.Send(context.Background(), req) + resp, err := transport.Send(t.Context(), req) require.NoError(t, err) assert.True(t, resp.IsError) assert.Contains(t, resp.Result, "unsupported command type") } func TestTransportCodeExecEmptyCode(t *testing.T) { - rt, err := NewRuntime(context.Background(), DefaultRuntimeConfig()) + t.Parallel() + rt, err := NewRuntime(t.Context(), DefaultRuntimeConfig()) require.NoError(t, err) - defer rt.Close(context.Background()) + defer rt.Close(t.Context()) transport := NewTransport(rt, nil) req := itr.NewCodeExecRequest("id-1", "sess", "", "javascript") - resp, err := transport.Send(context.Background(), req) + resp, err := transport.Send(t.Context(), req) require.NoError(t, err) assert.True(t, resp.IsError) assert.Contains(t, resp.Result, "empty code") } func TestTransportClose(t *testing.T) { - rt, err := NewRuntime(context.Background(), DefaultRuntimeConfig()) + t.Parallel() + rt, err := NewRuntime(t.Context(), DefaultRuntimeConfig()) require.NoError(t, err) transport := NewTransport(rt, nil) diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 9b9c96820..5f4058653 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -5,6 +5,7 @@ import ( ) func TestLogLevelFiltering(t *testing.T) { + t.Parallel() initialLevel := GetLevel() defer SetLevel(initialLevel) @@ -45,6 +46,7 @@ func TestLogLevelFiltering(t *testing.T) { } func TestLoggerWithComponent(t *testing.T) { + t.Parallel() initialLevel := GetLevel() defer SetLevel(initialLevel) @@ -81,6 +83,7 @@ func TestLoggerWithComponent(t *testing.T) { } func TestLogLevels(t *testing.T) { + t.Parallel() tests := []struct { name string level LogLevel @@ -103,6 +106,7 @@ func TestLogLevels(t *testing.T) { } func TestSetGetLevel(t *testing.T) { + t.Parallel() initialLevel := GetLevel() defer SetLevel(initialLevel) @@ -117,6 +121,7 @@ func TestSetGetLevel(t *testing.T) { } func TestLoggerHelperFunctions(t *testing.T) { + t.Parallel() initialLevel := GetLevel() defer SetLevel(initialLevel) diff --git a/pkg/memory/dag/backfill_test.go b/pkg/memory/dag/backfill_test.go index d5e40ca81..db0d04243 100644 --- a/pkg/memory/dag/backfill_test.go +++ b/pkg/memory/dag/backfill_test.go @@ -1,7 +1,6 @@ package dag_test import ( - "context" "fmt" "testing" "time" @@ -18,7 +17,8 @@ import ( ) func TestBackfillMissingSessionDAGs_CreatesSnapshotsAndPersistsStatus(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() d, err := delegate.NewLibSQLInMemory() require.NoError(t, err) require.NoError(t, d.Init(ctx)) diff --git a/pkg/memory/dag/dag_test.go b/pkg/memory/dag/dag_test.go index da12ce11e..73f5b51c4 100644 --- a/pkg/memory/dag/dag_test.go +++ b/pkg/memory/dag/dag_test.go @@ -4,6 +4,8 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -24,6 +26,7 @@ func makeMessages(n int) []Message { } func TestCompressor_EmptyInput(t *testing.T) { + t.Parallel() c := NewCompressor(DefaultCompressorConfig()) d := c.Compress(nil) assert.Empty(t, d.Nodes) @@ -31,6 +34,7 @@ func TestCompressor_EmptyInput(t *testing.T) { } func TestCompressor_SmallInput(t *testing.T) { + t.Parallel() c := NewCompressor(DefaultCompressorConfig()) msgs := []Message{ {Role: "user", Content: "Hello, how are you?"}, @@ -48,6 +52,7 @@ func TestCompressor_SmallInput(t *testing.T) { } func TestCompressor_ChunkSplitting(t *testing.T) { + t.Parallel() cfg := DefaultCompressorConfig() cfg.ChunkSize = 4 c := NewCompressor(cfg) @@ -67,6 +72,7 @@ func TestCompressor_ChunkSplitting(t *testing.T) { } func TestCompressor_SectionBuilding(t *testing.T) { + t.Parallel() cfg := DefaultCompressorConfig() cfg.ChunkSize = 4 cfg.SectionSize = 2 @@ -89,6 +95,7 @@ func TestCompressor_SectionBuilding(t *testing.T) { } func TestCompressor_SessionSummary(t *testing.T) { + t.Parallel() cfg := DefaultCompressorConfig() cfg.ChunkSize = 4 cfg.SectionSize = 2 @@ -109,6 +116,7 @@ func TestCompressor_SessionSummary(t *testing.T) { } func TestExtractSentences(t *testing.T) { + t.Parallel() tests := []struct { name string text string @@ -133,6 +141,7 @@ func TestExtractSentences(t *testing.T) { } func TestExtractSentences_Truncation(t *testing.T) { + t.Parallel() long := strings.Repeat("This is a very long sentence with many words. ", 20) result := extractSentences(long, 5) assert.LessOrEqual(t, len([]rune(result)), 210) @@ -140,6 +149,7 @@ func TestExtractSentences_Truncation(t *testing.T) { } func TestNode_FormatForPrompt(t *testing.T) { + t.Parallel() n := &Node{ ID: "chunk-1", Level: LevelChunk, @@ -153,6 +163,7 @@ func TestNode_FormatForPrompt(t *testing.T) { } func TestDAG_FormatLevel(t *testing.T) { + t.Parallel() cfg := DefaultCompressorConfig() cfg.ChunkSize = 4 c := NewCompressor(cfg) @@ -167,6 +178,7 @@ func TestDAG_FormatLevel(t *testing.T) { } func TestDAG_TotalTokens(t *testing.T) { + t.Parallel() cfg := DefaultCompressorConfig() cfg.ChunkSize = 4 c := NewCompressor(cfg) @@ -179,25 +191,30 @@ func TestDAG_TotalTokens(t *testing.T) { } func TestComputeBudget(t *testing.T) { + t.Parallel() cfg := DefaultBudgetConfig() b := ComputeBudget(100000, cfg) - assert.Equal(t, 100000, b.Total) - assert.Equal(t, 20000, b.SystemPrompt) - assert.Equal(t, 10000, b.Observations) - assert.Equal(t, 5000, b.Knowledge) - assert.Equal(t, 25000, b.DAGSummaries) - assert.Equal(t, 30000, b.RawTail) - assert.Equal(t, 10000, b.ToolResults) + assert.Empty(t, cmp.Diff(Budget{ + Total: 100000, + SystemPrompt: 20000, + Observations: 10000, + Knowledge: 5000, + DAGSummaries: 25000, + RawTail: 30000, + ToolResults: 10000, + }, b)) } func TestBudget_Remaining(t *testing.T) { + t.Parallel() b := Budget{Total: 10000} assert.Equal(t, 7000, b.Remaining(1000, 500, 500, 500, 500, 0)) assert.Equal(t, 0, b.Remaining(5000, 3000, 1000, 1000, 1000, 0)) } func TestSelectDAGLevel(t *testing.T) { + t.Parallel() cfg := DefaultCompressorConfig() cfg.ChunkSize = 4 cfg.SectionSize = 2 @@ -219,6 +236,7 @@ func TestSelectDAGLevel(t *testing.T) { } func TestTailMessageCount(t *testing.T) { + t.Parallel() assert.Equal(t, 4, TailMessageCount(100)) // Minimum assert.Equal(t, 20, TailMessageCount(1000)) // 1000/50 assert.Equal(t, 4, TailMessageCount(0)) // Zero budget @@ -226,6 +244,7 @@ func TestTailMessageCount(t *testing.T) { } func TestRenderDAGForBudget(t *testing.T) { + t.Parallel() assert.Empty(t, RenderDAGForBudget(nil, 1000)) cfg := DefaultCompressorConfig() diff --git a/pkg/memory/delegate/factory_test.go b/pkg/memory/delegate/factory_test.go index a147dd726..457a8febd 100644 --- a/pkg/memory/delegate/factory_test.go +++ b/pkg/memory/delegate/factory_test.go @@ -1,7 +1,6 @@ package delegate import ( - "context" "os" "path/filepath" "testing" @@ -12,6 +11,7 @@ import ( ) func TestNewFromConfig_LocalOnly_DefaultPath(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() defaultPath := filepath.Join(tmpDir, "test.db") @@ -23,7 +23,7 @@ func TestNewFromConfig_LocalOnly_DefaultPath(t *testing.T) { } defer d.Close() - if err := d.Init(context.Background()); err != nil { + if err := d.Init(t.Context()); err != nil { t.Fatalf("Init: %v", err) } @@ -32,13 +32,14 @@ func TestNewFromConfig_LocalOnly_DefaultPath(t *testing.T) { } // Verify it's functional - err = d.UpsertWorkingContext(context.Background(), "agent", "sess", "test content") + err = d.UpsertWorkingContext(t.Context(), "agent", "sess", "test content") if err != nil { t.Fatalf("UpsertWorkingContext: %v", err) } } func TestNewFromConfig_LocalOnly_CustomPath(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() customPath := filepath.Join(tmpDir, "custom.db") @@ -52,7 +53,7 @@ func TestNewFromConfig_LocalOnly_CustomPath(t *testing.T) { } defer d.Close() - if err := d.Init(context.Background()); err != nil { + if err := d.Init(t.Context()); err != nil { t.Fatalf("Init: %v", err) } @@ -63,6 +64,7 @@ func TestNewFromConfig_LocalOnly_CustomPath(t *testing.T) { } func TestNewFromConfig_CustomDims(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() dbPath := filepath.Join(tmpDir, "test.db") @@ -82,6 +84,7 @@ func TestNewFromConfig_CustomDims(t *testing.T) { } func TestNewFromConfig_DefaultDims(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() dbPath := filepath.Join(tmpDir, "test.db") @@ -99,6 +102,7 @@ func TestNewFromConfig_DefaultDims(t *testing.T) { } func TestNewFromConfig_ReplicaFallback(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() dbPath := filepath.Join(tmpDir, "test.db") @@ -121,12 +125,13 @@ func TestNewFromConfig_ReplicaFallback(t *testing.T) { } // Should still be functional in local mode - if err := d.Init(context.Background()); err != nil { + if err := d.Init(t.Context()); err != nil { t.Fatalf("Init: %v", err) } } func TestNewFromConfig_LocalFullRoundTrip(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() dbPath := filepath.Join(tmpDir, "roundtrip.db") @@ -140,11 +145,11 @@ func TestNewFromConfig_LocalFullRoundTrip(t *testing.T) { } defer d.Close() - if err := d.Init(context.Background()); err != nil { + if err := d.Init(t.Context()); err != nil { t.Fatalf("Init: %v", err) } - ctx := context.Background() + ctx := t.Context() // Working context round-trip if err := d.UpsertWorkingContext(ctx, "a1", "s1", "hello"); err != nil { @@ -180,6 +185,7 @@ func TestNewFromConfig_LocalFullRoundTrip(t *testing.T) { } func TestSyncConfig_Defaults(t *testing.T) { + t.Parallel() cfg := config.DefaultConfig() // Memory is always enabled -- no Enabled field to check. if cfg.Memory.EmbeddingDims != 768 { diff --git a/pkg/memory/delegate/sqlite_audit_test.go b/pkg/memory/delegate/sqlite_audit_test.go index 294f7c21a..7fd55a56f 100644 --- a/pkg/memory/delegate/sqlite_audit_test.go +++ b/pkg/memory/delegate/sqlite_audit_test.go @@ -25,6 +25,7 @@ func makeAuditEntry(agentID, sessionKey, action, target string) *memory.AuditEnt } func TestLibSQLDelegate_InsertAuditEntry(t *testing.T) { + t.Parallel() tests := []struct { name string entry *memory.AuditEntry @@ -54,7 +55,7 @@ func TestLibSQLDelegate_InsertAuditEntry(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() require.NoError(t, d.InsertAuditEntry(ctx, tt.entry)) count, err := d.CountAuditEntries(ctx, tt.entry.AgentID) @@ -65,6 +66,7 @@ func TestLibSQLDelegate_InsertAuditEntry(t *testing.T) { } func TestLibSQLDelegate_ListAuditEntries(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -114,7 +116,7 @@ func TestLibSQLDelegate_ListAuditEntries(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } @@ -126,6 +128,7 @@ func TestLibSQLDelegate_ListAuditEntries(t *testing.T) { } func TestLibSQLDelegate_ListAuditEntriesByAction(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -160,7 +163,7 @@ func TestLibSQLDelegate_ListAuditEntriesByAction(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } @@ -177,6 +180,7 @@ func TestLibSQLDelegate_ListAuditEntriesByAction(t *testing.T) { } func TestLibSQLDelegate_ListAuditEntriesBySession(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -209,7 +213,7 @@ func TestLibSQLDelegate_ListAuditEntriesBySession(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } @@ -221,8 +225,9 @@ func TestLibSQLDelegate_ListAuditEntriesBySession(t *testing.T) { } func TestLibSQLDelegate_PruneOldAuditEntries(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() require.NoError(t, d.InsertAuditEntry(ctx, makeAuditEntry("a1", "s1", "tool_call", "t1"))) require.NoError(t, d.InsertAuditEntry(ctx, makeAuditEntry("a1", "s1", "tool_call", "t2"))) @@ -240,6 +245,7 @@ func TestLibSQLDelegate_PruneOldAuditEntries(t *testing.T) { } func TestLibSQLDelegate_CountAuditEntriesByAction(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -269,7 +275,7 @@ func TestLibSQLDelegate_CountAuditEntriesByAction(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } diff --git a/pkg/memory/delegate/sqlite_bench_test.go b/pkg/memory/delegate/sqlite_bench_test.go index 7307ae51a..30b5ecb08 100644 --- a/pkg/memory/delegate/sqlite_bench_test.go +++ b/pkg/memory/delegate/sqlite_bench_test.go @@ -1,7 +1,6 @@ package delegate import ( - "context" "testing" "github.com/ZanzyTHEbar/dragonscale/pkg/ids" @@ -10,7 +9,7 @@ import ( func BenchmarkListRecallItems(b *testing.B) { d := newBenchDelegate(b) - ctx := context.Background() + ctx := b.Context() agent := "bench-agent" session := "bench-sess" @@ -36,7 +35,7 @@ func BenchmarkListRecallItems(b *testing.B) { func BenchmarkGetWorkingContext(b *testing.B) { d := newBenchDelegate(b) - ctx := context.Background() + ctx := b.Context() agent := "bench-agent" session := "bench-sess" @@ -51,7 +50,7 @@ func BenchmarkGetWorkingContext(b *testing.B) { func BenchmarkUpsertKV(b *testing.B) { d := newBenchDelegate(b) - ctx := context.Background() + ctx := b.Context() b.ReportAllocs() b.ResetTimer() @@ -62,7 +61,7 @@ func BenchmarkUpsertKV(b *testing.B) { func BenchmarkGetKV(b *testing.B) { d := newBenchDelegate(b) - ctx := context.Background() + ctx := b.Context() _ = d.UpsertKV(ctx, "bench-agent", "bench-key", "bench-value") b.ReportAllocs() @@ -74,7 +73,7 @@ func BenchmarkGetKV(b *testing.B) { func BenchmarkInsertAuditEntry(b *testing.B) { d := newBenchDelegate(b) - ctx := context.Background() + ctx := b.Context() b.ReportAllocs() b.ResetTimer() @@ -93,7 +92,7 @@ func BenchmarkInsertAuditEntry(b *testing.B) { // BenchmarkInsertRecallItems_Sequential measures sequential single-insert performance. func BenchmarkInsertRecallItems_Sequential(b *testing.B) { d := newBenchDelegate(b) - ctx := context.Background() + ctx := b.Context() b.ReportAllocs() b.ResetTimer() for b.Loop() { @@ -116,7 +115,7 @@ func BenchmarkInsertRecallItems_Sequential(b *testing.B) { // Compare with BenchmarkInsertRecallItems_Sequential to quantify WAL savings. func BenchmarkInsertRecallItems_Batch(b *testing.B) { d := newBenchDelegate(b) - ctx := context.Background() + ctx := b.Context() b.ReportAllocs() b.ResetTimer() for b.Loop() { @@ -140,7 +139,7 @@ func BenchmarkInsertRecallItems_Batch(b *testing.B) { // BenchmarkInsertArchivalChunks_Sequential measures sequential chunk inserts. func BenchmarkInsertArchivalChunks_Sequential(b *testing.B) { d := newBenchDelegate(b) - ctx := context.Background() + ctx := b.Context() recallID := ids.New() _ = d.InsertRecallItem(ctx, &memory.RecallItem{ ID: recallID, AgentID: "bench-agent", SessionKey: "s", @@ -162,7 +161,7 @@ func BenchmarkInsertArchivalChunks_Sequential(b *testing.B) { // BenchmarkInsertArchivalChunks_Batch measures batch-tx chunk inserts for 5 chunks. func BenchmarkInsertArchivalChunks_Batch(b *testing.B) { d := newBenchDelegate(b) - ctx := context.Background() + ctx := b.Context() recallID := ids.New() _ = d.InsertRecallItem(ctx, &memory.RecallItem{ ID: recallID, AgentID: "bench-agent", SessionKey: "s", @@ -185,11 +184,12 @@ func BenchmarkInsertArchivalChunks_Batch(b *testing.B) { func newBenchDelegate(b *testing.B) *LibSQLDelegate { b.Helper() + ctx := b.Context() d, err := NewLibSQLInMemory() if err != nil { b.Fatal(err) } - if err := d.Init(context.Background()); err != nil { + if err := d.Init(ctx); err != nil { b.Fatal(err) } b.Cleanup(func() { d.Close() }) diff --git a/pkg/memory/delegate/sqlite_dag_test.go b/pkg/memory/delegate/sqlite_dag_test.go index d2b59553a..3e527d821 100644 --- a/pkg/memory/delegate/sqlite_dag_test.go +++ b/pkg/memory/delegate/sqlite_dag_test.go @@ -1,7 +1,6 @@ package delegate import ( - "context" "testing" "github.com/ZanzyTHEbar/dragonscale/pkg/memory/dag" @@ -10,8 +9,9 @@ import ( ) func TestLibSQLDelegate_PersistDAG(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() compressor := dag.NewCompressor(dag.DefaultCompressorConfig()) msgs := make([]dag.Message, 16) diff --git a/pkg/memory/delegate/sqlite_doc_test.go b/pkg/memory/delegate/sqlite_doc_test.go index 83db598f2..989262f4d 100644 --- a/pkg/memory/delegate/sqlite_doc_test.go +++ b/pkg/memory/delegate/sqlite_doc_test.go @@ -21,6 +21,7 @@ func makeDoc(agentID, name, category, content string) *memory.AgentDocument { } func TestLibSQLDelegate_GetDocument(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -67,7 +68,7 @@ func TestLibSQLDelegate_GetDocument(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } @@ -86,6 +87,7 @@ func TestLibSQLDelegate_GetDocument(t *testing.T) { } func TestLibSQLDelegate_UpsertDocument(t *testing.T) { + t.Parallel() tests := []struct { name string ops []*memory.AgentDocument @@ -139,7 +141,7 @@ func TestLibSQLDelegate_UpsertDocument(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() for _, doc := range tt.ops { require.NoError(t, d.UpsertDocument(ctx, doc)) } @@ -152,6 +154,7 @@ func TestLibSQLDelegate_UpsertDocument(t *testing.T) { } func TestLibSQLDelegate_DeleteDocument(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -185,7 +188,7 @@ func TestLibSQLDelegate_DeleteDocument(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } @@ -206,6 +209,7 @@ func TestLibSQLDelegate_DeleteDocument(t *testing.T) { } func TestLibSQLDelegate_ListDocumentsByCategory(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -256,7 +260,7 @@ func TestLibSQLDelegate_ListDocumentsByCategory(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } @@ -271,6 +275,7 @@ func TestLibSQLDelegate_ListDocumentsByCategory(t *testing.T) { } func TestLibSQLDelegate_ListAllDocuments(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -318,7 +323,7 @@ func TestLibSQLDelegate_ListAllDocuments(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } diff --git a/pkg/memory/delegate/sqlite_integration_test.go b/pkg/memory/delegate/sqlite_integration_test.go index 6ae0949da..584f7b3df 100644 --- a/pkg/memory/delegate/sqlite_integration_test.go +++ b/pkg/memory/delegate/sqlite_integration_test.go @@ -1,7 +1,6 @@ package delegate import ( - "context" "testing" jsonv2 "github.com/go-json-experiment/json" @@ -14,8 +13,9 @@ import ( ) func TestCronKVBackend_Roundtrip(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() agentID := pkg.NAME kvKey := "cron:store" @@ -61,8 +61,9 @@ func TestCronKVBackend_Roundtrip(t *testing.T) { } func TestCronKVBackend_UpdatePreservesShape(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() agentID := pkg.NAME kvKey := "cron:store" @@ -78,8 +79,9 @@ func TestCronKVBackend_UpdatePreservesShape(t *testing.T) { } func TestCronKVBackend_PrefixScan(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() agentID := pkg.NAME require.NoError(t, d.UpsertKV(ctx, agentID, "cron:store", "{}")) @@ -94,8 +96,9 @@ func TestCronKVBackend_PrefixScan(t *testing.T) { } func TestEndToEnd_SessionAndAuditFlow(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() agentID := "a1" sessionKey := "sess-integration" @@ -171,8 +174,9 @@ func TestEndToEnd_SessionAndAuditFlow(t *testing.T) { } func TestEndToEnd_WorkingContextAndRecallRoundtrip(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() agentID := "a1" sessionKey := "sess-wc" diff --git a/pkg/memory/delegate/sqlite_kv_test.go b/pkg/memory/delegate/sqlite_kv_test.go index b112f341a..5321639d9 100644 --- a/pkg/memory/delegate/sqlite_kv_test.go +++ b/pkg/memory/delegate/sqlite_kv_test.go @@ -9,6 +9,7 @@ import ( ) func TestLibSQLDelegate_GetKV(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -73,7 +74,7 @@ func TestLibSQLDelegate_GetKV(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } @@ -89,6 +90,7 @@ func TestLibSQLDelegate_GetKV(t *testing.T) { } func TestLibSQLDelegate_UpsertKV(t *testing.T) { + t.Parallel() tests := []struct { name string ops []kvOp @@ -147,7 +149,7 @@ func TestLibSQLDelegate_UpsertKV(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() for _, op := range tt.ops { require.NoError(t, d.UpsertKV(ctx, op.agent, op.key, op.val)) } @@ -159,6 +161,7 @@ func TestLibSQLDelegate_UpsertKV(t *testing.T) { } func TestLibSQLDelegate_DeleteKV(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -193,7 +196,7 @@ func TestLibSQLDelegate_DeleteKV(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } @@ -218,6 +221,7 @@ func TestLibSQLDelegate_DeleteKV(t *testing.T) { } func TestLibSQLDelegate_ListKVByPrefix(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -286,7 +290,7 @@ func TestLibSQLDelegate_ListKVByPrefix(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } diff --git a/pkg/memory/delegate/sqlite_session_msg_test.go b/pkg/memory/delegate/sqlite_session_msg_test.go index 276146960..eefcfa8bb 100644 --- a/pkg/memory/delegate/sqlite_session_msg_test.go +++ b/pkg/memory/delegate/sqlite_session_msg_test.go @@ -30,6 +30,7 @@ func makeRecallItem(agentID, sessionKey, role, content, tags string) *memory.Rec } func TestLibSQLDelegate_InsertSessionMessage(t *testing.T) { + t.Parallel() tests := []struct { name string agentID string @@ -70,7 +71,7 @@ func TestLibSQLDelegate_InsertSessionMessage(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() require.NoError(t, d.InsertSessionMessage(ctx, tt.agentID, tt.sessionKey, tt.role, tt.content)) @@ -82,6 +83,7 @@ func TestLibSQLDelegate_InsertSessionMessage(t *testing.T) { } func TestLibSQLDelegate_ListSessionMessages(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -182,7 +184,7 @@ func TestLibSQLDelegate_ListSessionMessages(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } @@ -206,6 +208,7 @@ func TestLibSQLDelegate_ListSessionMessages(t *testing.T) { } func TestLibSQLDelegate_CountSessionMessages(t *testing.T) { + t.Parallel() tests := []struct { name string setup func(t *testing.T, d *LibSQLDelegate, ctx context.Context) @@ -257,7 +260,7 @@ func TestLibSQLDelegate_CountSessionMessages(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() if tt.setup != nil { tt.setup(t, d, ctx) } diff --git a/pkg/memory/delegate/sqlite_test.go b/pkg/memory/delegate/sqlite_test.go index bce5372c0..99aff2597 100644 --- a/pkg/memory/delegate/sqlite_test.go +++ b/pkg/memory/delegate/sqlite_test.go @@ -1,7 +1,6 @@ package delegate import ( - "context" "fmt" "testing" @@ -15,7 +14,7 @@ func newTestDelegate(t *testing.T) *LibSQLDelegate { if err != nil { t.Fatalf("NewLibSQLInMemory: %v", err) } - if err := d.Init(context.Background()); err != nil { + if err := d.Init(t.Context()); err != nil { t.Fatalf("Init: %v", err) } t.Cleanup(func() { d.Close() }) @@ -23,8 +22,9 @@ func newTestDelegate(t *testing.T) *LibSQLDelegate { } func TestLibSQLDelegate_WorkingContext(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() // Initially nil wc, err := d.GetWorkingContext(ctx, "agent-1", "sess-1") @@ -63,8 +63,9 @@ func TestLibSQLDelegate_WorkingContext(t *testing.T) { } func TestLibSQLDelegate_RecallItemCRUD(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() item := &memory.RecallItem{ ID: ids.New(), @@ -156,8 +157,9 @@ func testEmbedding768(seed ...float32) []float32 { } func TestLibSQLDelegate_ArchivalChunkCRUD(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() parentRecall := &memory.RecallItem{ ID: ids.New(), AgentID: "agent-1", SessionKey: "sess-1", @@ -237,8 +239,9 @@ func TestLibSQLDelegate_ArchivalChunkCRUD(t *testing.T) { } func TestLibSQLDelegate_SummaryCRUD(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() summary := &memory.MemorySummary{ ID: ids.New(), @@ -266,8 +269,9 @@ func TestLibSQLDelegate_SummaryCRUD(t *testing.T) { } func TestLibSQLDelegate_KeywordSearch(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() items := []*memory.RecallItem{ {ID: ids.New(), AgentID: "agent-1", SessionKey: "s1", Role: "user", Sector: memory.SectorSemantic, Importance: 0.9, Content: "Go programming language is fast"}, @@ -291,8 +295,9 @@ func TestLibSQLDelegate_KeywordSearch(t *testing.T) { } func TestLibSQLDelegate_Counts(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() // Initial counts should be zero rc, err := d.CountRecallItems(ctx, "agent-1", "") @@ -328,8 +333,9 @@ func TestLibSQLDelegate_Counts(t *testing.T) { } func TestLibSQLDelegate_FTSSearch(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() // Insert recall items with searchable content items := []*memory.RecallItem{ @@ -382,8 +388,9 @@ func TestLibSQLDelegate_FTSSearch(t *testing.T) { } func TestLibSQLDelegate_VectorSearch(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() // Insert archival chunks with 768-dim embeddings (schema requires F32_BLOB(768)) embData := [][]float32{ @@ -439,6 +446,7 @@ func TestLibSQLDelegate_VectorSearch(t *testing.T) { } func TestBuildFTSMatchExpr(t *testing.T) { + t.Parallel() tests := []struct { input string expected string @@ -465,6 +473,7 @@ func TestBuildFTSMatchExpr(t *testing.T) { } func TestVectorToString(t *testing.T) { + t.Parallel() tests := []struct { input memory.Embedding expected string @@ -485,7 +494,10 @@ func TestVectorToString(t *testing.T) { } func TestExtractVector(t *testing.T) { + t.Parallel( // Round-trip test: Embedding.Value() -> blob -> extractVector + ) + original := memory.Embedding{0.1, -0.2, 0.3, 0.99, -0.01} dv, err := original.Value() if err != nil { @@ -510,6 +522,7 @@ func TestExtractVector(t *testing.T) { } func TestLibSQLDelegate_Capabilities(t *testing.T) { + t.Parallel() d := newTestDelegate(t) // After Init(), capabilities should have been probed @@ -526,13 +539,14 @@ func TestLibSQLDelegate_Capabilities(t *testing.T) { } // Calling detect again should be a no-op (idempotent) - d.detectCapabilities(context.Background()) + d.detectCapabilities(t.Context()) if d.HasFTS() != hasFTS || d.HasVectorSearch() != hasVec { t.Error("detectCapabilities changed results on second call — not idempotent") } } func TestEmbeddingValueScanRoundTrip(t *testing.T) { + t.Parallel() vectors := []memory.Embedding{ {0.0, 1.0, -1.0, 0.5, -0.5}, {3.4028235e+38, -3.4028235e+38}, // max float32 @@ -578,8 +592,9 @@ func TestEmbeddingValueScanRoundTrip(t *testing.T) { } func TestIntegration_FullStackNoDisk(t *testing.T) { + t.Parallel() d := newTestDelegate(t) - ctx := context.Background() + ctx := t.Context() agentID := "integration-agent" t.Run("KV_Store", func(t *testing.T) { diff --git a/pkg/memory/integration_test.go b/pkg/memory/integration_test.go index 1b9d9803f..e2bf5c8f6 100644 --- a/pkg/memory/integration_test.go +++ b/pkg/memory/integration_test.go @@ -3,7 +3,6 @@ package memory_test import ( - "context" "testing" "github.com/ZanzyTHEbar/dragonscale/pkg/ids" @@ -23,7 +22,7 @@ func setupFullStack(t *testing.T) (*memstore.MemoryStore, *delegate.LibSQLDelega t.Helper() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err, "create in-memory delegate") - require.NoError(t, del.Init(context.Background()), "run goose migrations") + require.NoError(t, del.Init(t.Context()), "run goose migrations") t.Cleanup(func() { del.Close() }) chunker := memstore.NewMarkdownChunker(memstore.DefaultMarkdownChunkerConfig()) @@ -33,21 +32,23 @@ func setupFullStack(t *testing.T) (*memstore.MemoryStore, *delegate.LibSQLDelega } func TestIntegration_GooseMigrationIdempotent(t *testing.T) { + t.Parallel() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) defer del.Close() - ctx := context.Background() + ctx := t.Context() require.NoError(t, del.Init(ctx), "first migration up") require.NoError(t, del.Init(ctx), "idempotent re-Init") } func TestIntegration_GooseMigration_DownUpRoundTrip(t *testing.T) { + t.Parallel() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) defer del.Close() - ctx := context.Background() + ctx := t.Context() require.NoError(t, del.Init(ctx), "initial up") @@ -83,8 +84,9 @@ func TestIntegration_GooseMigration_DownUpRoundTrip(t *testing.T) { } func TestIntegration_BlobPK_RoundTrip(t *testing.T) { + t.Parallel() store, _ := setupFullStack(t) - ctx := context.Background() + ctx := t.Context() item := &memory.RecallItem{ AgentID: testAgent, @@ -106,8 +108,9 @@ func TestIntegration_BlobPK_RoundTrip(t *testing.T) { } func TestIntegration_ArchivalChunking_EndToEnd(t *testing.T) { + t.Parallel() store, del := setupFullStack(t) - ctx := context.Background() + ctx := t.Context() longContent := "# Architecture Notes\n\nThe system uses hexagonal architecture with ports and adapters.\n\n" longContent += "## Database Layer\n\nWe use libSQL with BLOB primary keys for storage efficiency.\n\n" @@ -131,8 +134,9 @@ func TestIntegration_ArchivalChunking_EndToEnd(t *testing.T) { } func TestIntegration_FTS5Search(t *testing.T) { + t.Parallel() store, _ := setupFullStack(t) - ctx := context.Background() + ctx := t.Context() items := []struct { content string @@ -172,8 +176,9 @@ func TestIntegration_FTS5Search(t *testing.T) { } func TestIntegration_CascadeDelete(t *testing.T) { + t.Parallel() store, del := setupFullStack(t) - ctx := context.Background() + ctx := t.Context() recallID, err := store.StoreArchival(ctx, "Archival content to be cascade deleted", testAgent, map[string]string{ "agent_id": testAgent, @@ -195,8 +200,9 @@ func TestIntegration_CascadeDelete(t *testing.T) { } func TestIntegration_ToolOffload(t *testing.T) { + t.Parallel() store, _ := setupFullStack(t) - ctx := context.Background() + ctx := t.Context() largeResult := "" for i := 0; i < 500; i++ { @@ -211,8 +217,9 @@ func TestIntegration_ToolOffload(t *testing.T) { } func TestIntegration_WorkingContext_Persistence(t *testing.T) { + t.Parallel() _, del := setupFullStack(t) - ctx := context.Background() + ctx := t.Context() require.NoError(t, del.UpsertWorkingContext(ctx, testAgent, testSession, "initial state")) @@ -228,8 +235,9 @@ func TestIntegration_WorkingContext_Persistence(t *testing.T) { } func TestIntegration_Summary_CRUD(t *testing.T) { + t.Parallel() store, del := setupFullStack(t) - ctx := context.Background() + ctx := t.Context() summary := &memory.MemorySummary{ AgentID: testAgent, @@ -249,8 +257,9 @@ func TestIntegration_Summary_CRUD(t *testing.T) { } func TestIntegration_IDUniqueness_AcrossEntities(t *testing.T) { + t.Parallel() store, _ := setupFullStack(t) - ctx := context.Background() + ctx := t.Context() seenIDs := make(map[ids.UUID]string) @@ -284,8 +293,9 @@ func TestIntegration_IDUniqueness_AcrossEntities(t *testing.T) { } func TestIntegration_ContextPressure(t *testing.T) { + t.Parallel() store, _ := setupFullStack(t) - ctx := context.Background() + ctx := t.Context() pressure, err := store.ContextUsage(ctx, testAgent, testSession) require.NoError(t, err) diff --git a/pkg/memory/kernel_contract_test.go b/pkg/memory/kernel_contract_test.go index 0ef4ce8ae..e4e2a9757 100644 --- a/pkg/memory/kernel_contract_test.go +++ b/pkg/memory/kernel_contract_test.go @@ -9,6 +9,7 @@ import ( ) func TestActiveContextProjection_TotalTokens(t *testing.T) { + t.Parallel() p := &ActiveContextProjection{ Segments: []ProjectionSegment{ {Tokens: 120}, @@ -20,6 +21,7 @@ func TestActiveContextProjection_TotalTokens(t *testing.T) { } func TestActiveContextProjection_HasLosslessRefs(t *testing.T) { + t.Parallel() now := time.Now().UTC() p := &ActiveContextProjection{ Segments: []ProjectionSegment{ diff --git a/pkg/memory/migrate_sessions_test.go b/pkg/memory/migrate_sessions_test.go index e2ee209ca..d73a3a1eb 100644 --- a/pkg/memory/migrate_sessions_test.go +++ b/pkg/memory/migrate_sessions_test.go @@ -115,6 +115,7 @@ func writeSessionFile(t *testing.T, dir, name string, sess SessionFile) { } func TestMigrateFileSessions_Basic(t *testing.T) { + t.Parallel() sessDir := t.TempDir() del := newMockDelegate() @@ -182,6 +183,7 @@ func TestMigrateFileSessions_Basic(t *testing.T) { } func TestMigrateFileSessions_Idempotent(t *testing.T) { + t.Parallel() sessDir := t.TempDir() del := newMockDelegate() @@ -217,6 +219,7 @@ func TestMigrateFileSessions_Idempotent(t *testing.T) { } func TestMigrateFileSessions_EmptyDir(t *testing.T) { + t.Parallel() sessDir := t.TempDir() del := newMockDelegate() @@ -230,6 +233,7 @@ func TestMigrateFileSessions_EmptyDir(t *testing.T) { } func TestMigrateFileSessions_NonexistentDir(t *testing.T) { + t.Parallel() del := newMockDelegate() result, err := MigrateFileSessions(t.Context(), del, pkg.NAME, "/nonexistent/path") @@ -242,6 +246,7 @@ func TestMigrateFileSessions_NonexistentDir(t *testing.T) { } func TestMigrateFileSessions_SkipsEmptyMessages(t *testing.T) { + t.Parallel() sessDir := t.TempDir() del := newMockDelegate() @@ -264,6 +269,7 @@ func TestMigrateFileSessions_SkipsEmptyMessages(t *testing.T) { } func TestMigrateFileSessions_FallbackKey(t *testing.T) { + t.Parallel() sessDir := t.TempDir() del := newMockDelegate() @@ -288,6 +294,7 @@ func TestMigrateFileSessions_FallbackKey(t *testing.T) { } func TestMigrateFileSessions_MalformedJSON(t *testing.T) { + t.Parallel() sessDir := t.TempDir() del := newMockDelegate() diff --git a/pkg/memory/observation/observation_test.go b/pkg/memory/observation/observation_test.go index 4716457dd..8a7f173b6 100644 --- a/pkg/memory/observation/observation_test.go +++ b/pkg/memory/observation/observation_test.go @@ -7,24 +7,30 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestNewObservation_ThreeDateModel(t *testing.T) { + t.Parallel() ref := time.Date(2026, 2, 16, 10, 0, 0, 0, time.UTC) obs := time.Date(2026, 2, 18, 14, 30, 0, 0, time.UTC) o := NewObservation("User prefers Go over Rust", PriorityNotable, ref, obs) - assert.Equal(t, "User prefers Go over Rust", o.Content) - assert.Equal(t, PriorityNotable, o.Priority) - assert.Equal(t, ref.Unix(), o.ReferencedAt) - assert.Equal(t, obs.Unix(), o.ObservedAt) - assert.Equal(t, "2 days ago", o.RelativeDate) + assert.Empty(t, cmp.Diff(Observation{ + Content: "User prefers Go over Rust", + Priority: PriorityNotable, + ObservedAt: obs.Unix(), + ReferencedAt: ref.Unix(), + RelativeDate: "2 days ago", + }, o)) } func TestRelativeDate(t *testing.T) { + t.Parallel() now := time.Date(2026, 2, 18, 14, 0, 0, 0, time.UTC) tests := []struct { @@ -53,6 +59,7 @@ func TestRelativeDate(t *testing.T) { } func TestPriorityEmoji(t *testing.T) { + t.Parallel() assert.Equal(t, "🔴", PriorityCritical.Emoji()) assert.Equal(t, "🟡", PriorityNotable.Emoji()) assert.Equal(t, "🔵", PriorityInformational.Emoji()) @@ -60,6 +67,7 @@ func TestPriorityEmoji(t *testing.T) { } func TestFormatBlock(t *testing.T) { + t.Parallel() now := time.Date(2026, 2, 18, 14, 30, 0, 0, time.UTC) obs := []Observation{ NewObservation("Decision: use SQLite", PriorityCritical, now, now), @@ -75,11 +83,13 @@ func TestFormatBlock(t *testing.T) { } func TestFormatBlock_Empty(t *testing.T) { + t.Parallel() assert.Equal(t, "", FormatBlock(nil)) assert.Equal(t, "", FormatBlock([]Observation{})) } func TestMarshalUnmarshalRoundTrip(t *testing.T) { + t.Parallel() now := time.Now() obs := []Observation{ NewObservation("Fact A", PriorityCritical, now, now), @@ -93,23 +103,25 @@ func TestMarshalUnmarshalRoundTrip(t *testing.T) { parsed, err := UnmarshalObservations(data) require.NoError(t, err) assert.Len(t, parsed, 2) - assert.Equal(t, "Fact A", parsed[0].Content) - assert.Equal(t, PriorityCritical, parsed[0].Priority) + assert.Empty(t, cmp.Diff(obs[0], parsed[0])) } func TestUnmarshalObservations_Empty(t *testing.T) { + t.Parallel() obs, err := UnmarshalObservations("") assert.NoError(t, err) assert.Nil(t, obs) } func TestEstimateTokens(t *testing.T) { + t.Parallel() tokens := EstimateTokens("Hello, world!") assert.True(t, tokens > 0) assert.True(t, tokens < 20) } func TestParseObservations(t *testing.T) { + t.Parallel() now := time.Now() response := `critical|User decided to migrate to SQLite notable|Prefers hexagonal architecture @@ -119,13 +131,31 @@ notable|` obs := parseObservations(response, now) assert.Len(t, obs, 3) - assert.Equal(t, PriorityCritical, obs[0].Priority) - assert.Equal(t, "User decided to migrate to SQLite", obs[0].Content) - assert.Equal(t, PriorityNotable, obs[1].Priority) - assert.Equal(t, PriorityInformational, obs[2].Priority) + assert.Empty(t, cmp.Diff(Observation{ + Content: "User decided to migrate to SQLite", + Priority: PriorityCritical, + ObservedAt: now.Unix(), + ReferencedAt: now.Unix(), + RelativeDate: relativeDate(now, now), + }, obs[0])) + assert.Empty(t, cmp.Diff(Observation{ + Content: "Prefers hexagonal architecture", + Priority: PriorityNotable, + ObservedAt: now.Unix(), + ReferencedAt: now.Unix(), + RelativeDate: relativeDate(now, now), + }, obs[1])) + assert.Empty(t, cmp.Diff(Observation{ + Content: "Uses VS Code as primary editor", + Priority: PriorityInformational, + ObservedAt: now.Unix(), + ReferencedAt: now.Unix(), + RelativeDate: relativeDate(now, now), + }, obs[2])) } func TestObserver_ShouldObserve(t *testing.T) { + t.Parallel() mockModel := func(_ context.Context, _ string) (string, error) { return "", nil } @@ -140,6 +170,7 @@ func TestObserver_ShouldObserve(t *testing.T) { } func TestObserver_Observe(t *testing.T) { + t.Parallel() mockModel := func(_ context.Context, prompt string) (string, error) { return "critical|Important decision made\nnotable|User preference noted", nil } @@ -151,13 +182,14 @@ func TestObserver_Observe(t *testing.T) { {Role: "assistant", Content: "Good choice for embedded use cases"}, } - obs, err := o.Observe(context.Background(), msgs, nil) + obs, err := o.Observe(t.Context(), msgs, nil) require.NoError(t, err) assert.Len(t, obs, 2) assert.Equal(t, PriorityCritical, obs[0].Priority) } func TestReflector_ShouldReflect(t *testing.T) { + t.Parallel() mockModel := func(_ context.Context, _ string) (string, error) { return "", nil } @@ -175,6 +207,7 @@ func TestReflector_ShouldReflect(t *testing.T) { } func TestReflector_Reflect(t *testing.T) { + t.Parallel() mockModel := func(_ context.Context, prompt string) (string, error) { return "KEEP 0\nDROP 1\nKEEP 2", nil } @@ -188,7 +221,7 @@ func TestReflector_Reflect(t *testing.T) { NewObservation("Notable thing", PriorityNotable, now, now), } - kept, err := r.Reflect(context.Background(), obs) + kept, err := r.Reflect(t.Context(), obs) require.NoError(t, err) assert.Len(t, kept, 2) assert.Equal(t, "Critical fact", kept[0].Content) @@ -196,6 +229,7 @@ func TestReflector_Reflect(t *testing.T) { } func TestReflector_ReflectFallbackKeepsCritical(t *testing.T) { + t.Parallel() mockModel := func(_ context.Context, _ string) (string, error) { return "garbage output", nil } @@ -208,13 +242,14 @@ func TestReflector_ReflectFallbackKeepsCritical(t *testing.T) { NewObservation("Can drop", PriorityInformational, now, now), } - kept, err := r.Reflect(context.Background(), obs) + kept, err := r.Reflect(t.Context(), obs) require.NoError(t, err) assert.Len(t, kept, 1) assert.Equal(t, "Must keep", kept[0].Content) } func TestParsePriority(t *testing.T) { + t.Parallel() assert.Equal(t, PriorityCritical, parsePriority("critical")) assert.Equal(t, PriorityCritical, parsePriority("CRITICAL")) assert.Equal(t, PriorityNotable, parsePriority("notable")) @@ -223,6 +258,7 @@ func TestParsePriority(t *testing.T) { } func TestParseKeptIndices(t *testing.T) { + t.Parallel() now := time.Now() obs := []Observation{ NewObservation("A", PriorityCritical, now, now), diff --git a/pkg/memory/store/cached_embedder_test.go b/pkg/memory/store/cached_embedder_test.go index 0ab1f7d6d..bd1861ce8 100644 --- a/pkg/memory/store/cached_embedder_test.go +++ b/pkg/memory/store/cached_embedder_test.go @@ -42,9 +42,10 @@ func (e *countingEmbedder) Dimensions() int { return e.dims } func (e *countingEmbedder) Model() string { return "test-model" } func TestCachedEmbedder_CachesIdenticalText(t *testing.T) { + t.Parallel() inner := &countingEmbedder{dims: 8} cached := NewCachedEmbedder(inner, DefaultCachedEmbedderConfig()) - ctx := context.Background() + ctx := t.Context() // First call — should hit inner vec1, err := cached.Embed(ctx, "hello world") @@ -76,9 +77,10 @@ func TestCachedEmbedder_CachesIdenticalText(t *testing.T) { } func TestCachedEmbedder_DifferentTextHitsInner(t *testing.T) { + t.Parallel() inner := &countingEmbedder{dims: 4} cached := NewCachedEmbedder(inner, DefaultCachedEmbedderConfig()) - ctx := context.Background() + ctx := t.Context() cached.Embed(ctx, "text A") cached.Embed(ctx, "text B") @@ -93,9 +95,10 @@ func TestCachedEmbedder_DifferentTextHitsInner(t *testing.T) { } func TestCachedEmbedder_BatchPartialCache(t *testing.T) { + t.Parallel() inner := &countingEmbedder{dims: 4} cached := NewCachedEmbedder(inner, DefaultCachedEmbedderConfig()) - ctx := context.Background() + ctx := t.Context() // Pre-cache one text cached.Embed(ctx, "cached text") @@ -126,9 +129,10 @@ func TestCachedEmbedder_BatchPartialCache(t *testing.T) { } func TestCachedEmbedder_BatchAllCached(t *testing.T) { + t.Parallel() inner := &countingEmbedder{dims: 4} cached := NewCachedEmbedder(inner, DefaultCachedEmbedderConfig()) - ctx := context.Background() + ctx := t.Context() // Pre-cache all texts cached.Embed(ctx, "A") @@ -148,6 +152,7 @@ func TestCachedEmbedder_BatchAllCached(t *testing.T) { } func TestCachedEmbedder_Dimensions(t *testing.T) { + t.Parallel() inner := &countingEmbedder{dims: 768} cached := NewCachedEmbedder(inner, DefaultCachedEmbedderConfig()) if cached.Dimensions() != 768 { @@ -156,6 +161,7 @@ func TestCachedEmbedder_Dimensions(t *testing.T) { } func TestCachedEmbedder_Model(t *testing.T) { + t.Parallel() inner := &countingEmbedder{dims: 4} cached := NewCachedEmbedder(inner, DefaultCachedEmbedderConfig()) if cached.Model() != "test-model" { @@ -164,12 +170,13 @@ func TestCachedEmbedder_Model(t *testing.T) { } func TestCachedEmbedder_MaxEntries(t *testing.T) { + t.Parallel() inner := &countingEmbedder{dims: 4} cached := NewCachedEmbedder(inner, CachedEmbedderConfig{ MaxEntries: 3, TTL: time.Hour, }) - ctx := context.Background() + ctx := t.Context() // Fill cache cached.Embed(ctx, "A") diff --git a/pkg/memory/store/chunker_test.go b/pkg/memory/store/chunker_test.go index f2fb970a2..e8189d51e 100644 --- a/pkg/memory/store/chunker_test.go +++ b/pkg/memory/store/chunker_test.go @@ -9,6 +9,7 @@ import ( ) func TestMarkdownChunker_BasicSplit(t *testing.T) { + t.Parallel() chunker := NewMarkdownChunker(MarkdownChunkerConfig{ ChunkSize: 100, ChunkOverlap: 20, @@ -26,6 +27,7 @@ func TestMarkdownChunker_BasicSplit(t *testing.T) { } func TestMarkdownChunker_SmallContent(t *testing.T) { + t.Parallel() chunker := NewMarkdownChunker(DefaultMarkdownChunkerConfig()) chunks, err := chunker.Chunk("Short text.") @@ -35,6 +37,7 @@ func TestMarkdownChunker_SmallContent(t *testing.T) { } func TestMarkdownChunker_PreservesMarkdownStructure(t *testing.T) { + t.Parallel() chunker := NewMarkdownChunker(MarkdownChunkerConfig{ ChunkSize: 200, ChunkOverlap: 40, @@ -81,6 +84,7 @@ Even more content follows here with additional details and explanations that mak } func TestMarkdownChunker_EmptyContent(t *testing.T) { + t.Parallel() chunker := NewMarkdownChunker(DefaultMarkdownChunkerConfig()) chunks, err := chunker.Chunk("") require.NoError(t, err) @@ -88,6 +92,7 @@ func TestMarkdownChunker_EmptyContent(t *testing.T) { } func TestMarkdownChunker_DefaultConfig(t *testing.T) { + t.Parallel() cfg := DefaultMarkdownChunkerConfig() assert.Equal(t, 1600, cfg.ChunkSize) assert.Equal(t, 320, cfg.ChunkOverlap) diff --git a/pkg/memory/store/embed_factory_test.go b/pkg/memory/store/embed_factory_test.go index 882dfb501..7e56f850d 100644 --- a/pkg/memory/store/embed_factory_test.go +++ b/pkg/memory/store/embed_factory_test.go @@ -1,15 +1,17 @@ package store import ( - jsonv2 "github.com/go-json-experiment/json" "net/http" "net/http/httptest" "testing" + jsonv2 "github.com/go-json-experiment/json" + "github.com/ZanzyTHEbar/dragonscale/pkg/config" ) func TestNewEmbedderFromConfig_EmptyProvider(t *testing.T) { + t.Parallel() emb, err := NewEmbedderFromConfig(config.EmbeddingConfig{}, config.ProvidersConfig{}) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -20,6 +22,7 @@ func TestNewEmbedderFromConfig_EmptyProvider(t *testing.T) { } func TestNewEmbedderFromConfig_UnknownProvider(t *testing.T) { + t.Parallel() _, err := NewEmbedderFromConfig(config.EmbeddingConfig{Provider: "nonexistent"}, config.ProvidersConfig{}) if err == nil { t.Error("expected error for unknown provider") @@ -27,6 +30,7 @@ func TestNewEmbedderFromConfig_UnknownProvider(t *testing.T) { } func TestNewEmbedderFromConfig_OpenAI_NoKey(t *testing.T) { + t.Parallel() _, err := NewEmbedderFromConfig( config.EmbeddingConfig{Provider: "openai"}, config.ProvidersConfig{}, @@ -37,6 +41,7 @@ func TestNewEmbedderFromConfig_OpenAI_NoKey(t *testing.T) { } func TestNewEmbedderFromConfig_OpenAI_FallbackKey(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respData, _ := jsonv2.Marshal(map[string]interface{}{ "data": []map[string]interface{}{ @@ -70,6 +75,7 @@ func TestNewEmbedderFromConfig_OpenAI_FallbackKey(t *testing.T) { } func TestNewEmbedderFromConfig_Ollama(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respData, _ := jsonv2.Marshal(map[string]interface{}{ "embeddings": [][]float32{{0.1, 0.2, 0.3}}, @@ -98,6 +104,7 @@ func TestNewEmbedderFromConfig_Ollama(t *testing.T) { } func TestNewEmbedderFromConfig_Ollama_FallbackBase(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respData, _ := jsonv2.Marshal(map[string]interface{}{ "embeddings": [][]float32{{0.5}}, @@ -121,6 +128,7 @@ func TestNewEmbedderFromConfig_Ollama_FallbackBase(t *testing.T) { } func TestNewEmbedderFromConfig_CaseInsensitive(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respData, _ := jsonv2.Marshal(map[string]interface{}{ "embeddings": [][]float32{{0.1}}, diff --git a/pkg/memory/store/embed_ollama_test.go b/pkg/memory/store/embed_ollama_test.go index 695f30f5e..32f8c329e 100644 --- a/pkg/memory/store/embed_ollama_test.go +++ b/pkg/memory/store/embed_ollama_test.go @@ -1,14 +1,15 @@ package store import ( - "context" - jsonv2 "github.com/go-json-experiment/json" "net/http" "net/http/httptest" "testing" + + jsonv2 "github.com/go-json-experiment/json" ) func TestOllamaEmbedder_Embed(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/embed" { t.Errorf("expected /api/embed, got %s", r.URL.Path) @@ -35,7 +36,7 @@ func TestOllamaEmbedder_Embed(t *testing.T) { Model: "test-model", }) - vec, err := e.Embed(context.Background(), "hello world") + vec, err := e.Embed(t.Context(), "hello world") if err != nil { t.Fatalf("Embed: %v", err) } @@ -53,6 +54,7 @@ func TestOllamaEmbedder_Embed(t *testing.T) { } func TestOllamaEmbedder_EmbedBatch(t *testing.T) { + t.Parallel() callCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callCount++ @@ -65,7 +67,7 @@ func TestOllamaEmbedder_EmbedBatch(t *testing.T) { e := NewOllamaEmbedder(OllamaEmbedderConfig{Base: srv.URL}) - vecs, err := e.EmbedBatch(context.Background(), []string{"a", "b", "c"}) + vecs, err := e.EmbedBatch(t.Context(), []string{"a", "b", "c"}) if err != nil { t.Fatalf("EmbedBatch: %v", err) } @@ -78,6 +80,7 @@ func TestOllamaEmbedder_EmbedBatch(t *testing.T) { } func TestOllamaEmbedder_ServerError(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("model not found")) @@ -85,13 +88,14 @@ func TestOllamaEmbedder_ServerError(t *testing.T) { defer srv.Close() e := NewOllamaEmbedder(OllamaEmbedderConfig{Base: srv.URL}) - _, err := e.Embed(context.Background(), "test") + _, err := e.Embed(t.Context(), "test") if err == nil { t.Error("expected error for server error response") } } func TestOllamaEmbedder_EmptyResponse(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respData, _ := jsonv2.Marshal(ollamaEmbedResponse{Embeddings: [][]float32{}}) w.Write(respData) @@ -99,13 +103,14 @@ func TestOllamaEmbedder_EmptyResponse(t *testing.T) { defer srv.Close() e := NewOllamaEmbedder(OllamaEmbedderConfig{Base: srv.URL}) - _, err := e.Embed(context.Background(), "test") + _, err := e.Embed(t.Context(), "test") if err == nil { t.Error("expected error for empty embeddings") } } func TestOllamaEmbedder_DefaultModel(t *testing.T) { + t.Parallel() e := NewOllamaEmbedder(OllamaEmbedderConfig{}) if e.Model() != defaultOllamaModel { t.Errorf("expected %q, got %q", defaultOllamaModel, e.Model()) diff --git a/pkg/memory/store/embed_openai_test.go b/pkg/memory/store/embed_openai_test.go index c0736cd20..c407a432e 100644 --- a/pkg/memory/store/embed_openai_test.go +++ b/pkg/memory/store/embed_openai_test.go @@ -1,14 +1,15 @@ package store import ( - "context" - jsonv2 "github.com/go-json-experiment/json" "net/http" "net/http/httptest" "testing" + + jsonv2 "github.com/go-json-experiment/json" ) func TestOpenAIEmbedder_Embed(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/embeddings" { t.Errorf("expected /embeddings, got %s", r.URL.Path) @@ -40,7 +41,7 @@ func TestOpenAIEmbedder_Embed(t *testing.T) { APIKey: "test-key", }) - vec, err := e.Embed(context.Background(), "hello") + vec, err := e.Embed(t.Context(), "hello") if err != nil { t.Fatalf("Embed: %v", err) } @@ -56,6 +57,7 @@ func TestOpenAIEmbedder_Embed(t *testing.T) { } func TestOpenAIEmbedder_EmbedBatch(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req openAIEmbedRequest jsonv2.UnmarshalRead(r.Body, &req) @@ -84,7 +86,7 @@ func TestOpenAIEmbedder_EmbedBatch(t *testing.T) { APIKey: "k", }) - vecs, err := e.EmbedBatch(context.Background(), []string{"a", "b", "c"}) + vecs, err := e.EmbedBatch(t.Context(), []string{"a", "b", "c"}) if err != nil { t.Fatalf("EmbedBatch: %v", err) } @@ -94,6 +96,7 @@ func TestOpenAIEmbedder_EmbedBatch(t *testing.T) { } func TestOpenAIEmbedder_SingleTextNotArray(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req openAIEmbedRequest jsonv2.UnmarshalRead(r.Body, &req) @@ -111,13 +114,14 @@ func TestOpenAIEmbedder_SingleTextNotArray(t *testing.T) { defer srv.Close() e := NewOpenAIEmbedder(OpenAIEmbedderConfig{Base: srv.URL, APIKey: "k"}) - _, err := e.Embed(context.Background(), "single") + _, err := e.Embed(t.Context(), "single") if err != nil { t.Fatalf("Embed: %v", err) } } func TestOpenAIEmbedder_ServerError(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(`{"error":{"message":"invalid api key"}}`)) @@ -125,13 +129,14 @@ func TestOpenAIEmbedder_ServerError(t *testing.T) { defer srv.Close() e := NewOpenAIEmbedder(OpenAIEmbedderConfig{Base: srv.URL, APIKey: "bad"}) - _, err := e.Embed(context.Background(), "test") + _, err := e.Embed(t.Context(), "test") if err == nil { t.Error("expected error for 401 response") } } func TestOpenAIEmbedder_DefaultModel(t *testing.T) { + t.Parallel() e := NewOpenAIEmbedder(OpenAIEmbedderConfig{APIKey: "k"}) if e.Model() != defaultOpenAIModel { t.Errorf("expected %q, got %q", defaultOpenAIModel, e.Model()) @@ -139,6 +144,7 @@ func TestOpenAIEmbedder_DefaultModel(t *testing.T) { } func TestOpenAIEmbedder_NoAuth(t *testing.T) { + t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Authorization") != "" { t.Error("expected no auth header when key is empty") @@ -151,7 +157,7 @@ func TestOpenAIEmbedder_NoAuth(t *testing.T) { defer srv.Close() e := NewOpenAIEmbedder(OpenAIEmbedderConfig{Base: srv.URL}) - _, err := e.Embed(context.Background(), "test") + _, err := e.Embed(t.Context(), "test") if err != nil { t.Fatalf("Embed: %v", err) } diff --git a/pkg/memory/store/memory_store_test.go b/pkg/memory/store/memory_store_test.go index 1e4ae1967..173efb018 100644 --- a/pkg/memory/store/memory_store_test.go +++ b/pkg/memory/store/memory_store_test.go @@ -67,7 +67,7 @@ func deterministicVec(text string, dim int) memory.Embedding { func newTestStore(t *testing.T, withEmbedder bool) *MemoryStore { t.Helper() - ctx := context.Background() + ctx := t.Context() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) @@ -95,7 +95,8 @@ func newTestStore(t *testing.T, withEmbedder bool) *MemoryStore { } func TestWorkingContext_SetAndGet(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) // Initially empty @@ -122,7 +123,8 @@ func TestWorkingContext_SetAndGet(t *testing.T) { } func TestWorkingContext_IsolatedBySessions(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) err := store.SetWorkingContext(ctx, "agent-1", "session-a", "Context A") @@ -140,7 +142,8 @@ func TestWorkingContext_IsolatedBySessions(t *testing.T) { } func TestRecall_CRUD(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) item := &memory.RecallItem{ @@ -187,7 +190,8 @@ func TestRecall_CRUD(t *testing.T) { } func TestArchival_StoreAndRetrieve(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, true) // Store a multi-chunk document @@ -209,7 +213,8 @@ func TestArchival_StoreAndRetrieve(t *testing.T) { } func TestArchival_WithoutEmbedder(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) // no embedder content := "Short archival content for testing without embeddings." @@ -226,7 +231,8 @@ func TestArchival_WithoutEmbedder(t *testing.T) { } func TestSearch_KeywordOnly(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) // Seed some recall items @@ -251,7 +257,8 @@ func TestSearch_KeywordOnly(t *testing.T) { } func TestSearch_HybridWithEmbeddings(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, true) // Seed recall items @@ -283,7 +290,8 @@ func TestSearch_HybridWithEmbeddings(t *testing.T) { } func TestSearch_ShadowModeUsesBaselineAndTracksParity(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) // Baseline recall hit. @@ -319,7 +327,8 @@ func TestSearch_ShadowModeUsesBaselineAndTracksParity(t *testing.T) { } func TestSearch_DoesNotPromoteWithoutAugmentedSignals(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) gates := retrievalPromotionGates{ @@ -361,7 +370,8 @@ func TestSearch_DoesNotPromoteWithoutAugmentedSignals(t *testing.T) { } func TestSearch_PromoteOnlyOnGateWin(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) gates := retrievalPromotionGates{ @@ -406,7 +416,8 @@ func TestSearch_PromoteOnlyOnGateWin(t *testing.T) { } func TestSearch_FastRollbackPreservesBaselinePath(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) del, ok := store.delegate.(*delegate.LibSQLDelegate) @@ -492,7 +503,8 @@ func TestSearch_FastRollbackPreservesBaselinePath(t *testing.T) { } func TestUpdateRetrievalPolicy_PersistFailuresDoNotBlockTransitions(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, true) baseDelegate := store.delegate @@ -530,7 +542,8 @@ func TestUpdateRetrievalPolicy_PersistFailuresDoNotBlockTransitions(t *testing.T } func TestSearch_ConcurrentRetrievalPolicyUpdates(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, true) require.NoError(t, store.StoreRecall(ctx, &memory.RecallItem{ @@ -576,7 +589,8 @@ func TestSearch_ConcurrentRetrievalPolicyUpdates(t *testing.T) { } func TestContextUsage(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) // Empty system — should be normal pressure @@ -597,7 +611,8 @@ func TestContextUsage(t *testing.T) { } func TestContextUsage_RecallTokenEstimateUsesContent(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) contentA := strings.Repeat("schedule follow-up reminder ", 80) @@ -631,7 +646,8 @@ func TestContextUsage_RecallTokenEstimateUsesContent(t *testing.T) { } func TestContextUsage_PressureLevels(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) @@ -656,6 +672,7 @@ func TestContextUsage_PressureLevels(t *testing.T) { } func TestShouldOffload(t *testing.T) { + t.Parallel() store := &MemoryStore{cfg: Config{OffloadThresholdTokens: 100}} assert.False(t, store.ShouldOffload("short")) @@ -663,7 +680,8 @@ func TestShouldOffload(t *testing.T) { } func TestOffloadToolResult(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) largeContent := strings.Repeat("This is a large tool result that should be offloaded. ", 20) @@ -682,7 +700,8 @@ func TestOffloadToolResult(t *testing.T) { } func TestStoreSummary(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, false) summary := &memory.MemorySummary{ @@ -699,7 +718,8 @@ func TestStoreSummary(t *testing.T) { } func TestDeleteRecall_CascadesArchival(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() store := newTestStore(t, true) // Store archival content (creates recall item + archival chunks) @@ -726,6 +746,7 @@ func TestDeleteRecall_CascadesArchival(t *testing.T) { // --- Retrieval pipeline unit tests --- func TestCosineSimilarity(t *testing.T) { + t.Parallel() tests := []struct { name string a, b memory.Embedding @@ -747,6 +768,7 @@ func TestCosineSimilarity(t *testing.T) { } func TestRRF_MergesTwoSets(t *testing.T) { + t.Parallel() idA, idB, idC := ids.New(), ids.New(), ids.New() set1 := []memory.SearchResult{ {ID: idA, Content: "a", Score: 1.0}, @@ -764,7 +786,10 @@ func TestRRF_MergesTwoSets(t *testing.T) { } func TestRecencyDecay(t *testing.T) { + t.Parallel( // 0 hours age → decay = 1.0 + ) + assert.InDelta(t, 1.0, RecencyDecay(0, 168), 0.001) // 168 hours (1 half-life) → decay = 0.5 @@ -775,6 +800,7 @@ func TestRecencyDecay(t *testing.T) { } func TestApplyRecencyDecay_ReordersByAge(t *testing.T) { + t.Parallel() now := time.Now() idOld, idNew := ids.New(), ids.New() diff --git a/pkg/memory/store/memory_tool_test.go b/pkg/memory/store/memory_tool_test.go index 34c688157..a266d8ec1 100644 --- a/pkg/memory/store/memory_tool_test.go +++ b/pkg/memory/store/memory_tool_test.go @@ -1,10 +1,10 @@ package store import ( - "context" - jsonv2 "github.com/go-json-experiment/json" "testing" + jsonv2 "github.com/go-json-experiment/json" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -17,7 +17,7 @@ func newTestMemoryTool(t *testing.T) *MemoryTool { func executeAndParse(t *testing.T, tool *MemoryTool, input string) *MemoryToolResponse { t.Helper() - ctx := context.Background() + ctx := t.Context() raw, err := tool.Execute(ctx, input) require.NoError(t, err) @@ -27,6 +27,7 @@ func executeAndParse(t *testing.T, tool *MemoryTool, input string) *MemoryToolRe } func TestMemoryTool_WriteAndRead(t *testing.T) { + t.Parallel() tool := newTestMemoryTool(t) // Write @@ -52,6 +53,7 @@ func TestMemoryTool_WriteAndRead(t *testing.T) { } func TestMemoryTool_WriteArchival(t *testing.T) { + t.Parallel() tool := newTestMemoryTool(t) resp := executeAndParse(t, tool, `{"action":"write","content":"Large document content for archival.","tier":"archival","source":"test"}`) @@ -60,6 +62,7 @@ func TestMemoryTool_WriteArchival(t *testing.T) { } func TestMemoryTool_Search(t *testing.T) { + t.Parallel() tool := newTestMemoryTool(t) // Seed data @@ -74,6 +77,7 @@ func TestMemoryTool_Search(t *testing.T) { } func TestMemoryTool_Update(t *testing.T) { + t.Parallel() tool := newTestMemoryTool(t) // Write @@ -91,6 +95,7 @@ func TestMemoryTool_Update(t *testing.T) { } func TestMemoryTool_Delete(t *testing.T) { + t.Parallel() tool := newTestMemoryTool(t) // Write @@ -108,6 +113,7 @@ func TestMemoryTool_Delete(t *testing.T) { } func TestMemoryTool_Status(t *testing.T) { + t.Parallel() tool := newTestMemoryTool(t) resp := executeAndParse(t, tool, `{"action":"status"}`) @@ -118,6 +124,7 @@ func TestMemoryTool_Status(t *testing.T) { } func TestMemoryTool_InvalidAction(t *testing.T) { + t.Parallel() tool := newTestMemoryTool(t) resp := executeAndParse(t, tool, `{"action":"explode"}`) @@ -126,6 +133,7 @@ func TestMemoryTool_InvalidAction(t *testing.T) { } func TestMemoryTool_InvalidJSON(t *testing.T) { + t.Parallel() tool := newTestMemoryTool(t) resp := executeAndParse(t, tool, `not json`) @@ -134,6 +142,7 @@ func TestMemoryTool_InvalidJSON(t *testing.T) { } func TestMemoryTool_MissingRequiredFields(t *testing.T) { + t.Parallel() tool := newTestMemoryTool(t) tests := []struct { diff --git a/pkg/memory/store/queue_test.go b/pkg/memory/store/queue_test.go index f55525140..913205164 100644 --- a/pkg/memory/store/queue_test.go +++ b/pkg/memory/store/queue_test.go @@ -1,7 +1,6 @@ package store import ( - "context" "strings" "testing" @@ -13,7 +12,7 @@ import ( func newTestQueueManager(t *testing.T, contextWindow int) (*QueueManager, *MemoryStore) { t.Helper() - ctx := context.Background() + ctx := t.Context() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) @@ -32,7 +31,8 @@ func newTestQueueManager(t *testing.T, contextWindow int) (*QueueManager, *Memor } func TestQueueManager_NormalPressure(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() qm, _ := newTestQueueManager(t, 100000) decision, err := qm.Evaluate(ctx, "agent-1", "session-1") @@ -42,7 +42,8 @@ func TestQueueManager_NormalPressure(t *testing.T) { } func TestQueueManager_WarnPressure(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() qm, store := newTestQueueManager(t, 100) // tiny window // Fill working context to ~75% of context window. @@ -56,7 +57,8 @@ func TestQueueManager_WarnPressure(t *testing.T) { } func TestQueueManager_OffloadPressure(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() qm, store := newTestQueueManager(t, 100) // tiny window // Fill to ~82% of context window. @@ -70,7 +72,8 @@ func TestQueueManager_OffloadPressure(t *testing.T) { } func TestQueueManager_FlushPressure(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() qm, store := newTestQueueManager(t, 100) // tiny window // Fill to ~88% of context window. @@ -84,7 +87,8 @@ func TestQueueManager_FlushPressure(t *testing.T) { } func TestQueueManager_EvictOldest(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() qm, store := newTestQueueManager(t, 100) // Seed recall items @@ -108,7 +112,8 @@ func TestQueueManager_EvictOldest(t *testing.T) { } func TestQueueManager_EvictEmpty(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() qm, _ := newTestQueueManager(t, 100) evicted, summary, err := qm.EvictOldest(ctx, "agent-1", "empty-session") @@ -118,6 +123,7 @@ func TestQueueManager_EvictEmpty(t *testing.T) { } func TestDefaultQueueManagerConfig(t *testing.T) { + t.Parallel() cfg := DefaultQueueManagerConfig() assert.InDelta(t, 0.70, cfg.WarnThreshold, 0.001) assert.InDelta(t, 0.80, cfg.OffloadThreshold, 0.001) diff --git a/pkg/memory/store/scorer_test.go b/pkg/memory/store/scorer_test.go index 80f408980..4dfd2d93b 100644 --- a/pkg/memory/store/scorer_test.go +++ b/pkg/memory/store/scorer_test.go @@ -1,7 +1,6 @@ package store import ( - "context" "testing" "github.com/ZanzyTHEbar/dragonscale/pkg/memory" @@ -10,8 +9,9 @@ import ( ) func TestHeuristicScorer_BasicScoring(t *testing.T) { + t.Parallel() scorer := NewHeuristicScorer() - ctx := context.Background() + ctx := t.Context() tests := []struct { name string @@ -38,8 +38,9 @@ func TestHeuristicScorer_BasicScoring(t *testing.T) { } func TestHeuristicScorer_SectorClassification(t *testing.T) { + t.Parallel() scorer := NewHeuristicScorer() - ctx := context.Background() + ctx := t.Context() tests := []struct { name string @@ -78,6 +79,7 @@ func TestHeuristicScorer_SectorClassification(t *testing.T) { } func TestParseScoringResponse_ValidJSON(t *testing.T) { + t.Parallel() input := `{"importance": 0.85, "salience": 0.6, "sector": "semantic"}` result, err := parseScoringResponse(input) require.NoError(t, err) @@ -87,6 +89,7 @@ func TestParseScoringResponse_ValidJSON(t *testing.T) { } func TestParseScoringResponse_WithCodeFences(t *testing.T) { + t.Parallel() input := "```json\n{\"importance\": 0.9, \"salience\": 0.3, \"sector\": \"procedural\"}\n```" result, err := parseScoringResponse(input) require.NoError(t, err) @@ -95,6 +98,7 @@ func TestParseScoringResponse_WithCodeFences(t *testing.T) { } func TestParseScoringResponse_ClampsValues(t *testing.T) { + t.Parallel() input := `{"importance": 1.5, "salience": -0.3, "sector": "episodic"}` result, err := parseScoringResponse(input) require.NoError(t, err) @@ -103,6 +107,7 @@ func TestParseScoringResponse_ClampsValues(t *testing.T) { } func TestParseScoringResponse_UnknownSector(t *testing.T) { + t.Parallel() input := `{"importance": 0.5, "salience": 0.5, "sector": "unknown_sector"}` result, err := parseScoringResponse(input) require.NoError(t, err) @@ -110,6 +115,7 @@ func TestParseScoringResponse_UnknownSector(t *testing.T) { } func TestNormalizeSector(t *testing.T) { + t.Parallel() tests := []struct { input string expected memory.Sector diff --git a/pkg/migrate/migrate_test.go b/pkg/migrate/migrate_test.go index dc4bfe368..6731ec6ef 100644 --- a/pkg/migrate/migrate_test.go +++ b/pkg/migrate/migrate_test.go @@ -11,6 +11,7 @@ import ( ) func TestCamelToSnake(t *testing.T) { + t.Parallel() tests := []struct { name string input string @@ -41,6 +42,7 @@ func TestCamelToSnake(t *testing.T) { } func TestConvertKeysToSnake(t *testing.T) { + t.Parallel() input := map[string]interface{}{ "apiKey": "test-key", "apiBase": "https://example.com", @@ -87,6 +89,7 @@ func TestConvertKeysToSnake(t *testing.T) { } func TestLoadOpenClawConfig(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "openclaw.json") @@ -144,6 +147,7 @@ func TestLoadOpenClawConfig(t *testing.T) { } func TestConvertConfig(t *testing.T) { + t.Parallel() t.Run("providers mapping", func(t *testing.T) { data := map[string]interface{}{ "providers": map[string]interface{}{ @@ -301,6 +305,7 @@ func TestConvertConfig(t *testing.T) { } func TestMergeConfig(t *testing.T) { + t.Parallel() t.Run("fills empty fields", func(t *testing.T) { existing := config.DefaultConfig() incoming := config.DefaultConfig() @@ -365,6 +370,7 @@ func TestMergeConfig(t *testing.T) { } func TestPlanWorkspaceMigration(t *testing.T) { + t.Parallel() t.Run("copies available files", func(t *testing.T) { srcDir := t.TempDir() dstDir := t.TempDir() @@ -495,6 +501,7 @@ func TestPlanWorkspaceMigration(t *testing.T) { } func TestFindOpenClawConfig(t *testing.T) { + t.Parallel() t.Run("finds openclaw.json", func(t *testing.T) { tmpDir := t.TempDir() configPath := filepath.Join(tmpDir, "openclaw.json") @@ -549,6 +556,7 @@ func TestFindOpenClawConfig(t *testing.T) { } func TestRewriteWorkspacePath(t *testing.T) { + t.Parallel() tests := []struct { name string input string @@ -569,6 +577,7 @@ func TestRewriteWorkspacePath(t *testing.T) { } func TestRunDryRun(t *testing.T) { + t.Parallel() openclawHome := t.TempDir() picoClawHome := t.TempDir() @@ -610,6 +619,7 @@ func TestRunDryRun(t *testing.T) { } func TestRunFullMigration(t *testing.T) { + t.Parallel() openclawHome := t.TempDir() picoClawHome := t.TempDir() @@ -708,6 +718,7 @@ func TestRunFullMigration(t *testing.T) { } func TestRunOpenClawNotFound(t *testing.T) { + t.Parallel() opts := Options{ OpenClawHome: "/nonexistent/path/to/openclaw", PicoClawHome: t.TempDir(), @@ -720,6 +731,7 @@ func TestRunOpenClawNotFound(t *testing.T) { } func TestRunMutuallyExclusiveFlags(t *testing.T) { + t.Parallel() opts := Options{ ConfigOnly: true, WorkspaceOnly: true, @@ -732,6 +744,7 @@ func TestRunMutuallyExclusiveFlags(t *testing.T) { } func TestBackupFile(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() filePath := filepath.Join(tmpDir, "test.md") os.WriteFile(filePath, []byte("original content"), 0644) @@ -751,6 +764,7 @@ func TestBackupFile(t *testing.T) { } func TestCopyFile(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() srcPath := filepath.Join(tmpDir, "src.md") dstPath := filepath.Join(tmpDir, "dst.md") @@ -771,6 +785,7 @@ func TestCopyFile(t *testing.T) { } func TestRunConfigOnly(t *testing.T) { + t.Parallel() openclawHome := t.TempDir() picoClawHome := t.TempDir() @@ -811,6 +826,7 @@ func TestRunConfigOnly(t *testing.T) { } func TestRunWorkspaceOnly(t *testing.T) { + t.Parallel() openclawHome := t.TempDir() picoClawHome := t.TempDir() diff --git a/pkg/rlm/engine_test.go b/pkg/rlm/engine_test.go index 4610569d4..559b74c4a 100644 --- a/pkg/rlm/engine_test.go +++ b/pkg/rlm/engine_test.go @@ -27,18 +27,22 @@ func errorModel(_ context.Context, _, _ string) (string, uint32, error) { } func TestEngine_SmallContext_AnswerDirectly(t *testing.T) { + t.Parallel() cfg := rlm.DefaultEngineConfig() cfg.Strategy.DirectThreshold = 10000 // larger than our test context engine := rlm.NewEngine(cfg, nil, echoModel) - answer, tokens, err := engine.Answer(context.Background(), "sess", "what?", "short context") + answer, tokens, err := engine.Answer(t.Context(), "sess", "what?", "short context") require.NoError(t, err) assert.NotEmpty(t, answer) assert.Greater(t, tokens, uint32(0)) } func TestEngine_LargeContext_Partitions(t *testing.T) { + t.Parallel( // Force partitioning by setting DirectThreshold very low. + ) + cfg := rlm.DefaultEngineConfig() cfg.Strategy.DirectThreshold = 10 cfg.Strategy.DefaultPartitionK = 2 @@ -48,13 +52,14 @@ func TestEngine_LargeContext_Partitions(t *testing.T) { largeCtx := strings.Repeat("hello world ", 100) // ~1200 bytes engine := rlm.NewEngine(cfg, nil, echoModel) - answer, tokens, err := engine.Answer(context.Background(), "sess", "summarise", largeCtx) + answer, tokens, err := engine.Answer(t.Context(), "sess", "summarise", largeCtx) require.NoError(t, err) assert.NotEmpty(t, answer) assert.Greater(t, tokens, uint32(0)) } func TestEngine_MaxDepthTerminates(t *testing.T) { + t.Parallel() cfg := rlm.DefaultEngineConfig() cfg.Strategy.DirectThreshold = 0 // always partition cfg.Strategy.MaxDepth = 3 @@ -64,20 +69,22 @@ func TestEngine_MaxDepthTerminates(t *testing.T) { engine := rlm.NewEngine(cfg, nil, echoModel) // Should terminate without stack overflow. - _, _, err := engine.Answer(context.Background(), "sess", "any", content) + _, _, err := engine.Answer(t.Context(), "sess", "any", content) assert.NoError(t, err) } func TestEngine_ModelError_Propagates(t *testing.T) { + t.Parallel() cfg := rlm.DefaultEngineConfig() cfg.Strategy.DirectThreshold = 10000 engine := rlm.NewEngine(cfg, nil, errorModel) - _, _, err := engine.Answer(context.Background(), "sess", "q", "ctx") + _, _, err := engine.Answer(t.Context(), "sess", "q", "ctx") assert.Error(t, err) } func TestEngine_GrepQuery_NarrowsContext(t *testing.T) { + t.Parallel() cfg := rlm.DefaultEngineConfig() cfg.Strategy.DirectThreshold = 10 cfg.Strategy.DefaultPartitionK = 2 @@ -88,14 +95,15 @@ func TestEngine_GrepQuery_NarrowsContext(t *testing.T) { content := "foo\nfunc myFunction() {}\nbar\nbaz" engine := rlm.NewEngine(cfg, nil, echoModel) - answer, _, err := engine.Answer(context.Background(), "sess", "find func definition", content) + answer, _, err := engine.Answer(t.Context(), "sess", "find func definition", content) require.NoError(t, err) assert.NotEmpty(t, answer) } func TestFanOut_AllPartitions_Processed(t *testing.T) { + t.Parallel() partitions := []string{"A", "B", "C", "D"} - results := rlm.FanOut(context.Background(), partitions, 2, + results := rlm.FanOut(t.Context(), partitions, 2, func(_ context.Context, idx int, key, part string) rlm.PartitionResult { return rlm.PartitionResult{PartitionIdx: idx, ContextKey: key, Answer: "ans-" + part, Tokens: 1} }) @@ -108,11 +116,12 @@ func TestFanOut_AllPartitions_Processed(t *testing.T) { } func TestFanOut_UnboundedConcurrency(t *testing.T) { + t.Parallel() parts := make([]string, 20) for i := range parts { parts[i] = fmt.Sprintf("part-%d", i) } - results := rlm.FanOut(context.Background(), parts, 0, + results := rlm.FanOut(t.Context(), parts, 0, func(_ context.Context, idx int, key, part string) rlm.PartitionResult { return rlm.PartitionResult{PartitionIdx: idx, Answer: part, Tokens: 2} }) @@ -121,7 +130,8 @@ func TestFanOut_UnboundedConcurrency(t *testing.T) { } func TestFanOut_Empty(t *testing.T) { - results := rlm.FanOut(context.Background(), nil, 4, + t.Parallel() + results := rlm.FanOut(t.Context(), nil, 4, func(_ context.Context, _ int, _, _ string) rlm.PartitionResult { return rlm.PartitionResult{} }) @@ -129,6 +139,7 @@ func TestFanOut_Empty(t *testing.T) { } func TestMergeResults_Deduplication(t *testing.T) { + t.Parallel() results := []rlm.PartitionResult{ {Answer: "alpha"}, {Answer: "beta"}, @@ -140,6 +151,7 @@ func TestMergeResults_Deduplication(t *testing.T) { } func TestMergeResults_WithErrors(t *testing.T) { + t.Parallel() results := []rlm.PartitionResult{ {Answer: "good"}, {Err: fmt.Errorf("failed"), Answer: "should be skipped"}, @@ -149,6 +161,7 @@ func TestMergeResults_WithErrors(t *testing.T) { } func TestStrategyPlanner_SmallContext_OpFinal(t *testing.T) { + t.Parallel() cfg := rlm.DefaultStrategyConfig() cfg.DirectThreshold = 1000 planner := rlm.NewStrategyPlanner(cfg) @@ -158,6 +171,7 @@ func TestStrategyPlanner_SmallContext_OpFinal(t *testing.T) { } func TestStrategyPlanner_MaxDepth_OpFinal(t *testing.T) { + t.Parallel() cfg := rlm.DefaultStrategyConfig() cfg.MaxDepth = 3 planner := rlm.NewStrategyPlanner(cfg) @@ -167,6 +181,7 @@ func TestStrategyPlanner_MaxDepth_OpFinal(t *testing.T) { } func TestStrategyPlanner_KeywordQuery_OpGrep(t *testing.T) { + t.Parallel() planner := rlm.NewStrategyPlanner(rlm.DefaultStrategyConfig()) // "find" prefix should trigger grep. @@ -176,6 +191,7 @@ func TestStrategyPlanner_KeywordQuery_OpGrep(t *testing.T) { } func TestStrategyPlanner_LargeContext_OpPartition(t *testing.T) { + t.Parallel() planner := rlm.NewStrategyPlanner(rlm.DefaultStrategyConfig()) op := planner.PlanNext(100000, "summarise everything", 0) diff --git a/pkg/rlm/fanout_test.go b/pkg/rlm/fanout_test.go index f42d8d472..64aec1921 100644 --- a/pkg/rlm/fanout_test.go +++ b/pkg/rlm/fanout_test.go @@ -12,14 +12,16 @@ import ( ) func TestFanOutEmpty(t *testing.T) { - results := FanOut(context.Background(), nil, 0, nil) + t.Parallel() + results := FanOut(t.Context(), nil, 0, nil) assert.Nil(t, results) } func TestFanOutUnbounded(t *testing.T) { + t.Parallel() partitions := []string{"part-0", "part-1", "part-2"} - results := FanOut(context.Background(), partitions, 0, func(ctx context.Context, idx int, key, partition string) PartitionResult { + results := FanOut(t.Context(), partitions, 0, func(ctx context.Context, idx int, key, partition string) PartitionResult { return PartitionResult{ PartitionIdx: idx, ContextKey: key, @@ -37,6 +39,7 @@ func TestFanOutUnbounded(t *testing.T) { } func TestFanOutBounded(t *testing.T) { + t.Parallel() partitions := make([]string, 10) for i := range partitions { partitions[i] = fmt.Sprintf("chunk-%d", i) @@ -45,7 +48,7 @@ func TestFanOutBounded(t *testing.T) { var maxConcurrent int64 var current int64 - results := FanOut(context.Background(), partitions, 3, func(ctx context.Context, idx int, key, partition string) PartitionResult { + results := FanOut(t.Context(), partitions, 3, func(ctx context.Context, idx int, key, partition string) PartitionResult { c := atomic.AddInt64(¤t, 1) for { old := atomic.LoadInt64(&maxConcurrent) @@ -71,9 +74,10 @@ func TestFanOutBounded(t *testing.T) { } func TestFanOutPreservesOrder(t *testing.T) { + t.Parallel() partitions := []string{"A", "B", "C", "D"} - results := FanOut(context.Background(), partitions, 2, func(ctx context.Context, idx int, key, partition string) PartitionResult { + results := FanOut(t.Context(), partitions, 2, func(ctx context.Context, idx int, key, partition string) PartitionResult { return PartitionResult{ PartitionIdx: idx, Answer: partition, @@ -88,6 +92,7 @@ func TestFanOutPreservesOrder(t *testing.T) { } func TestMergeResultsDeduplication(t *testing.T) { + t.Parallel() results := []PartitionResult{ {Answer: " answer one "}, {Answer: "answer one"}, @@ -101,6 +106,7 @@ func TestMergeResultsDeduplication(t *testing.T) { } func TestMergeResultsAllErrors(t *testing.T) { + t.Parallel() results := []PartitionResult{ {Err: fmt.Errorf("e1")}, {Err: fmt.Errorf("e2")}, @@ -109,6 +115,7 @@ func TestMergeResultsAllErrors(t *testing.T) { } func TestMergeResultsAllEmpty(t *testing.T) { + t.Parallel() results := []PartitionResult{ {Answer: ""}, {Answer: " "}, @@ -117,6 +124,7 @@ func TestMergeResultsAllEmpty(t *testing.T) { } func TestTotalTokens(t *testing.T) { + t.Parallel() results := []PartitionResult{ {Tokens: 100}, {Tokens: 250}, @@ -126,5 +134,6 @@ func TestTotalTokens(t *testing.T) { } func TestTotalTokensEmpty(t *testing.T) { + t.Parallel() assert.Equal(t, uint32(0), TotalTokens(nil)) } diff --git a/pkg/rlm/rope_test.go b/pkg/rlm/rope_test.go index 7f92de5ee..51557d3cd 100644 --- a/pkg/rlm/rope_test.go +++ b/pkg/rlm/rope_test.go @@ -10,12 +10,14 @@ import ( ) func TestRope_EmptyRope(t *testing.T) { + t.Parallel() r := rlm.NewRope("") assert.Equal(t, 0, r.Len()) assert.Equal(t, "", r.String()) } func TestRope_BasicAppendAndString(t *testing.T) { + t.Parallel() r := rlm.NewRope("hello") r.Append(" world") assert.Equal(t, 11, r.Len()) @@ -23,6 +25,7 @@ func TestRope_BasicAppendAndString(t *testing.T) { } func TestRope_LargeContent(t *testing.T) { + t.Parallel() content := strings.Repeat("abcdefghij", 1000) // 10000 bytes r := rlm.NewRope(content) assert.Equal(t, 10000, r.Len()) @@ -30,6 +33,7 @@ func TestRope_LargeContent(t *testing.T) { } func TestRope_Slice_ValidRange(t *testing.T) { + t.Parallel() r := rlm.NewRope("hello world") s, err := r.Slice(6, 11) require.NoError(t, err) @@ -37,6 +41,7 @@ func TestRope_Slice_ValidRange(t *testing.T) { } func TestRope_Slice_ZeroLength(t *testing.T) { + t.Parallel() r := rlm.NewRope("hello") s, err := r.Slice(2, 2) require.NoError(t, err) @@ -44,12 +49,14 @@ func TestRope_Slice_ZeroLength(t *testing.T) { } func TestRope_Slice_OutOfRange(t *testing.T) { + t.Parallel() r := rlm.NewRope("hello") _, err := r.Slice(3, 10) assert.Error(t, err) } func TestRope_Slice_AcrossAppendBoundary(t *testing.T) { + t.Parallel() r := rlm.NewRope("hello") r.Append(" world") s, err := r.Slice(3, 8) @@ -58,12 +65,14 @@ func TestRope_Slice_AcrossAppendBoundary(t *testing.T) { } func TestRope_Lines(t *testing.T) { + t.Parallel() r := rlm.NewRope("line1\nline2\nline3") lines := r.Lines() assert.Equal(t, []string{"line1", "line2", "line3"}, lines) } func TestRope_GrepLines_CaseSensitive(t *testing.T) { + t.Parallel() r := rlm.NewRope("apple\nBanana\napricot\ncherry") matches := r.GrepLines("ap", 0, false) require.Len(t, matches, 2) @@ -72,6 +81,7 @@ func TestRope_GrepLines_CaseSensitive(t *testing.T) { } func TestRope_GrepLines_CaseInsensitive(t *testing.T) { + t.Parallel() r := rlm.NewRope("Apple\nbanana\nAPRICOT") matches := r.GrepLines("apple", 0, true) require.Len(t, matches, 1) @@ -79,18 +89,21 @@ func TestRope_GrepLines_CaseInsensitive(t *testing.T) { } func TestRope_GrepLines_MaxMatches(t *testing.T) { + t.Parallel() r := rlm.NewRope("aa\naa\naa\naa\naa") matches := r.GrepLines("aa", 3, false) assert.Len(t, matches, 3) } func TestRope_GrepLines_NoMatches(t *testing.T) { + t.Parallel() r := rlm.NewRope("hello world") matches := r.GrepLines("xyz", 0, false) assert.Empty(t, matches) } func TestRope_Partition_Even(t *testing.T) { + t.Parallel() r := rlm.NewRope("12345678") parts := r.Partition(4) assert.Len(t, parts, 4) @@ -98,6 +111,7 @@ func TestRope_Partition_Even(t *testing.T) { } func TestRope_Partition_MoreThanContent(t *testing.T) { + t.Parallel() r := rlm.NewRope("hi") parts := r.Partition(10) assert.Len(t, parts, 10) @@ -107,6 +121,7 @@ func TestRope_Partition_MoreThanContent(t *testing.T) { } func TestRope_Partition_Empty(t *testing.T) { + t.Parallel() r := rlm.NewRope("") parts := r.Partition(4) assert.Len(t, parts, 4) @@ -116,7 +131,10 @@ func TestRope_Partition_Empty(t *testing.T) { } func TestRope_RuneLen(t *testing.T) { + t.Parallel( // Multi-byte Unicode characters. + ) + r := rlm.NewRope("héllo") // 'é' is 2 bytes assert.Equal(t, 5, r.RuneLen()) assert.Equal(t, 6, r.Len()) // bytes diff --git a/pkg/rlm/strategy_test.go b/pkg/rlm/strategy_test.go index 3453233b3..4a6b31b29 100644 --- a/pkg/rlm/strategy_test.go +++ b/pkg/rlm/strategy_test.go @@ -7,6 +7,7 @@ import ( ) func TestStrategyPlanNextFinalAtMaxDepth(t *testing.T) { + t.Parallel() sp := NewStrategyPlanner(StrategyConfig{ DirectThreshold: 8192, DefaultPartitionK: 4, @@ -18,6 +19,7 @@ func TestStrategyPlanNextFinalAtMaxDepth(t *testing.T) { } func TestStrategyPlanNextFinalSmallContext(t *testing.T) { + t.Parallel() sp := NewStrategyPlanner(DefaultStrategyConfig()) op := sp.PlanNext(1000, "any query", 0) @@ -25,6 +27,7 @@ func TestStrategyPlanNextFinalSmallContext(t *testing.T) { } func TestStrategyPlanNextGrepForKeywordQuery(t *testing.T) { + t.Parallel() sp := NewStrategyPlanner(DefaultStrategyConfig()) tests := []struct { @@ -47,6 +50,7 @@ func TestStrategyPlanNextGrepForKeywordQuery(t *testing.T) { } func TestStrategyPlanNextPartitionDefault(t *testing.T) { + t.Parallel() sp := NewStrategyPlanner(DefaultStrategyConfig()) op := sp.PlanNext(100_000, "summarize this document", 0) assert.Equal(t, OpPartition, op.Type) @@ -54,6 +58,7 @@ func TestStrategyPlanNextPartitionDefault(t *testing.T) { } func TestStrategyPlanNextPartitionLargeContext(t *testing.T) { + t.Parallel() sp := NewStrategyPlanner(DefaultStrategyConfig()) op := sp.PlanNext(5_000_000, "summarize this corpus", 0) assert.Equal(t, OpPartition, op.Type) @@ -61,18 +66,22 @@ func TestStrategyPlanNextPartitionLargeContext(t *testing.T) { } func TestExtractKeywordQuoted(t *testing.T) { + t.Parallel() assert.Equal(t, "handleRequest", extractKeyword(`find "handleRequest" in the codebase`)) } func TestExtractKeywordNoQuotes(t *testing.T) { + t.Parallel() assert.Equal(t, "find", extractKeyword("find the main function")) } func TestExtractKeywordEmpty(t *testing.T) { + t.Parallel() assert.Equal(t, "", extractKeyword("")) } func TestLooksLikeKeywordQuery(t *testing.T) { + t.Parallel() assert.True(t, looksLikeKeywordQuery(`find "something"`)) assert.True(t, looksLikeKeywordQuery("error: something broke")) assert.True(t, looksLikeKeywordQuery("func processData")) diff --git a/pkg/runtime/runtime_test.go b/pkg/runtime/runtime_test.go index f7af7c813..ea73c792a 100644 --- a/pkg/runtime/runtime_test.go +++ b/pkg/runtime/runtime_test.go @@ -44,6 +44,7 @@ func TestResolveBaseConfigPath_PrefersXDGOverLegacy(t *testing.T) { } func TestResolveBaseConfigPath_FallsBackToLegacyWhenXDGMissing(t *testing.T) { + t.Parallel() home := t.TempDir() xdg := t.TempDir() t.Setenv("HOME", home) @@ -58,6 +59,7 @@ func TestResolveBaseConfigPath_FallsBackToLegacyWhenXDGMissing(t *testing.T) { } func TestLoadResolvedConfig_AppliesOverlayAndKeepsBaseValues(t *testing.T) { + t.Parallel() dir := t.TempDir() basePath := filepath.Join(dir, "base.json") overlayPath := filepath.Join(dir, "overlay.json") @@ -82,6 +84,7 @@ func TestLoadResolvedConfig_AppliesOverlayAndKeepsBaseValues(t *testing.T) { } func TestEnsureMinProviderTimeout_SetsFloor(t *testing.T) { + t.Parallel() dir := t.TempDir() basePath := filepath.Join(dir, "base.json") require.NoError(t, os.WriteFile(basePath, []byte(`{"providers":{"openai":{"timeout":0}}}`), 0o644)) @@ -95,10 +98,11 @@ func TestEnsureMinProviderTimeout_SetsFloor(t *testing.T) { } func TestStartOutbound_DropAndConsumeDoNotBlockPublishers(t *testing.T) { + t.Parallel() modes := []OutboundMode{OutboundModeDrop, OutboundModeConsume} for _, mode := range modes { t.Run(string(mode), func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() msgBus := bus.NewMessageBus() @@ -127,7 +131,8 @@ func TestStartOutbound_DropAndConsumeDoNotBlockPublishers(t *testing.T) { } func TestStartOutbound_CallbackReceivesMessages(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) + t.Parallel() + ctx, cancel := context.WithCancel(t.Context()) defer cancel() msgBus := bus.NewMessageBus() @@ -158,6 +163,7 @@ func TestStartOutbound_CallbackReceivesMessages(t *testing.T) { } func TestValidateKernelInvariants(t *testing.T) { + t.Parallel() t.Run("nil loop", func(t *testing.T) { err := validateKernelInvariants(nil) require.Error(t, err) diff --git a/pkg/security/jsonextract_test.go b/pkg/security/jsonextract_test.go index b4076f525..352e6c635 100644 --- a/pkg/security/jsonextract_test.go +++ b/pkg/security/jsonextract_test.go @@ -9,6 +9,7 @@ import ( ) func TestExtractJSON_RawJSON(t *testing.T) { + t.Parallel() tests := []struct { name string input string @@ -30,6 +31,7 @@ func TestExtractJSON_RawJSON(t *testing.T) { } func TestExtractJSON_CodeFence(t *testing.T) { + t.Parallel() tests := []struct { name string input string @@ -58,6 +60,7 @@ func TestExtractJSON_CodeFence(t *testing.T) { } func TestExtractJSON_EmbeddedInProse(t *testing.T) { + t.Parallel() input := `Based on my analysis, the result is {"importance": 0.9, "sector": "semantic"} which indicates high relevance.` var result struct { Importance float64 `json:"importance"` @@ -70,6 +73,7 @@ func TestExtractJSON_EmbeddedInProse(t *testing.T) { } func TestExtractJSON_NestedBracesInStrings(t *testing.T) { + t.Parallel() input := `{"content": "function() { return {}; }", "count": 1}` var result map[string]interface{} err := ExtractJSON(input, &result, nil) @@ -79,6 +83,7 @@ func TestExtractJSON_NestedBracesInStrings(t *testing.T) { } func TestExtractJSON_InjectionAttempts(t *testing.T) { + t.Parallel() tests := []struct { name string input string @@ -131,12 +136,14 @@ func TestExtractJSON_InjectionAttempts(t *testing.T) { } func TestExtractJSON_EmptyInput(t *testing.T) { + t.Parallel() var result map[string]interface{} err := ExtractJSON("", &result, nil) assert.ErrorIs(t, err, ErrNoJSON) } func TestExtractJSON_MultipleFences_TakesFirst(t *testing.T) { + t.Parallel() input := "```json\n{\"first\": true}\n```\nmore text\n```json\n{\"second\": true}\n```" var result map[string]interface{} err := ExtractJSON(input, &result, nil) @@ -147,6 +154,7 @@ func TestExtractJSON_MultipleFences_TakesFirst(t *testing.T) { } func TestSanitizeToolArgs_ValidInput(t *testing.T) { + t.Parallel() schema := map[string]ArgSpec{ "path": {Type: ArgString, Required: true, MaxLength: 256}, "content": {Type: ArgString, Required: true}, @@ -166,6 +174,7 @@ func TestSanitizeToolArgs_ValidInput(t *testing.T) { } func TestSanitizeToolArgs_MissingRequired(t *testing.T) { + t.Parallel() schema := map[string]ArgSpec{ "path": {Type: ArgString, Required: true}, } @@ -176,6 +185,7 @@ func TestSanitizeToolArgs_MissingRequired(t *testing.T) { } func TestSanitizeToolArgs_ExceedsMaxLength(t *testing.T) { + t.Parallel() schema := map[string]ArgSpec{ "cmd": {Type: ArgString, Required: true, MaxLength: 10}, } @@ -186,6 +196,7 @@ func TestSanitizeToolArgs_ExceedsMaxLength(t *testing.T) { } func TestSanitizeToolArgs_TypeCoercion(t *testing.T) { + t.Parallel() tests := []struct { name string argType ArgType @@ -220,12 +231,14 @@ func TestSanitizeToolArgs_TypeCoercion(t *testing.T) { } func TestExtractFirstBraced_EscapedQuotes(t *testing.T) { + t.Parallel() input := `{"msg": "say \"hello\" world"}` result := extractFirstBraced(input) assert.Equal(t, input, result) } func TestExtractFirstBraced_UnbalancedBraces(t *testing.T) { + t.Parallel() input := `text { not closed` result := extractFirstBraced(input) assert.Empty(t, result) diff --git a/pkg/security/redact_test.go b/pkg/security/redact_test.go index 0738ad716..be00fb01e 100644 --- a/pkg/security/redact_test.go +++ b/pkg/security/redact_test.go @@ -7,6 +7,7 @@ import ( ) func TestRedactor_APIKeys(t *testing.T) { + t.Parallel() r := NewRedactor() tests := []struct { name string @@ -30,6 +31,7 @@ func TestRedactor_APIKeys(t *testing.T) { } func TestRedactor_Bearer(t *testing.T) { + t.Parallel() r := NewRedactor() input := "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.xxxxx" out := r.Redact(input) @@ -37,6 +39,7 @@ func TestRedactor_Bearer(t *testing.T) { } func TestRedactor_JWT(t *testing.T) { + t.Parallel() r := NewRedactor() jwt := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U" out := r.Redact(jwt) @@ -44,6 +47,7 @@ func TestRedactor_JWT(t *testing.T) { } func TestRedactor_PII(t *testing.T) { + t.Parallel() r := NewRedactor() tests := []struct { name string @@ -64,6 +68,7 @@ func TestRedactor_PII(t *testing.T) { } func TestRedactor_SecretValues(t *testing.T) { + t.Parallel() r := NewRedactor() tests := []struct { name string @@ -84,6 +89,7 @@ func TestRedactor_SecretValues(t *testing.T) { } func TestRedactor_SafeText(t *testing.T) { + t.Parallel() r := NewRedactor() safe := "This is a normal log message about processing 42 items." assert.Equal(t, safe, r.Redact(safe)) @@ -91,6 +97,7 @@ func TestRedactor_SafeText(t *testing.T) { } func TestRedactor_RedactMap(t *testing.T) { + t.Parallel() r := NewRedactor() m := map[string]interface{}{ "command": "curl -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.xxxxxxxxxxxxxxxxxxxx.yyyyyyyy'", @@ -109,6 +116,7 @@ func TestRedactor_RedactMap(t *testing.T) { } func TestMaskKey(t *testing.T) { + t.Parallel() tests := []struct { input string want string diff --git a/pkg/security/securebus/audit_test.go b/pkg/security/securebus/audit_test.go index 8d1dab270..9e5668016 100644 --- a/pkg/security/securebus/audit_test.go +++ b/pkg/security/securebus/audit_test.go @@ -11,6 +11,7 @@ import ( ) func TestAuditLogAppendAndRetrieve(t *testing.T) { + t.Parallel() al := NewAuditLog() event := AuditEvent{ @@ -32,6 +33,7 @@ func TestAuditLogAppendAndRetrieve(t *testing.T) { } func TestAuditLogConcurrentAppend(t *testing.T) { + t.Parallel() al := NewAuditLog() n := 100 @@ -52,6 +54,7 @@ func TestAuditLogConcurrentAppend(t *testing.T) { } func TestAuditLogFilterBySession(t *testing.T) { + t.Parallel() al := NewAuditLog() _ = al.Append(AuditEvent{RequestID: "r1", SessionKey: "sess-A"}) @@ -69,6 +72,7 @@ func TestAuditLogFilterBySession(t *testing.T) { } func TestAuditLogLeakEvents(t *testing.T) { + t.Parallel() al := NewAuditLog() _ = al.Append(AuditEvent{RequestID: "r1", LeakDetected: false}) @@ -98,6 +102,7 @@ func (ms *mockSink) Write(event AuditEvent) error { } func TestAuditLogSinkIntegration(t *testing.T) { + t.Parallel() sink := &mockSink{} al := NewAuditLog(sink) @@ -109,6 +114,7 @@ func TestAuditLogSinkIntegration(t *testing.T) { } func TestAuditLogSinkError(t *testing.T) { + t.Parallel() sink := &mockSink{failAt: 1} al := NewAuditLog(sink) @@ -121,6 +127,7 @@ func TestAuditLogSinkError(t *testing.T) { } func TestAuditLogEventsImmutable(t *testing.T) { + t.Parallel() al := NewAuditLog() _ = al.Append(AuditEvent{RequestID: "r1"}) diff --git a/pkg/security/securebus/bus_test.go b/pkg/security/securebus/bus_test.go index 0f51e3232..cff31e2b6 100644 --- a/pkg/security/securebus/bus_test.go +++ b/pkg/security/securebus/bus_test.go @@ -2,9 +2,10 @@ package securebus_test import ( "context" - jsonv2 "github.com/go-json-experiment/json" "testing" + jsonv2 "github.com/go-json-experiment/json" + "github.com/ZanzyTHEbar/dragonscale/pkg/itr" "github.com/ZanzyTHEbar/dragonscale/pkg/security" "github.com/ZanzyTHEbar/dragonscale/pkg/security/securebus" @@ -76,12 +77,13 @@ func makeBus(t *testing.T, toolMap map[string]tools.Tool, secrets *security.Secr // ── tests ───────────────────────────────────────────────────────────────────── func TestBus_SuccessfulToolExec(t *testing.T) { + t.Parallel() tool := &staticTool{name: "greet", result: "hello world"} bus := makeBus(t, map[string]tools.Tool{"greet": tool}, nil) defer bus.Close() req := itr.NewToolExecRequest("req-1", "sess", "tc-1", "greet", makeArgsJSON(nil)) - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.False(t, resp.IsError) assert.Equal(t, "hello world", resp.Result) @@ -89,22 +91,24 @@ func TestBus_SuccessfulToolExec(t *testing.T) { } func TestBus_UnknownTool(t *testing.T) { + t.Parallel() bus := makeBus(t, map[string]tools.Tool{}, nil) defer bus.Close() req := itr.NewToolExecRequest("req-2", "sess", "tc-2", "nonexistent", makeArgsJSON(nil)) - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.True(t, resp.IsError) } func TestBus_ToolReturnsError(t *testing.T) { + t.Parallel() tool := &staticTool{name: "fail", result: "something broke", isErr: true} bus := makeBus(t, map[string]tools.Tool{"fail": tool}, nil) defer bus.Close() req := itr.NewToolExecRequest("req-3", "sess", "tc-3", "fail", makeArgsJSON(nil)) - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.True(t, resp.IsError) assert.Equal(t, 1, bus.AuditLog().Len()) @@ -113,14 +117,17 @@ func TestBus_ToolReturnsError(t *testing.T) { } func TestBus_LeakDetection(t *testing.T) { + t.Parallel( // Tool output contains an API key — should be redacted. + ) + apiKey := "AKIAIOSFODNN7EXAMPLE" // fake AWS key matching redactor pattern tool := &staticTool{name: "leaky", result: "result: " + apiKey} bus := makeBus(t, map[string]tools.Tool{"leaky": tool}, nil) defer bus.Close() req := itr.NewToolExecRequest("req-4", "sess", "tc-4", "leaky", makeArgsJSON(nil)) - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.True(t, resp.LeakDetected, "should detect API key in output") assert.NotContains(t, resp.Result, apiKey, "raw API key must not appear in response") @@ -130,7 +137,10 @@ func TestBus_LeakDetection(t *testing.T) { } func TestBus_SecretInjection_ArgVariant(t *testing.T) { + t.Parallel( // Tool reads injected "token" arg from args map. + ) + echoT := &echoTool{} // Give echo tool a capability that declares a secret injected as arg:input. @@ -174,7 +184,7 @@ func TestBus_SecretInjection_ArgVariant(t *testing.T) { defer bus.Close() req := itr.NewToolExecRequest("req-5", "sess", "tc-5", "echo", makeArgsJSON(nil)) - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.False(t, resp.IsError) assert.Equal(t, "supersecret", resp.Result, "injected secret should appear in tool output") @@ -185,6 +195,7 @@ func TestBus_SecretInjection_ArgVariant(t *testing.T) { } func TestBus_PolicyViolation_RecursionDepth(t *testing.T) { + t.Parallel() tool := &staticTool{name: "ok", result: "fine"} bus := makeBus(t, map[string]tools.Tool{"ok": tool}, nil) defer bus.Close() @@ -192,7 +203,7 @@ func TestBus_PolicyViolation_RecursionDepth(t *testing.T) { req := itr.NewToolExecRequest("req-6", "sess", "tc-6", "ok", makeArgsJSON(nil)) req.Depth = 255 // far exceeds MaxRecursionDepth=10 - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.True(t, resp.IsError, "depth violation should produce an error response") events := bus.AuditLog().Events() @@ -201,13 +212,14 @@ func TestBus_PolicyViolation_RecursionDepth(t *testing.T) { } func TestBus_AuditLog_FilterBySession(t *testing.T) { + t.Parallel() tool := &staticTool{name: "t", result: "ok"} bus := makeBus(t, map[string]tools.Tool{"t": tool}, nil) defer bus.Close() for _, sk := range []string{"session-A", "session-A", "session-B"} { req := itr.NewToolExecRequest("req-audit-"+sk, sk, "tc", "t", makeArgsJSON(nil)) - bus.Execute(context.Background(), req) + bus.Execute(t.Context(), req) } assert.Equal(t, 3, bus.AuditLog().Len()) @@ -216,40 +228,44 @@ func TestBus_AuditLog_FilterBySession(t *testing.T) { } func TestBus_Transport_Send(t *testing.T) { + t.Parallel() tool := &staticTool{name: "ping", result: "pong"} bus := makeBus(t, map[string]tools.Tool{"ping": tool}, nil) defer bus.Close() req := itr.NewToolExecRequest("req-tr", "sess", "tc", "ping", makeArgsJSON(nil)) - resp, err := bus.Transport().Send(context.Background(), req) + resp, err := bus.Transport().Send(t.Context(), req) require.NoError(t, err) assert.Equal(t, "pong", resp.Result) } func TestBus_InvalidArgsJSON(t *testing.T) { + t.Parallel() tool := &staticTool{name: "ok", result: "ok"} bus := makeBus(t, map[string]tools.Tool{"ok": tool}, nil) defer bus.Close() req := itr.NewToolExecRequest("req-bad", "sess", "tc", "ok", "{invalid json") - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.True(t, resp.IsError) } func TestBus_RLMFinalCommand(t *testing.T) { + t.Parallel() bus := makeBus(t, nil, nil) defer bus.Close() req := itr.NewFinalRequest("req-final", "sess", 0, "the answer", "") - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.False(t, resp.IsError) assert.Equal(t, "the answer", resp.Result) } func TestBus_CloseIdempotent(t *testing.T) { + t.Parallel() bus := makeBus(t, nil, nil) assert.NotPanics(t, func() { @@ -260,6 +276,7 @@ func TestBus_CloseIdempotent(t *testing.T) { } func TestBus_ToolSearch(t *testing.T) { + t.Parallel() bus := makeBus(t, nil, nil) defer bus.Close() @@ -268,24 +285,26 @@ func TestBus_ToolSearch(t *testing.T) { }) req := itr.NewToolSearchRequest("req-search", "sess", "file operations", 5) - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.False(t, resp.IsError) assert.Contains(t, resp.Result, "read_file") } func TestBus_ToolSearchNotConfigured(t *testing.T) { + t.Parallel() bus := makeBus(t, nil, nil) defer bus.Close() req := itr.NewToolSearchRequest("req-search2", "sess", "anything", 5) - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.True(t, resp.IsError) assert.Contains(t, resp.Result, "not configured") } func TestBus_NilToolResult(t *testing.T) { + t.Parallel() executor := func(ctx context.Context, name string, args map[string]interface{}) *tools.ToolResult { return nil } @@ -296,7 +315,7 @@ func TestBus_NilToolResult(t *testing.T) { defer bus.Close() req := itr.NewToolExecRequest("req-nil", "sess", "tc", "something", "{}") - resp := bus.Execute(context.Background(), req) + resp := bus.Execute(t.Context(), req) assert.False(t, resp.IsError) assert.Empty(t, resp.Result) diff --git a/pkg/security/securebus/policy_test.go b/pkg/security/securebus/policy_test.go index fc38c3a9d..745995e72 100644 --- a/pkg/security/securebus/policy_test.go +++ b/pkg/security/securebus/policy_test.go @@ -10,6 +10,7 @@ import ( ) func TestPolicyValidateRecursionDepth(t *testing.T) { + t.Parallel() pe := NewPolicyEngine(PolicyConfig{MaxRecursionDepth: 5}) req := itr.ToolRequest{Depth: 3} @@ -22,6 +23,7 @@ func TestPolicyValidateRecursionDepth(t *testing.T) { } func TestPolicyValidateNoDepthLimit(t *testing.T) { + t.Parallel() pe := NewPolicyEngine(PolicyConfig{MaxRecursionDepth: 0}) req := itr.ToolRequest{Depth: 255} @@ -29,6 +31,7 @@ func TestPolicyValidateNoDepthLimit(t *testing.T) { } func TestPolicyValidateNetworkSSRFBlocked(t *testing.T) { + t.Parallel() pe := NewPolicyEngine(DefaultPolicyConfig()) ssrfURLs := []string{ @@ -53,6 +56,7 @@ func TestPolicyValidateNetworkSSRFBlocked(t *testing.T) { } func TestPolicyValidateNetworkAllowed(t *testing.T) { + t.Parallel() pe := NewPolicyEngine(DefaultPolicyConfig()) rules := []tools.EndpointRule{{Pattern: "https://api.github.com/*"}} @@ -60,6 +64,7 @@ func TestPolicyValidateNetworkAllowed(t *testing.T) { } func TestPolicyValidateNetworkNoRules(t *testing.T) { + t.Parallel() pe := NewPolicyEngine(DefaultPolicyConfig()) err := pe.ValidateNetwork("https://example.com", nil) require.Error(t, err) @@ -67,6 +72,7 @@ func TestPolicyValidateNetworkNoRules(t *testing.T) { } func TestPolicyValidateNetworkNoMatchingRule(t *testing.T) { + t.Parallel() pe := NewPolicyEngine(DefaultPolicyConfig()) rules := []tools.EndpointRule{{Pattern: "https://api.github.com/*"}} @@ -76,6 +82,7 @@ func TestPolicyValidateNetworkNoMatchingRule(t *testing.T) { } func TestPolicyValidateFilesystemAllowed(t *testing.T) { + t.Parallel() pe := NewPolicyEngine(PolicyConfig{AllowedWorkspace: "/workspace"}) rules := []tools.PathRule{{Pattern: "src/*", Mode: "rw"}} @@ -85,6 +92,7 @@ func TestPolicyValidateFilesystemAllowed(t *testing.T) { } func TestPolicyValidateFilesystemNoRules(t *testing.T) { + t.Parallel() pe := NewPolicyEngine(PolicyConfig{}) err := pe.ValidateFilesystem("/etc/passwd", "r", nil) require.Error(t, err) @@ -92,6 +100,7 @@ func TestPolicyValidateFilesystemNoRules(t *testing.T) { } func TestPolicyValidateFilesystemModeMismatch(t *testing.T) { + t.Parallel() pe := NewPolicyEngine(PolicyConfig{AllowedWorkspace: "/workspace"}) rules := []tools.PathRule{{Pattern: "data/*", Mode: "r"}} diff --git a/pkg/security/securebus/socket_transport_test.go b/pkg/security/securebus/socket_transport_test.go index cfc18d1da..ca654ca14 100644 --- a/pkg/security/securebus/socket_transport_test.go +++ b/pkg/security/securebus/socket_transport_test.go @@ -14,6 +14,7 @@ import ( ) func TestSocketTransportRoundTrip(t *testing.T) { + t.Parallel() sockPath := filepath.Join(t.TempDir(), "test.sock") server, err := NewSocketTransportServer(sockPath) @@ -47,7 +48,7 @@ func TestSocketTransportRoundTrip(t *testing.T) { Timestamp: time.Now().UnixNano(), } - resp, err := client.Send(context.Background(), req) + resp, err := client.Send(t.Context(), req) require.NoError(t, err) assert.Equal(t, "req-001", resp.ID) assert.Contains(t, resp.Result, "req-001") @@ -57,6 +58,7 @@ func TestSocketTransportRoundTrip(t *testing.T) { } func TestSocketTransportMultipleRequests(t *testing.T) { + t.Parallel() sockPath := filepath.Join(t.TempDir(), "multi.sock") server, err := NewSocketTransportServer(sockPath) @@ -82,13 +84,14 @@ func TestSocketTransportMultipleRequests(t *testing.T) { "echo", `{}`, ) - resp, err := client.Send(context.Background(), req) + resp, err := client.Send(t.Context(), req) require.NoError(t, err) assert.Equal(t, req.ID, resp.ID) } } func TestSocketTransportCleanup(t *testing.T) { + t.Parallel() sockPath := filepath.Join(t.TempDir(), "cleanup.sock") server, err := NewSocketTransportServer(sockPath) @@ -104,6 +107,7 @@ func TestSocketTransportCleanup(t *testing.T) { } func TestSocketTransportClientSendOnClosed(t *testing.T) { + t.Parallel() sockPath := filepath.Join(t.TempDir(), "closed.sock") server, err := NewSocketTransportServer(sockPath) @@ -120,7 +124,7 @@ func TestSocketTransportClientSendOnClosed(t *testing.T) { client.Close() - _, err = client.Send(context.Background(), itr.ToolRequest{ID: "fail"}) + _, err = client.Send(t.Context(), itr.ToolRequest{ID: "fail"}) assert.Error(t, err) server.Close() diff --git a/pkg/security/urlguard_test.go b/pkg/security/urlguard_test.go index 5a4a06259..1815faa34 100644 --- a/pkg/security/urlguard_test.go +++ b/pkg/security/urlguard_test.go @@ -8,6 +8,7 @@ import ( ) func TestValidateURL_AllowedURLs(t *testing.T) { + t.Parallel() tests := []string{ "https://example.com", "https://api.openai.com/v1/chat", @@ -23,6 +24,7 @@ func TestValidateURL_AllowedURLs(t *testing.T) { } func TestValidateURL_BlockedSchemes(t *testing.T) { + t.Parallel() tests := []string{ "file:///etc/passwd", "ftp://internal.server/data", @@ -38,6 +40,7 @@ func TestValidateURL_BlockedSchemes(t *testing.T) { } func TestValidateURL_BlockedHosts(t *testing.T) { + t.Parallel() tests := []struct { name string url string @@ -57,6 +60,7 @@ func TestValidateURL_BlockedHosts(t *testing.T) { } func TestValidateURL_BlockedIPs(t *testing.T) { + t.Parallel() tests := []struct { name string url string @@ -78,6 +82,7 @@ func TestValidateURL_BlockedIPs(t *testing.T) { } func TestValidateURL_EmptyAndInvalid(t *testing.T) { + t.Parallel() tests := []struct { name string url string @@ -95,6 +100,7 @@ func TestValidateURL_EmptyAndInvalid(t *testing.T) { } func TestIsBlockedIP(t *testing.T) { + t.Parallel() tests := []struct { ip string blocked bool diff --git a/pkg/security/vault_test.go b/pkg/security/vault_test.go index 7989c6ef5..e70fa6cc2 100644 --- a/pkg/security/vault_test.go +++ b/pkg/security/vault_test.go @@ -8,6 +8,7 @@ import ( ) func TestVault_RoundTrip(t *testing.T) { + t.Parallel() key, err := GenerateKey() require.NoError(t, err) require.Len(t, key, 32) @@ -35,6 +36,7 @@ func TestVault_RoundTrip(t *testing.T) { } func TestVault_DifferentCiphertexts(t *testing.T) { + t.Parallel() key, _ := GenerateKey() v, _ := NewVault(key) @@ -44,6 +46,7 @@ func TestVault_DifferentCiphertexts(t *testing.T) { } func TestVault_WrongKey(t *testing.T) { + t.Parallel() key1, _ := GenerateKey() key2, _ := GenerateKey() @@ -58,6 +61,7 @@ func TestVault_WrongKey(t *testing.T) { } func TestVault_TamperedCiphertext(t *testing.T) { + t.Parallel() key, _ := GenerateKey() v, _ := NewVault(key) @@ -70,6 +74,7 @@ func TestVault_TamperedCiphertext(t *testing.T) { } func TestVault_InvalidKeyLength(t *testing.T) { + t.Parallel() _, err := NewVault([]byte("too-short")) assert.ErrorIs(t, err, ErrKeyLength) @@ -78,6 +83,7 @@ func TestVault_InvalidKeyLength(t *testing.T) { } func TestVault_EmptyInput(t *testing.T) { + t.Parallel() key, _ := GenerateKey() v, _ := NewVault(key) @@ -90,6 +96,7 @@ func TestVault_EmptyInput(t *testing.T) { } func TestVault_BinaryData(t *testing.T) { + t.Parallel() key, _ := GenerateKey() v, _ := NewVault(key) diff --git a/pkg/security/zkp_test.go b/pkg/security/zkp_test.go index 6d3d9ba14..320f67876 100644 --- a/pkg/security/zkp_test.go +++ b/pkg/security/zkp_test.go @@ -9,6 +9,7 @@ import ( ) func TestSchnorrKeypair(t *testing.T) { + t.Parallel() key := make([]byte, 32) for i := range key { key[i] = byte(i + 1) @@ -22,11 +23,13 @@ func TestSchnorrKeypair(t *testing.T) { } func TestSchnorrKeypairRejectsBadLength(t *testing.T) { + t.Parallel() _, _, err := SchnorrKeypair([]byte("short")) assert.Error(t, err) } func TestSchnorrFullHandshake(t *testing.T) { + t.Parallel() masterKey := make([]byte, 32) for i := range masterKey { masterKey[i] = byte(i + 42) @@ -49,6 +52,7 @@ func TestSchnorrFullHandshake(t *testing.T) { } func TestSchnorrRejectsWrongKey(t *testing.T) { + t.Parallel() masterKey1 := make([]byte, 32) masterKey2 := make([]byte, 32) for i := range masterKey1 { @@ -73,6 +77,7 @@ func TestSchnorrRejectsWrongKey(t *testing.T) { } func TestZKPSessionManagerIssueAndValidate(t *testing.T) { + t.Parallel() masterKey := make([]byte, 32) for i := range masterKey { masterKey[i] = byte(i + 7) @@ -96,6 +101,7 @@ func TestZKPSessionManagerIssueAndValidate(t *testing.T) { } func TestZKPSessionManagerRejectsInvalidProof(t *testing.T) { + t.Parallel() masterKey := make([]byte, 32) for i := range masterKey { masterKey[i] = byte(i) @@ -109,6 +115,7 @@ func TestZKPSessionManagerRejectsInvalidProof(t *testing.T) { } func TestZKPSessionManagerExpiry(t *testing.T) { + t.Parallel() masterKey := make([]byte, 32) for i := range masterKey { masterKey[i] = byte(i + 3) @@ -128,6 +135,7 @@ func TestZKPSessionManagerExpiry(t *testing.T) { } func TestZKPSessionManagerRevoke(t *testing.T) { + t.Parallel() masterKey := make([]byte, 32) for i := range masterKey { masterKey[i] = byte(i + 5) @@ -148,6 +156,7 @@ func TestZKPSessionManagerRevoke(t *testing.T) { } func TestHandshakePayloadBinaryRoundTrip(t *testing.T) { + t.Parallel() hp := HandshakePayload{Phase: 3} for i := 0; i < 32; i++ { hp.RX[i] = byte(i) @@ -165,6 +174,7 @@ func TestHandshakePayloadBinaryRoundTrip(t *testing.T) { } func TestHandshakeResultBinaryRoundTrip(t *testing.T) { + t.Parallel() hr := HandshakeResult{ExpiresUnix: time.Now().Unix()} for i := 0; i < 32; i++ { hr.SessionToken[i] = byte(i + 200) @@ -179,6 +189,7 @@ func TestHandshakeResultBinaryRoundTrip(t *testing.T) { } func TestZKPSessionManagerCleanup(t *testing.T) { + t.Parallel() masterKey := make([]byte, 32) for i := range masterKey { masterKey[i] = byte(i + 11) diff --git a/pkg/session/manager_test.go b/pkg/session/manager_test.go index b65cd3c56..58c999b2d 100644 --- a/pkg/session/manager_test.go +++ b/pkg/session/manager_test.go @@ -1,7 +1,6 @@ package session import ( - "context" "fmt" "os" "path/filepath" @@ -9,6 +8,8 @@ import ( "time" jsonv2 "github.com/go-json-experiment/json" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,6 +18,7 @@ import ( ) func TestSanitizeFilename(t *testing.T) { + t.Parallel() tests := []struct { input string expected string @@ -40,6 +42,7 @@ func TestSanitizeFilename(t *testing.T) { } func TestSave_WithColonInKey(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() sm := NewSessionManager(tmpDir) @@ -71,6 +74,7 @@ func TestSave_WithColonInKey(t *testing.T) { } func TestSave_RejectsPathTraversal(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() sm := NewSessionManager(tmpDir) @@ -84,6 +88,7 @@ func TestSave_RejectsPathTraversal(t *testing.T) { } func TestTruncateHistory_ToolCallAware(t *testing.T) { + t.Parallel() sm := NewSessionManager("") // in-memory only key := "test-tool-truncation" @@ -129,6 +134,7 @@ func TestTruncateHistory_ToolCallAware(t *testing.T) { } func TestTruncateHistory_NoToolCalls(t *testing.T) { + t.Parallel() sm := NewSessionManager("") key := "test-no-tools" @@ -151,6 +157,7 @@ func TestTruncateHistory_NoToolCalls(t *testing.T) { } func TestAddFullMessage_NoHardCapTruncation(t *testing.T) { + t.Parallel() sm := NewSessionManager("") key := "test-hard-cap" @@ -167,6 +174,7 @@ func TestAddFullMessage_NoHardCapTruncation(t *testing.T) { } func TestCleanupStale(t *testing.T) { + t.Parallel() sm := NewSessionManager("") sm.AddFullMessage("active", messages.Message{Role: "user", Content: "hi"}) @@ -195,11 +203,12 @@ func TestCleanupStale(t *testing.T) { } func TestSessionManager_DelegatePersistence(t *testing.T) { + t.Parallel() del, err := delegate.NewLibSQLInMemory() if err != nil { t.Fatalf("NewLibSQLInMemory: %v", err) } - if err := del.Init(context.Background()); err != nil { + if err := del.Init(t.Context()); err != nil { t.Fatalf("Init: %v", err) } defer del.Close() @@ -215,7 +224,7 @@ func TestSessionManager_DelegatePersistence(t *testing.T) { t.Fatalf("expected 2 messages in-memory, got %d", len(history)) } - items, err := del.ListRecallItems(context.Background(), "test-agent", key, 100, 0) + items, err := del.ListRecallItems(t.Context(), "test-agent", key, 100, 0) if err != nil { t.Fatalf("ListRecallItems: %v", err) } @@ -228,11 +237,12 @@ func TestSessionManager_DelegatePersistence(t *testing.T) { } func TestSessionManager_DelegateSaveIsNoop(t *testing.T) { + t.Parallel() del, err := delegate.NewLibSQLInMemory() if err != nil { t.Fatalf("NewLibSQLInMemory: %v", err) } - if err := del.Init(context.Background()); err != nil { + if err := del.Init(t.Context()); err != nil { t.Fatalf("Init: %v", err) } defer del.Close() @@ -256,11 +266,12 @@ func TestSessionManager_DelegateSaveIsNoop(t *testing.T) { } func TestSessionManager_DelegateBootstrapPaginationAndOrder(t *testing.T) { + t.Parallel() del, err := delegate.NewLibSQLInMemory() if err != nil { t.Fatalf("NewLibSQLInMemory: %v", err) } - if err := del.Init(context.Background()); err != nil { + if err := del.Init(t.Context()); err != nil { t.Fatalf("Init: %v", err) } defer del.Close() @@ -291,9 +302,10 @@ func TestSessionManager_DelegateBootstrapPaginationAndOrder(t *testing.T) { } func TestSessionManager_ProjectionPointerPersistedAndRestored(t *testing.T) { + t.Parallel() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) - require.NoError(t, del.Init(context.Background())) + require.NoError(t, del.Init(t.Context())) defer del.Close() sessionKey := "ptr-persist" @@ -303,35 +315,34 @@ func TestSessionManager_ProjectionPointerPersistedAndRestored(t *testing.T) { sm.AddMessage(sessionKey, "user", "third") // Read persisted pointer from KV - raw, err := del.GetKV(context.Background(), "test-agent", projectionPointerKey(sessionKey)) + raw, err := del.GetKV(t.Context(), "test-agent", projectionPointerKey(sessionKey)) require.NoError(t, err) require.NotEmpty(t, raw) var ptr ProjectionPointer require.NoError(t, jsonv2.Unmarshal([]byte(raw), &ptr)) - assert.Equal(t, 3, ptr.Count) - assert.False(t, ptr.FirstMessageID.IsZero()) - assert.False(t, ptr.LastMessageID.IsZero()) + assert.NotZero(t, ptr.FirstMessageID) + assert.NotZero(t, ptr.LastMessageID) assert.False(t, ptr.FirstCreatedAt.IsZero()) assert.False(t, ptr.LastCreatedAt.IsZero()) + assert.Equal(t, 3, ptr.Count) // New manager restores; pointer is re-persisted (same values) sm2 := NewSessionManager("", WithSessionDelegate(del, "test-agent")) history := sm2.GetHistory(sessionKey) require.Len(t, history, 3) - raw2, err := del.GetKV(context.Background(), "test-agent", projectionPointerKey(sessionKey)) + raw2, err := del.GetKV(t.Context(), "test-agent", projectionPointerKey(sessionKey)) require.NoError(t, err) require.NotEmpty(t, raw2) var ptr2 ProjectionPointer require.NoError(t, jsonv2.Unmarshal([]byte(raw2), &ptr2)) - assert.Equal(t, ptr.Count, ptr2.Count) - assert.Equal(t, ptr.FirstMessageID, ptr2.FirstMessageID) - assert.Equal(t, ptr.LastMessageID, ptr2.LastMessageID) + assert.Empty(t, cmp.Diff(ptr, ptr2, cmpopts.IgnoreFields(ProjectionPointer{}, "FirstCreatedAt", "LastCreatedAt"))) } func TestSessionManager_ProjectionPointerUpdatedOnAppend(t *testing.T) { + t.Parallel() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) - require.NoError(t, del.Init(context.Background())) + require.NoError(t, del.Init(t.Context())) defer del.Close() sessionKey := "ptr-append" @@ -339,7 +350,7 @@ func TestSessionManager_ProjectionPointerUpdatedOnAppend(t *testing.T) { for i := 0; i < 4; i++ { sm.AddMessage(sessionKey, "user", fmt.Sprintf("msg-%d", i)) - raw, err := del.GetKV(context.Background(), "test-agent", projectionPointerKey(sessionKey)) + raw, err := del.GetKV(t.Context(), "test-agent", projectionPointerKey(sessionKey)) require.NoError(t, err) require.NotEmpty(t, raw) var ptr ProjectionPointer @@ -349,9 +360,10 @@ func TestSessionManager_ProjectionPointerUpdatedOnAppend(t *testing.T) { } func TestSessionManager_IntegrityMismatchRestoreStillSucceeds(t *testing.T) { + t.Parallel() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) - require.NoError(t, del.Init(context.Background())) + require.NoError(t, del.Init(t.Context())) defer del.Close() sessionKey := "integrity-mismatch" @@ -362,7 +374,7 @@ func TestSessionManager_IntegrityMismatchRestoreStillSucceeds(t *testing.T) { // Corrupt the stored pointer to simulate prior state mismatch corrupt := ProjectionPointer{Count: 0} data, _ := jsonv2.Marshal(corrupt) - require.NoError(t, del.UpsertKV(context.Background(), "test-agent", projectionPointerKey(sessionKey), string(data))) + require.NoError(t, del.UpsertKV(t.Context(), "test-agent", projectionPointerKey(sessionKey), string(data))) // New manager restores; should succeed (lossless) despite mismatch sm2 := NewSessionManager("", WithSessionDelegate(del, "test-agent")) @@ -372,7 +384,7 @@ func TestSessionManager_IntegrityMismatchRestoreStillSucceeds(t *testing.T) { assert.Equal(t, "b", history[1].Content) // Pointer should now reflect restored state - raw, err := del.GetKV(context.Background(), "test-agent", projectionPointerKey(sessionKey)) + raw, err := del.GetKV(t.Context(), "test-agent", projectionPointerKey(sessionKey)) require.NoError(t, err) require.NotEmpty(t, raw) var ptr ProjectionPointer @@ -381,9 +393,10 @@ func TestSessionManager_IntegrityMismatchRestoreStillSucceeds(t *testing.T) { } func TestSessionManager_ProjectionBackfillStatusPersistedOnBootstrap(t *testing.T) { + t.Parallel() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) - require.NoError(t, del.Init(context.Background())) + require.NoError(t, del.Init(t.Context())) defer del.Close() writer := NewSessionManager("", WithSessionDelegate(del, "test-agent")) @@ -393,7 +406,7 @@ func TestSessionManager_ProjectionBackfillStatusPersistedOnBootstrap(t *testing. _ = NewSessionManager("", WithSessionDelegate(del, "test-agent")) - raw, err := del.GetKV(context.Background(), "test-agent", projectionBackfillStatusKey()) + raw, err := del.GetKV(t.Context(), "test-agent", projectionBackfillStatusKey()) require.NoError(t, err) require.NotEmpty(t, raw) diff --git a/pkg/skills/graph_test.go b/pkg/skills/graph_test.go index 3979b956d..40ea6c1fc 100644 --- a/pkg/skills/graph_test.go +++ b/pkg/skills/graph_test.go @@ -10,6 +10,7 @@ import ( ) func TestParseWikilinks(t *testing.T) { + t.Parallel() tests := []struct { name string content string @@ -66,6 +67,7 @@ func TestParseWikilinks(t *testing.T) { } func TestMergeUnique(t *testing.T) { + t.Parallel() tests := []struct { name string a, b []string @@ -94,6 +96,7 @@ func writeSkill(t *testing.T, dir, name, content string) { } func TestBuildGraph_Basic(t *testing.T) { + t.Parallel() tmp := t.TempDir() writeSkill(t, tmp, "risk-management", `--- @@ -152,6 +155,7 @@ No wikilinks here. } func TestBuildGraph_FrontmatterLinksAndWikilinks(t *testing.T) { + t.Parallel() tmp := t.TempDir() writeSkill(t, tmp, "alpha", `--- @@ -186,6 +190,7 @@ No links. } func TestTraverseFrom(t *testing.T) { + t.Parallel() tmp := t.TempDir() writeSkill(t, tmp, "a", `--- @@ -233,6 +238,7 @@ No outgoing links. } func TestSearchSkills(t *testing.T) { + t.Parallel() tmp := t.TempDir() writeSkill(t, tmp, "risk-management", `--- @@ -278,6 +284,7 @@ Content. } func TestListMOCs(t *testing.T) { + t.Parallel() tmp := t.TempDir() writeSkill(t, tmp, "trading-moc", `--- @@ -305,6 +312,7 @@ Content. } func TestGetIndex(t *testing.T) { + t.Parallel() tmp := t.TempDir() writeSkill(t, tmp, "index", `--- @@ -332,6 +340,7 @@ Content. } func TestGetIndex_None(t *testing.T) { + t.Parallel() tmp := t.TempDir() writeSkill(t, tmp, "some-skill", `--- name: some-skill @@ -346,6 +355,7 @@ Content. } func TestExtendedFrontmatter_JSON(t *testing.T) { + t.Parallel() tmp := t.TempDir() writeSkill(t, tmp, "json-skill", `--- diff --git a/pkg/skills/loader_test.go b/pkg/skills/loader_test.go index e0e7109cf..80182e8d1 100644 --- a/pkg/skills/loader_test.go +++ b/pkg/skills/loader_test.go @@ -7,6 +7,7 @@ import ( ) func TestSkillsInfoValidate(t *testing.T) { + t.Parallel() testcases := []struct { name string skillName string diff --git a/pkg/skills/templates_test.go b/pkg/skills/templates_test.go index 424e071d6..1b3aacec5 100644 --- a/pkg/skills/templates_test.go +++ b/pkg/skills/templates_test.go @@ -10,6 +10,7 @@ import ( ) func TestAvailableTemplates(t *testing.T) { + t.Parallel() templates := AvailableTemplates() assert.Contains(t, templates, "trading") assert.Contains(t, templates, "legal") @@ -17,6 +18,7 @@ func TestAvailableTemplates(t *testing.T) { } func TestInstallTemplate(t *testing.T) { + t.Parallel() tmp := t.TempDir() err := InstallTemplate("trading", tmp) @@ -35,6 +37,7 @@ func TestInstallTemplate(t *testing.T) { } func TestInstallTemplate_BuildsValidGraph(t *testing.T) { + t.Parallel() tmp := t.TempDir() require.NoError(t, InstallTemplate("trading", tmp)) @@ -56,6 +59,7 @@ func TestInstallTemplate_BuildsValidGraph(t *testing.T) { } func TestInstallTemplate_NotFound(t *testing.T) { + t.Parallel() tmp := t.TempDir() err := InstallTemplate("nonexistent", tmp) assert.Error(t, err) diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index c223e9afb..0d512ab0a 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -1,7 +1,6 @@ package state import ( - "context" "fmt" "os" "path/filepath" @@ -11,7 +10,10 @@ import ( ) func TestAtomicSave(t *testing.T) { + t.Parallel( // Create temp workspace + ) + tmpDir, err := os.MkdirTemp("", "state-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -21,7 +23,7 @@ func TestAtomicSave(t *testing.T) { sm := NewManager(tmpDir) // Test SetLastChannel - err = sm.SetLastChannel(context.Background(), "test-channel") + err = sm.SetLastChannel(t.Context(), "test-channel") if err != nil { t.Fatalf("SetLastChannel failed: %v", err) } @@ -51,6 +53,7 @@ func TestAtomicSave(t *testing.T) { } func TestSetLastChatID(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "state-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -60,7 +63,7 @@ func TestSetLastChatID(t *testing.T) { sm := NewManager(tmpDir) // Test SetLastChatID - err = sm.SetLastChatID(context.Background(), "test-chat-id") + err = sm.SetLastChatID(t.Context(), "test-chat-id") if err != nil { t.Fatalf("SetLastChatID failed: %v", err) } @@ -84,6 +87,7 @@ func TestSetLastChatID(t *testing.T) { } func TestAtomicity_NoCorruptionOnInterrupt(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "state-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -93,7 +97,7 @@ func TestAtomicity_NoCorruptionOnInterrupt(t *testing.T) { sm := NewManager(tmpDir) // Write initial state - err = sm.SetLastChannel(context.Background(), "initial-channel") + err = sm.SetLastChannel(t.Context(), "initial-channel") if err != nil { t.Fatalf("SetLastChannel failed: %v", err) } @@ -115,7 +119,7 @@ func TestAtomicity_NoCorruptionOnInterrupt(t *testing.T) { os.Remove(tempFile) // Now do a proper save - err = sm.SetLastChannel(context.Background(), "new-channel") + err = sm.SetLastChannel(t.Context(), "new-channel") if err != nil { t.Fatalf("SetLastChannel failed: %v", err) } @@ -127,6 +131,7 @@ func TestAtomicity_NoCorruptionOnInterrupt(t *testing.T) { } func TestConcurrentAccess(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "state-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -140,7 +145,7 @@ func TestConcurrentAccess(t *testing.T) { for i := 0; i < 10; i++ { go func(idx int) { channel := fmt.Sprintf("channel-%d", idx) - sm.SetLastChannel(context.Background(), channel) + sm.SetLastChannel(t.Context(), channel) done <- true }(i) } @@ -170,6 +175,7 @@ func TestConcurrentAccess(t *testing.T) { } func TestNewManager_ExistingState(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "state-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) @@ -178,8 +184,8 @@ func TestNewManager_ExistingState(t *testing.T) { // Create initial state sm1 := NewManager(tmpDir) - sm1.SetLastChannel(context.Background(), "existing-channel") - sm1.SetLastChatID(context.Background(), "existing-chat-id") + sm1.SetLastChannel(t.Context(), "existing-channel") + sm1.SetLastChatID(t.Context(), "existing-chat-id") // Create new manager with same workspace sm2 := NewManager(tmpDir) @@ -195,6 +201,7 @@ func TestNewManager_ExistingState(t *testing.T) { } func TestNewManager_EmptyWorkspace(t *testing.T) { + t.Parallel() tmpDir, err := os.MkdirTemp("", "state-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) diff --git a/pkg/sync/identity_test.go b/pkg/sync/identity_test.go index fef25297e..1a1dabf77 100644 --- a/pkg/sync/identity_test.go +++ b/pkg/sync/identity_test.go @@ -85,6 +85,7 @@ func setupIdentityDir(t *testing.T, files map[string]string) string { } func TestSyncAll_InsertsNewFiles(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": "# Agent\nYou are helpful.", "SOUL.md": "# Soul\nCurious and kind.", @@ -95,7 +96,7 @@ func TestSyncAll_InsertsNewFiles(t *testing.T) { store := newMockStore() s := New(dir, "agent-1", store) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) for _, name := range IdentityFiles { doc := store.getDoc("agent-1", name) @@ -109,6 +110,7 @@ func TestSyncAll_InsertsNewFiles(t *testing.T) { } func TestSyncAll_SkipsUnchangedFiles(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": "# Agent\nSame content.", }) @@ -116,29 +118,30 @@ func TestSyncAll_SkipsUnchangedFiles(t *testing.T) { store := newMockStore() s := New(dir, "agent-1", store) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) firstDoc := store.getDoc("agent-1", "AGENT.md") require.NotNil(t, firstDoc) firstID := firstDoc.ID - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) secondDoc := store.getDoc("agent-1", "AGENT.md") assert.Equal(t, firstID, secondDoc.ID, "unchanged file should not be re-upserted") } func TestSyncAll_UpsertsModifiedFiles(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": "# Agent\nVersion 1", }) store := newMockStore() s := New(dir, "agent-1", store) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) v1Hash := store.getHash("agent-1", "AGENT.md") require.NoError(t, os.WriteFile(filepath.Join(dir, "AGENT.md"), []byte("# Agent\nVersion 2"), 0644)) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) v2Hash := store.getHash("agent-1", "AGENT.md") assert.NotEqual(t, v1Hash, v2Hash, "hash should change after file modification") @@ -148,6 +151,7 @@ func TestSyncAll_UpsertsModifiedFiles(t *testing.T) { } func TestSyncAll_SkipsMissingFiles(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": "# Agent only", }) @@ -155,7 +159,7 @@ func TestSyncAll_SkipsMissingFiles(t *testing.T) { store := newMockStore() s := New(dir, "agent-1", store) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) assert.NotNil(t, store.getDoc("agent-1", "AGENT.md")) assert.Nil(t, store.getDoc("agent-1", "SOUL.md"), "missing file should not create a doc") @@ -164,6 +168,7 @@ func TestSyncAll_SkipsMissingFiles(t *testing.T) { } func TestSyncAll_SkipsEmptyFiles(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": " \n\t\n ", }) @@ -171,11 +176,12 @@ func TestSyncAll_SkipsEmptyFiles(t *testing.T) { store := newMockStore() s := New(dir, "agent-1", store) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) assert.Nil(t, store.getDoc("agent-1", "AGENT.md"), "empty/whitespace-only file should be skipped") } func TestSyncAll_IsolatesAgents(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": "# Shared agent file", }) @@ -184,8 +190,8 @@ func TestSyncAll_IsolatesAgents(t *testing.T) { s1 := New(dir, "agent-a", store) s2 := New(dir, "agent-b", store) - require.NoError(t, s1.SyncAll(context.Background())) - require.NoError(t, s2.SyncAll(context.Background())) + require.NoError(t, s1.SyncAll(t.Context())) + require.NoError(t, s2.SyncAll(t.Context())) docA := store.getDoc("agent-a", "AGENT.md") docB := store.getDoc("agent-b", "AGENT.md") @@ -195,51 +201,54 @@ func TestSyncAll_IsolatesAgents(t *testing.T) { } func TestCheckAndSync_DetectsModifiedFile(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": "# Original", }) store := newMockStore() s := New(dir, "agent-1", store) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) time.Sleep(50 * time.Millisecond) require.NoError(t, os.WriteFile(filepath.Join(dir, "AGENT.md"), []byte("# Modified"), 0644)) - require.NoError(t, s.CheckAndSync(context.Background())) + require.NoError(t, s.CheckAndSync(t.Context())) doc := store.getDoc("agent-1", "AGENT.md") assert.Contains(t, doc.Content, "Modified") } func TestCheckAndSync_SkipsUntouchedFiles(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": "# Stable", }) store := newMockStore() s := New(dir, "agent-1", store) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) hash1 := store.getHash("agent-1", "AGENT.md") s.lastSync.Store(time.Now().Add(time.Second).UnixNano()) - require.NoError(t, s.CheckAndSync(context.Background())) + require.NoError(t, s.CheckAndSync(t.Context())) hash2 := store.getHash("agent-1", "AGENT.md") assert.Equal(t, hash1, hash2, "untouched file should not trigger re-sync") } func TestWatch_DetectsFileChange(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": "# Initial", }) store := newMockStore() s := New(dir, "agent-1", store) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) - ctx := context.Background() + ctx := t.Context() require.NoError(t, s.Watch(ctx)) defer s.Close() @@ -252,15 +261,16 @@ func TestWatch_DetectsFileChange(t *testing.T) { } func TestWatch_IgnoresNonIdentityFiles(t *testing.T) { + t.Parallel() dir := setupIdentityDir(t, map[string]string{ "AGENT.md": "# Agent", }) store := newMockStore() s := New(dir, "agent-1", store) - require.NoError(t, s.SyncAll(context.Background())) + require.NoError(t, s.SyncAll(t.Context())) - ctx := context.Background() + ctx := t.Context() require.NoError(t, s.Watch(ctx)) defer s.Close() @@ -271,6 +281,7 @@ func TestWatch_IgnoresNonIdentityFiles(t *testing.T) { } func TestContentHash_Deterministic(t *testing.T) { + t.Parallel() data := []byte("hello world") h1 := contentHash(data) h2 := contentHash(data) @@ -279,12 +290,14 @@ func TestContentHash_Deterministic(t *testing.T) { } func TestContentHash_DifferentForDifferentContent(t *testing.T) { + t.Parallel() h1 := contentHash([]byte("version 1")) h2 := contentHash([]byte("version 2")) assert.NotEqual(t, h1, h2) } func TestIsIdentityFile(t *testing.T) { + t.Parallel() tests := []struct { name string want bool @@ -306,6 +319,7 @@ func TestIsIdentityFile(t *testing.T) { } func TestNew_SetsFields(t *testing.T) { + t.Parallel() store := newMockStore() s := New("/tmp/identity", "test-agent", store) assert.Equal(t, "/tmp/identity", s.identityDir) diff --git a/pkg/tools/agentic_map_test.go b/pkg/tools/agentic_map_test.go index 55bd66c8e..750b1ad8f 100644 --- a/pkg/tools/agentic_map_test.go +++ b/pkg/tools/agentic_map_test.go @@ -13,6 +13,7 @@ import ( ) func TestAgenticMapTool_Execute_Success(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) manager.SetRunLoop(func(_ context.Context, _ ToolLoopConfig, _, userPrompt, _, _ string) (*ToolLoopResult, error) { @@ -20,7 +21,7 @@ func TestAgenticMapTool_Execute_Success(t *testing.T) { }) tool := NewAgenticMapTool(manager) - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(t.Context(), map[string]interface{}{ "items": []interface{}{ map[string]interface{}{"name": "a"}, map[string]interface{}{"name": "b"}, @@ -46,6 +47,7 @@ func TestAgenticMapTool_Execute_Success(t *testing.T) { } func TestAgenticMapTool_Execute_RetriesFailedItems(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) callCount := 0 @@ -58,7 +60,7 @@ func TestAgenticMapTool_Execute_RetriesFailedItems(t *testing.T) { }) tool := NewAgenticMapTool(manager) - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(t.Context(), map[string]interface{}{ "items": []interface{}{map[string]interface{}{"name": "retry-me"}}, "task_template": "Retry item {{index}} => {{item_json}}", "max_retries": float64(2), @@ -80,11 +82,12 @@ func TestAgenticMapTool_Execute_RetriesFailedItems(t *testing.T) { } func TestAgenticMapTool_Execute_RequiresPlaceholders(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) tool := NewAgenticMapTool(manager) - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(t.Context(), map[string]interface{}{ "items": []interface{}{map[string]interface{}{"name": "x"}}, "task_template": "Handle item without placeholders", }) @@ -95,11 +98,12 @@ func TestAgenticMapTool_Execute_RequiresPlaceholders(t *testing.T) { } func TestAgenticMapTool_Execute_ContextCancelled(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) tool := NewAgenticMapTool(manager) - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) cancel() result := tool.Execute(ctx, map[string]interface{}{ diff --git a/pkg/tools/call_test.go b/pkg/tools/call_test.go index bdf721aaf..f8ad5dc4f 100644 --- a/pkg/tools/call_test.go +++ b/pkg/tools/call_test.go @@ -7,6 +7,7 @@ import ( ) func TestToolCallTool_Name(t *testing.T) { + t.Parallel() r := NewToolRegistry() tc := NewToolCallTool(r) if tc.Name() != "tool_call" { @@ -15,6 +16,7 @@ func TestToolCallTool_Name(t *testing.T) { } func TestToolCallTool_Description(t *testing.T) { + t.Parallel() r := NewToolRegistry() tc := NewToolCallTool(r) if tc.Description() == "" { @@ -23,6 +25,7 @@ func TestToolCallTool_Description(t *testing.T) { } func TestToolCallTool_Parameters(t *testing.T) { + t.Parallel() r := NewToolRegistry() tc := NewToolCallTool(r) params := tc.Parameters() @@ -40,9 +43,10 @@ func TestToolCallTool_Parameters(t *testing.T) { } func TestToolCallTool_MissingToolName(t *testing.T) { + t.Parallel() r := NewToolRegistry() tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{}) + result := tc.Execute(t.Context(), map[string]interface{}{}) if !result.IsError { t.Error("expected error for missing tool_name") @@ -50,11 +54,12 @@ func TestToolCallTool_MissingToolName(t *testing.T) { } func TestToolCallTool_DispatchesToTool(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read a file"}) tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "read_file", "arguments": map[string]interface{}{"path": "/tmp/test.txt"}, }) @@ -68,11 +73,12 @@ func TestToolCallTool_DispatchesToTool(t *testing.T) { } func TestToolCallTool_PreventRecursion_ToolCall(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.RegisterMetaTools() tc, _ := r.Get("tool_call") - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "tool_call", }) @@ -82,11 +88,12 @@ func TestToolCallTool_PreventRecursion_ToolCall(t *testing.T) { } func TestToolCallTool_PreventRecursion_ToolSearch(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.RegisterMetaTools() tc, _ := r.Get("tool_call") - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "tool_search", }) @@ -96,11 +103,12 @@ func TestToolCallTool_PreventRecursion_ToolSearch(t *testing.T) { } func TestToolCallTool_JSONStringArguments(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&echoTool{}) tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "echo", "arguments": `{"msg":"hello"}`, }) @@ -114,11 +122,12 @@ func TestToolCallTool_JSONStringArguments(t *testing.T) { } func TestToolCallTool_NilArguments(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "no_args", desc: "Tool that needs no args"}) tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "no_args", }) @@ -128,11 +137,12 @@ func TestToolCallTool_NilArguments(t *testing.T) { } func TestToolCallTool_InvalidJSONArguments(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubToolWithSchema{name: "read_file", desc: "Read a file"}) tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "read_file", "arguments": "not-json", }) @@ -147,11 +157,12 @@ func TestToolCallTool_InvalidJSONArguments(t *testing.T) { } func TestToolCallTool_MissingRequiredArgs_IncludesSchemaHint(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubToolWithSchema{name: "read_file", desc: "Read a file"}) tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "read_file", "arguments": map[string]interface{}{}, }) @@ -168,10 +179,11 @@ func TestToolCallTool_MissingRequiredArgs_IncludesSchemaHint(t *testing.T) { } func TestToolCallTool_ToolNotFoundSuggestsSearch(t *testing.T) { + t.Parallel() r := NewToolRegistry() tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "nonexistent", }) @@ -184,6 +196,7 @@ func TestToolCallTool_ToolNotFoundSuggestsSearch(t *testing.T) { } func TestToolCallTool_ContextPropagation(t *testing.T) { + t.Parallel() r := NewToolRegistry() ct := &contextCaptureTool{} r.Register(ct) @@ -191,7 +204,7 @@ func TestToolCallTool_ContextPropagation(t *testing.T) { tc := NewToolCallTool(r) tc.SetContext("test-channel", "test-chat") - tc.Execute(context.Background(), map[string]interface{}{ + tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "capture", "arguments": map[string]interface{}{}, }) @@ -203,6 +216,7 @@ func TestToolCallTool_ContextPropagation(t *testing.T) { } func TestToolCallTool_ResourceProvider_LoadsResources(t *testing.T) { + t.Parallel() r := NewToolRegistry() rt := &resourceAwareTool{ resources: map[string]string{ @@ -212,7 +226,7 @@ func TestToolCallTool_ResourceProvider_LoadsResources(t *testing.T) { r.Register(rt) tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "resource_tool", "arguments": map[string]interface{}{}, }) @@ -231,12 +245,13 @@ func TestToolCallTool_ResourceProvider_LoadsResources(t *testing.T) { } func TestToolCallTool_NormalizesCommaSeparatedToolName(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "write_file", desc: "write"}) r.Register(&stubTool{name: "read_file", desc: "read"}) tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "write_file, read_file", "arguments": map[string]interface{}{"path": "x.txt"}, }) @@ -250,11 +265,12 @@ func TestToolCallTool_NormalizesCommaSeparatedToolName(t *testing.T) { } func TestToolCallTool_NormalizesEmbeddedToolName(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "exec", desc: "exec"}) tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "exec_tool_search_query_exec_run_shell_command_return_output_caution", "arguments": map[string]interface{}{"command": "echo hi"}, }) @@ -268,11 +284,12 @@ func TestToolCallTool_NormalizesEmbeddedToolName(t *testing.T) { } func TestToolCallTool_NoResourceProvider_StillWorks(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "plain", desc: "No resources"}) tc := NewToolCallTool(r) - result := tc.Execute(context.Background(), map[string]interface{}{ + result := tc.Execute(t.Context(), map[string]interface{}{ "tool_name": "plain", "arguments": map[string]interface{}{}, }) @@ -283,7 +300,8 @@ func TestToolCallTool_NoResourceProvider_StillWorks(t *testing.T) { } func TestResourcesFromContext_Empty(t *testing.T) { - res := ResourcesFromContext(context.Background()) + t.Parallel() + res := ResourcesFromContext(t.Context()) if res != nil { t.Error("expected nil for empty context") } diff --git a/pkg/tools/dag_test.go b/pkg/tools/dag_test.go index d5d22b24f..5a4c68979 100644 --- a/pkg/tools/dag_test.go +++ b/pkg/tools/dag_test.go @@ -1,7 +1,6 @@ package tools import ( - "context" "encoding/json" "strconv" "testing" @@ -16,7 +15,7 @@ import ( func setupDAGTools(t *testing.T) (DAGToolDeps, *delegate.LibSQLDelegate) { d, err := delegate.NewLibSQLInMemory() require.NoError(t, err) - require.NoError(t, d.Init(context.Background())) + require.NoError(t, d.Init(t.Context())) // Insert session messages so dag_expand/dag_grep have data agentID, sessionKey := "test-agent", "test-session" @@ -26,7 +25,7 @@ func setupDAGTools(t *testing.T) (DAGToolDeps, *delegate.LibSQLDelegate) { role = "assistant" } content := "Message number " + strconv.Itoa(i) - require.NoError(t, d.InsertSessionMessage(context.Background(), agentID, sessionKey, role, content)) + require.NoError(t, d.InsertSessionMessage(t.Context(), agentID, sessionKey, role, content)) } // Persist a DAG snapshot @@ -40,7 +39,7 @@ func setupDAGTools(t *testing.T) (DAGToolDeps, *delegate.LibSQLDelegate) { msgs[i] = dag.Message{Role: role, Content: "Message " + string(rune('A'+i%26))} } dagOut := compressor.Compress(msgs) - require.NoError(t, d.PersistDAG(context.Background(), agentID, sessionKey, &dag.PersistSnapshot{ + require.NoError(t, d.PersistDAG(t.Context(), agentID, sessionKey, &dag.PersistSnapshot{ FromMsgIdx: 0, ToMsgIdx: 16, MsgCount: 16, @@ -66,11 +65,12 @@ func setupDAGTools(t *testing.T) (DAGToolDeps, *delegate.LibSQLDelegate) { } func TestDagDescribeTool(t *testing.T) { + t.Parallel() deps, del := setupDAGTools(t) defer del.Close() tool := NewDagDescribeTool(deps) - ctx := context.Background() + ctx := t.Context() // Describe an existing node (chunk-1 from default config with 16 msgs) res := tool.Execute(ctx, map[string]interface{}{"node_id": "chunk-1", "session_key": "test-session"}) @@ -80,10 +80,11 @@ func TestDagDescribeTool(t *testing.T) { } func TestDagDescribeTool_NoSnapshot(t *testing.T) { + t.Parallel() d, err := delegate.NewLibSQLInMemory() require.NoError(t, err) defer d.Close() - require.NoError(t, d.Init(context.Background())) + require.NoError(t, d.Init(t.Context())) tool := NewDagDescribeTool(DAGToolDeps{ Queries: d.Queries(), @@ -91,17 +92,18 @@ func TestDagDescribeTool_NoSnapshot(t *testing.T) { AgentID: "x", SessionFn: func() string { return "nonexistent" }, }) - res := tool.Execute(context.Background(), map[string]interface{}{"node_id": "chunk-1"}) + res := tool.Execute(t.Context(), map[string]interface{}{"node_id": "chunk-1"}) assert.True(t, res.IsError) assert.Contains(t, res.ForLLM, "no DAG snapshot") } func TestDagGrepTool(t *testing.T) { + t.Parallel() deps, del := setupDAGTools(t) defer del.Close() tool := NewDagGrepTool(deps) - ctx := context.Background() + ctx := t.Context() res := tool.Execute(ctx, map[string]interface{}{"query": "Message", "session_key": "test-session"}) assert.False(t, res.IsError) @@ -110,11 +112,12 @@ func TestDagGrepTool(t *testing.T) { } func TestDagGrepTool_ScopedByNode(t *testing.T) { + t.Parallel() deps, del := setupDAGTools(t) defer del.Close() tool := NewDagGrepTool(deps) - ctx := context.Background() + ctx := t.Context() res := tool.Execute(ctx, map[string]interface{}{ "query": "number", @@ -126,11 +129,12 @@ func TestDagGrepTool_ScopedByNode(t *testing.T) { } func TestDagExpandTool(t *testing.T) { + t.Parallel() deps, del := setupDAGTools(t) defer del.Close() tool := NewDagExpandTool(deps) - ctx := context.Background() + ctx := t.Context() // Expand chunk-1; needs Lister to return messages res := tool.Execute(ctx, map[string]interface{}{"node_id": "chunk-1", "session_key": "test-session"}) @@ -143,16 +147,17 @@ func TestDagExpandTool(t *testing.T) { } func TestDagExpandTool_NoReader(t *testing.T) { + t.Parallel() d, err := delegate.NewLibSQLInMemory() require.NoError(t, err) defer d.Close() - require.NoError(t, d.Init(context.Background())) + require.NoError(t, d.Init(t.Context())) // Persist minimal DAG compressor := dag.NewCompressor(dag.DefaultCompressorConfig()) msgs := []dag.Message{{Role: "user", Content: "x"}} dagOut := compressor.Compress(msgs) - require.NoError(t, d.PersistDAG(context.Background(), "a", "s", &dag.PersistSnapshot{ + require.NoError(t, d.PersistDAG(t.Context(), "a", "s", &dag.PersistSnapshot{ FromMsgIdx: 0, ToMsgIdx: 1, MsgCount: 1, DAG: dagOut, })) @@ -162,16 +167,17 @@ func TestDagExpandTool_NoReader(t *testing.T) { AgentID: "a", SessionFn: func() string { return "s" }, }) - res := tool.Execute(context.Background(), map[string]interface{}{"node_id": "chunk-1", "session_key": "s"}) + res := tool.Execute(t.Context(), map[string]interface{}{"node_id": "chunk-1", "session_key": "s"}) assert.True(t, res.IsError) assert.Contains(t, res.ForLLM, "dag query store is not configured") } func TestDagExpandTool_RecoveryReference(t *testing.T) { + t.Parallel() deps, del := setupDAGTools(t) defer del.Close() - ctx := context.Background() + ctx := t.Context() record := DAGRecoveryRecord{ NodeID: DAGRecoveryNodePrefix + "test-recovery", SessionKey: "test-session", diff --git a/pkg/tools/edit_test.go b/pkg/tools/edit_test.go index c4c02772d..303fac2e4 100644 --- a/pkg/tools/edit_test.go +++ b/pkg/tools/edit_test.go @@ -1,7 +1,6 @@ package tools import ( - "context" "os" "path/filepath" "strings" @@ -10,12 +9,13 @@ import ( // TestEditTool_EditFile_Success verifies successful file editing func TestEditTool_EditFile_Success(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "test.txt") os.WriteFile(testFile, []byte("Hello World\nThis is a test"), 0644) tool := NewEditFileTool(tmpDir, true) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": testFile, "old_text": "World", @@ -55,11 +55,12 @@ func TestEditTool_EditFile_Success(t *testing.T) { // TestEditTool_EditFile_NotFound verifies error handling for non-existent file func TestEditTool_EditFile_NotFound(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "nonexistent.txt") tool := NewEditFileTool(tmpDir, true) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": testFile, "old_text": "old", @@ -81,12 +82,13 @@ func TestEditTool_EditFile_NotFound(t *testing.T) { // TestEditTool_EditFile_OldTextNotFound verifies error when old_text doesn't exist func TestEditTool_EditFile_OldTextNotFound(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "test.txt") os.WriteFile(testFile, []byte("Hello World"), 0644) tool := NewEditFileTool(tmpDir, true) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": testFile, "old_text": "Goodbye", @@ -108,12 +110,13 @@ func TestEditTool_EditFile_OldTextNotFound(t *testing.T) { // TestEditTool_EditFile_MultipleMatches verifies error when old_text appears multiple times func TestEditTool_EditFile_MultipleMatches(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "test.txt") os.WriteFile(testFile, []byte("test test test"), 0644) tool := NewEditFileTool(tmpDir, true) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": testFile, "old_text": "test", @@ -135,13 +138,14 @@ func TestEditTool_EditFile_MultipleMatches(t *testing.T) { // TestEditTool_EditFile_OutsideAllowedDir verifies error when path is outside allowed directory func TestEditTool_EditFile_OutsideAllowedDir(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() otherDir := t.TempDir() testFile := filepath.Join(otherDir, "test.txt") os.WriteFile(testFile, []byte("content"), 0644) tool := NewEditFileTool(tmpDir, true) // Restrict to tmpDir - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": testFile, "old_text": "content", @@ -163,8 +167,9 @@ func TestEditTool_EditFile_OutsideAllowedDir(t *testing.T) { // TestEditTool_EditFile_MissingPath verifies error handling for missing path func TestEditTool_EditFile_MissingPath(t *testing.T) { + t.Parallel() tool := NewEditFileTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "old_text": "old", "new_text": "new", @@ -180,8 +185,9 @@ func TestEditTool_EditFile_MissingPath(t *testing.T) { // TestEditTool_EditFile_MissingOldText verifies error handling for missing old_text func TestEditTool_EditFile_MissingOldText(t *testing.T) { + t.Parallel() tool := NewEditFileTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": "/tmp/test.txt", "new_text": "new", @@ -197,8 +203,9 @@ func TestEditTool_EditFile_MissingOldText(t *testing.T) { // TestEditTool_EditFile_MissingNewText verifies error handling for missing new_text func TestEditTool_EditFile_MissingNewText(t *testing.T) { + t.Parallel() tool := NewEditFileTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": "/tmp/test.txt", "old_text": "old", @@ -214,12 +221,13 @@ func TestEditTool_EditFile_MissingNewText(t *testing.T) { // TestEditTool_AppendFile_Success verifies successful file appending func TestEditTool_AppendFile_Success(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "test.txt") os.WriteFile(testFile, []byte("Initial content"), 0644) tool := NewAppendFileTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": testFile, "content": "\nAppended content", @@ -258,8 +266,9 @@ func TestEditTool_AppendFile_Success(t *testing.T) { // TestEditTool_AppendFile_MissingPath verifies error handling for missing path func TestEditTool_AppendFile_MissingPath(t *testing.T) { + t.Parallel() tool := NewAppendFileTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "content": "test", } @@ -274,8 +283,9 @@ func TestEditTool_AppendFile_MissingPath(t *testing.T) { // TestEditTool_AppendFile_MissingContent verifies error handling for missing content func TestEditTool_AppendFile_MissingContent(t *testing.T) { + t.Parallel() tool := NewAppendFileTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": "/tmp/test.txt", } diff --git a/pkg/tools/filesystem_test.go b/pkg/tools/filesystem_test.go index 536284c5d..ad1ba85cc 100644 --- a/pkg/tools/filesystem_test.go +++ b/pkg/tools/filesystem_test.go @@ -1,7 +1,6 @@ package tools import ( - "context" "os" "path/filepath" "strings" @@ -10,12 +9,13 @@ import ( // TestFilesystemTool_ReadFile_Success verifies successful file reading func TestFilesystemTool_ReadFile_Success(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "test.txt") os.WriteFile(testFile, []byte("test content"), 0644) tool := &ReadFileTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": testFile, } @@ -41,8 +41,9 @@ func TestFilesystemTool_ReadFile_Success(t *testing.T) { // TestFilesystemTool_ReadFile_NotFound verifies error handling for missing file func TestFilesystemTool_ReadFile_NotFound(t *testing.T) { + t.Parallel() tool := &ReadFileTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": "/nonexistent_file_12345.txt", } @@ -62,8 +63,9 @@ func TestFilesystemTool_ReadFile_NotFound(t *testing.T) { // TestFilesystemTool_ReadFile_MissingPath verifies error handling for missing path func TestFilesystemTool_ReadFile_MissingPath(t *testing.T) { + t.Parallel() tool := &ReadFileTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{} result := tool.Execute(ctx, args) @@ -81,11 +83,12 @@ func TestFilesystemTool_ReadFile_MissingPath(t *testing.T) { // TestFilesystemTool_WriteFile_Success verifies successful file writing func TestFilesystemTool_WriteFile_Success(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "newfile.txt") tool := &WriteFileTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": testFile, "content": "hello world", @@ -120,11 +123,12 @@ func TestFilesystemTool_WriteFile_Success(t *testing.T) { // TestFilesystemTool_WriteFile_CreateDir verifies directory creation func TestFilesystemTool_WriteFile_CreateDir(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "subdir", "newfile.txt") tool := &WriteFileTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": testFile, "content": "test", @@ -149,8 +153,9 @@ func TestFilesystemTool_WriteFile_CreateDir(t *testing.T) { // TestFilesystemTool_WriteFile_MissingPath verifies error handling for missing path func TestFilesystemTool_WriteFile_MissingPath(t *testing.T) { + t.Parallel() tool := &WriteFileTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "content": "test", } @@ -165,8 +170,9 @@ func TestFilesystemTool_WriteFile_MissingPath(t *testing.T) { // TestFilesystemTool_WriteFile_MissingContent verifies error handling for missing content func TestFilesystemTool_WriteFile_MissingContent(t *testing.T) { + t.Parallel() tool := &WriteFileTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": "/tmp/test.txt", } @@ -186,13 +192,14 @@ func TestFilesystemTool_WriteFile_MissingContent(t *testing.T) { // TestFilesystemTool_ListDir_Success verifies successful directory listing func TestFilesystemTool_ListDir_Success(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() os.WriteFile(filepath.Join(tmpDir, "file1.txt"), []byte("content"), 0644) os.WriteFile(filepath.Join(tmpDir, "file2.txt"), []byte("content"), 0644) os.Mkdir(filepath.Join(tmpDir, "subdir"), 0755) tool := &ListDirTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": tmpDir, } @@ -215,8 +222,9 @@ func TestFilesystemTool_ListDir_Success(t *testing.T) { // TestFilesystemTool_ListDir_NotFound verifies error handling for non-existent directory func TestFilesystemTool_ListDir_NotFound(t *testing.T) { + t.Parallel() tool := &ListDirTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "path": "/nonexistent_directory_12345", } @@ -236,8 +244,9 @@ func TestFilesystemTool_ListDir_NotFound(t *testing.T) { // TestFilesystemTool_ListDir_DefaultPath verifies default to current directory func TestFilesystemTool_ListDir_DefaultPath(t *testing.T) { + t.Parallel() tool := &ListDirTool{} - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{} result := tool.Execute(ctx, args) @@ -250,6 +259,7 @@ func TestFilesystemTool_ListDir_DefaultPath(t *testing.T) { // Block paths that look inside workspace but point outside via symlink. func TestFilesystemTool_ReadFile_RejectsSymlinkEscape(t *testing.T) { + t.Parallel() root := t.TempDir() workspace := filepath.Join(root, "workspace") if err := os.MkdirAll(workspace, 0755); err != nil { @@ -267,7 +277,7 @@ func TestFilesystemTool_ReadFile_RejectsSymlinkEscape(t *testing.T) { } tool := NewReadFileTool(workspace, true) - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(t.Context(), map[string]interface{}{ "path": link, }) @@ -280,6 +290,7 @@ func TestFilesystemTool_ReadFile_RejectsSymlinkEscape(t *testing.T) { } func TestValidatePath_NullByteRejection(t *testing.T) { + t.Parallel() workspace := t.TempDir() tests := []struct { name string @@ -303,6 +314,7 @@ func TestValidatePath_NullByteRejection(t *testing.T) { } func TestValidatePath_SensitiveFileBlocking(t *testing.T) { + t.Parallel() workspace := t.TempDir() tests := []struct { name string @@ -345,6 +357,7 @@ func TestValidatePath_SensitiveFileBlocking(t *testing.T) { } func TestValidatePath_SensitiveFileAllowedWithoutRestrict(t *testing.T) { + t.Parallel() workspace := t.TempDir() _, err := validatePath(".env", workspace, false) if err != nil { @@ -353,10 +366,11 @@ func TestValidatePath_SensitiveFileAllowedWithoutRestrict(t *testing.T) { } func TestWriteFileTool_RejectsOversizedContent(t *testing.T) { + t.Parallel() workspace := t.TempDir() tool := NewWriteFileTool(workspace, false) oversized := strings.Repeat("x", maxWriteBytes+1) - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(t.Context(), map[string]interface{}{ "path": filepath.Join(workspace, "big.txt"), "content": oversized, }) @@ -369,6 +383,7 @@ func TestWriteFileTool_RejectsOversizedContent(t *testing.T) { } func TestValidatePath_TraversalBlocked(t *testing.T) { + t.Parallel() workspace := t.TempDir() tests := []struct { name string @@ -389,6 +404,7 @@ func TestValidatePath_TraversalBlocked(t *testing.T) { } func TestValidatePath_ValidPathsAllowed(t *testing.T) { + t.Parallel() workspace := t.TempDir() os.MkdirAll(filepath.Join(workspace, "src", "pkg"), 0755) diff --git a/pkg/tools/focus_test.go b/pkg/tools/focus_test.go index 7ed83cfec..55a9c5fc8 100644 --- a/pkg/tools/focus_test.go +++ b/pkg/tools/focus_test.go @@ -53,6 +53,7 @@ func (m *mockFocusDelegate) DeleteKV(ctx context.Context, agentID, key string) e } func TestStartFocus(t *testing.T) { + t.Parallel() sm := session.NewSessionManager("") sk := "test-session" sm.GetOrCreate(sk) @@ -63,7 +64,7 @@ func TestStartFocus(t *testing.T) { delegate := newMockFocusDelegate() tool := NewStartFocusTool(delegate, sm, func() string { return sk }) - ctx := context.Background() + ctx := t.Context() result := tool.Execute(ctx, map[string]interface{}{ "topic": "investigate auth bug", }) @@ -82,15 +83,17 @@ func TestStartFocus(t *testing.T) { } func TestStartFocus_MissingTopic(t *testing.T) { + t.Parallel() sm := session.NewSessionManager("") delegate := newMockFocusDelegate() tool := NewStartFocusTool(delegate, sm, func() string { return "s" }) - result := tool.Execute(context.Background(), map[string]interface{}{}) + result := tool.Execute(t.Context(), map[string]interface{}{}) assert.Contains(t, result.ForLLM, "topic is required") } func TestCompleteFocus(t *testing.T) { + t.Parallel() sm := session.NewSessionManager("") sk := "test-session" sm.GetOrCreate(sk) @@ -101,7 +104,7 @@ func TestCompleteFocus(t *testing.T) { delegate := newMockFocusDelegate() startTool := NewStartFocusTool(delegate, sm, func() string { return sk }) - ctx := context.Background() + ctx := t.Context() startTool.Execute(ctx, map[string]interface{}{"topic": "debug auth"}) sm.AddMessage(sk, "user", "check logs") @@ -146,6 +149,7 @@ func TestCompleteFocus(t *testing.T) { } func TestCompleteFocus_NoActiveFocus(t *testing.T) { + t.Parallel() sm := session.NewSessionManager("") sk := "test-session" sm.GetOrCreate(sk) @@ -153,18 +157,19 @@ func TestCompleteFocus_NoActiveFocus(t *testing.T) { delegate := newMockFocusDelegate() tool := NewCompleteFocusTool(delegate, sm, func() string { return sk }) - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(t.Context(), map[string]interface{}{ "summary": "some summary", }) assert.Contains(t, result.ForLLM, "no active focus session") } func TestCompleteFocus_MultipleKnowledgeEntries(t *testing.T) { + t.Parallel() sm := session.NewSessionManager("") sk := "test-session" sm.GetOrCreate(sk) delegate := newMockFocusDelegate() - ctx := context.Background() + ctx := t.Context() startTool := NewStartFocusTool(delegate, sm, func() string { return sk }) completeTool := NewCompleteFocusTool(delegate, sm, func() string { return sk }) @@ -192,6 +197,7 @@ func TestCompleteFocus_MultipleKnowledgeEntries(t *testing.T) { } func TestPruneHistory(t *testing.T) { + t.Parallel() tool := &CompleteFocusTool{} tests := []struct { @@ -256,6 +262,7 @@ func TestPruneHistory(t *testing.T) { } func TestKnowledgeBlockFormat(t *testing.T) { + t.Parallel() kb := &KnowledgeBlock{ Entries: []KnowledgeEntry{ {Topic: "Auth Flow", Summary: "Token validation needs expiry check."}, @@ -271,13 +278,15 @@ func TestKnowledgeBlockFormat(t *testing.T) { } func TestKnowledgeBlockFormat_Empty(t *testing.T) { + t.Parallel() kb := &KnowledgeBlock{} assert.Empty(t, kb.FormatBlock()) } func TestLoadKnowledgeBlock(t *testing.T) { + t.Parallel() delegate := newMockFocusDelegate() - ctx := context.Background() + ctx := t.Context() block := LoadKnowledgeBlock(ctx, delegate, "nonexistent") assert.Empty(t, block) diff --git a/pkg/tools/llm_map_test.go b/pkg/tools/llm_map_test.go index 7acbbf260..6759b5dfe 100644 --- a/pkg/tools/llm_map_test.go +++ b/pkg/tools/llm_map_test.go @@ -54,6 +54,7 @@ func (m *llmMapMockModel) Provider() string { return "mock" } func (m *llmMapMockModel) Model() string { return "mock-llm-map" } func TestLLMMapTool_Execute_Success(t *testing.T) { + t.Parallel() model := &llmMapMockModel{ responses: []string{ `{"label":"alpha","priority":1}`, @@ -62,7 +63,7 @@ func TestLLMMapTool_Execute_Success(t *testing.T) { } tool := NewLLMMapTool(model, "mock-llm-map") - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(t.Context(), map[string]interface{}{ "instruction": "Convert item to {label, priority}", "items": []interface{}{ map[string]interface{}{"name": "A"}, @@ -90,12 +91,13 @@ func TestLLMMapTool_Execute_Success(t *testing.T) { } func TestLLMMapTool_Execute_SchemaValidationFailure(t *testing.T) { + t.Parallel() model := &llmMapMockModel{ responses: []string{`{"only":"value"}`}, } tool := NewLLMMapTool(model, "mock-llm-map") - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(t.Context(), map[string]interface{}{ "instruction": "Return normalized object", "items": []interface{}{map[string]interface{}{"name": "x"}}, "output_schema": map[string]interface{}{ diff --git a/pkg/tools/map_flatbuffer_codec_test.go b/pkg/tools/map_flatbuffer_codec_test.go index da334d369..99438f0e9 100644 --- a/pkg/tools/map_flatbuffer_codec_test.go +++ b/pkg/tools/map_flatbuffer_codec_test.go @@ -5,6 +5,7 @@ import ( ) func TestMapRunSpecFlatBuffer_RoundTrip(t *testing.T) { + t.Parallel() original := MapRunSpec{ Version: 1, OperatorKind: MapOperatorLLM, @@ -42,6 +43,7 @@ func TestMapRunSpecFlatBuffer_RoundTrip(t *testing.T) { } func TestMapItemFlatBuffer_RoundTrip(t *testing.T) { + t.Parallel() input := MapItemInputRecord{ Version: 1, ItemIndex: 7, diff --git a/pkg/tools/map_runtime_integration_test.go b/pkg/tools/map_runtime_integration_test.go index 48818cd4e..38d9288a2 100644 --- a/pkg/tools/map_runtime_integration_test.go +++ b/pkg/tools/map_runtime_integration_test.go @@ -74,7 +74,7 @@ func newMapRuntimeForTest(t *testing.T, llm fantasy.LanguageModel, manager *Suba t.Helper() d, err := delegate.NewLibSQLInMemory() require.NoError(t, err) - require.NoError(t, d.Init(context.Background())) + require.NoError(t, d.Init(t.Context())) t.Cleanup(func() { _ = d.Close() }) return NewMapRuntime(d.Queries(), pkg.NAME, llm, "mock-map-llm", manager) @@ -90,6 +90,7 @@ func decodeResultMap(t *testing.T, result *ToolResult) map[string]interface{} { } func TestLLMMap_WorkerJSONL_StatusAndRead(t *testing.T) { + t.Parallel() model := &scriptedMapLLM{ responses: []scriptedMapLLMResponse{ {Text: `{"label":"alpha","priority":1}`}, @@ -102,7 +103,7 @@ func TestLLMMap_WorkerJSONL_StatusAndRead(t *testing.T) { statusTool := NewMapRunStatusTool(runtime) readTool := NewMapRunReadTool(runtime) - enqueue := decodeResultMap(t, mapTool.Execute(context.Background(), map[string]interface{}{ + enqueue := decodeResultMap(t, mapTool.Execute(t.Context(), map[string]interface{}{ "instruction": "normalize to {label, priority}", "input_jsonl": "{\"name\":\"a\"}\n{\"name\":\"b\"}", "execution_mode": "worker", @@ -111,14 +112,14 @@ func TestLLMMap_WorkerJSONL_StatusAndRead(t *testing.T) { require.NotEmpty(t, runID) assert.Equal(t, "worker", enqueue["execution_mode"]) - status := decodeResultMap(t, statusTool.Execute(context.Background(), map[string]interface{}{ + status := decodeResultMap(t, statusTool.Execute(t.Context(), map[string]interface{}{ "run_id": runID, "process_steps": float64(20), })) assert.Equal(t, mapRunStatusSucceeded, status["status"]) assert.EqualValues(t, 2, status["succeeded_items"]) - readJSON := decodeResultMap(t, readTool.Execute(context.Background(), map[string]interface{}{ + readJSON := decodeResultMap(t, readTool.Execute(t.Context(), map[string]interface{}{ "run_id": runID, "limit": float64(10), "format": "json", @@ -127,7 +128,7 @@ func TestLLMMap_WorkerJSONL_StatusAndRead(t *testing.T) { require.True(t, ok) require.Len(t, itemsAny, 2) - readJSONL := decodeResultMap(t, readTool.Execute(context.Background(), map[string]interface{}{ + readJSONL := decodeResultMap(t, readTool.Execute(t.Context(), map[string]interface{}{ "run_id": runID, "format": "jsonl", })) @@ -137,6 +138,7 @@ func TestLLMMap_WorkerJSONL_StatusAndRead(t *testing.T) { } func TestLLMMap_IdempotencyReuse(t *testing.T) { + t.Parallel() model := &scriptedMapLLM{ responses: []scriptedMapLLMResponse{ {Text: `{"label":"alpha"}`}, @@ -153,14 +155,15 @@ func TestLLMMap_IdempotencyReuse(t *testing.T) { "idempotency_key": "run-1", "session_key": "sess-a", } - first := decodeResultMap(t, mapTool.Execute(context.Background(), args)) - second := decodeResultMap(t, mapTool.Execute(context.Background(), args)) + first := decodeResultMap(t, mapTool.Execute(t.Context(), args)) + second := decodeResultMap(t, mapTool.Execute(t.Context(), args)) assert.Equal(t, first["run_id"], second["run_id"]) assert.Equal(t, true, second["idempotent_reuse"]) } func TestLLMMap_IdempotencyReuse_Concurrent(t *testing.T) { + t.Parallel() model := &scriptedMapLLM{ responses: []scriptedMapLLMResponse{ {Text: `{"label":"alpha"}`}, @@ -181,7 +184,7 @@ func TestLLMMap_IdempotencyReuse_Concurrent(t *testing.T) { go func() { defer wg.Done() <-start - result := mapTool.Execute(context.Background(), map[string]interface{}{ + result := mapTool.Execute(t.Context(), map[string]interface{}{ "instruction": "normalize", "items": []interface{}{map[string]interface{}{"name": "a"}}, "execution_mode": "worker", @@ -227,6 +230,7 @@ func TestLLMMap_IdempotencyReuse_Concurrent(t *testing.T) { } func TestLLMMap_InlineRetriesThenSucceeds(t *testing.T) { + t.Parallel() model := &scriptedMapLLM{ responses: []scriptedMapLLMResponse{ {Err: fmt.Errorf("transient model outage")}, @@ -238,7 +242,7 @@ func TestLLMMap_InlineRetriesThenSucceeds(t *testing.T) { mapTool.SetRuntime(runtime) readTool := NewMapRunReadTool(runtime) - result := decodeResultMap(t, mapTool.Execute(context.Background(), map[string]interface{}{ + result := decodeResultMap(t, mapTool.Execute(t.Context(), map[string]interface{}{ "instruction": "normalize", "items": []interface{}{map[string]interface{}{"name": "retry"}}, "execution_mode": "inline", @@ -248,7 +252,7 @@ func TestLLMMap_InlineRetriesThenSucceeds(t *testing.T) { runID, _ := result["run_id"].(string) require.NotEmpty(t, runID) - read := decodeResultMap(t, readTool.Execute(context.Background(), map[string]interface{}{ + read := decodeResultMap(t, readTool.Execute(t.Context(), map[string]interface{}{ "run_id": runID, "format": "json", })) @@ -261,6 +265,7 @@ func TestLLMMap_InlineRetriesThenSucceeds(t *testing.T) { } func TestLLMMap_InlineExhaustedRetriesFailsRun(t *testing.T) { + t.Parallel() model := &scriptedMapLLM{ responses: []scriptedMapLLMResponse{ {Text: `not-json`}, @@ -271,7 +276,7 @@ func TestLLMMap_InlineExhaustedRetriesFailsRun(t *testing.T) { mapTool := NewLLMMapTool(model, "mock-map-llm") mapTool.SetRuntime(runtime) - payload := decodeResultMap(t, mapTool.Execute(context.Background(), map[string]interface{}{ + payload := decodeResultMap(t, mapTool.Execute(t.Context(), map[string]interface{}{ "instruction": "normalize", "items": []interface{}{map[string]interface{}{"name": "bad"}}, "execution_mode": "inline", @@ -284,6 +289,7 @@ func TestLLMMap_InlineExhaustedRetriesFailsRun(t *testing.T) { } func TestMapRunRead_DetectsOutputHashMismatch(t *testing.T) { + t.Parallel() model := &scriptedMapLLM{ responses: []scriptedMapLLMResponse{ {Text: `{"label":"alpha"}`}, @@ -294,7 +300,7 @@ func TestMapRunRead_DetectsOutputHashMismatch(t *testing.T) { mapTool.SetRuntime(runtime) readTool := NewMapRunReadTool(runtime) - result := decodeResultMap(t, mapTool.Execute(context.Background(), map[string]interface{}{ + result := decodeResultMap(t, mapTool.Execute(t.Context(), map[string]interface{}{ "instruction": "normalize", "items": []interface{}{map[string]interface{}{"name": "a"}}, "execution_mode": "inline", @@ -304,7 +310,7 @@ func TestMapRunRead_DetectsOutputHashMismatch(t *testing.T) { runID, err := ids.Parse(runIDText) require.NoError(t, err) - rows, err := runtime.queries.ListMapItemsByRunPaged(context.Background(), memsqlc.ListMapItemsByRunPagedParams{ + rows, err := runtime.queries.ListMapItemsByRunPaged(t.Context(), memsqlc.ListMapItemsByRunPagedParams{ RunID: runID, Lim: 10, Off: 0, @@ -312,14 +318,14 @@ func TestMapRunRead_DetectsOutputHashMismatch(t *testing.T) { require.NoError(t, err) require.Len(t, rows, 1) - _, err = runtime.queries.MarkMapItemSucceeded(context.Background(), memsqlc.MarkMapItemSucceededParams{ + _, err = runtime.queries.MarkMapItemSucceeded(t.Context(), memsqlc.MarkMapItemSucceededParams{ OutputFb: rows[0].OutputFb, OutputHash: optionalStringPtr("forced-mismatch"), ID: rows[0].ID, }) require.NoError(t, err) - readResult := readTool.Execute(context.Background(), map[string]interface{}{ + readResult := readTool.Execute(t.Context(), map[string]interface{}{ "run_id": runIDText, "format": "json", }) @@ -329,6 +335,7 @@ func TestMapRunRead_DetectsOutputHashMismatch(t *testing.T) { } func TestAgenticMap_WorkerLifecycle(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) manager.SetRunLoop(func(_ context.Context, _ ToolLoopConfig, _, userPrompt, _, _ string) (*ToolLoopResult, error) { @@ -341,7 +348,7 @@ func TestAgenticMap_WorkerLifecycle(t *testing.T) { statusTool := NewMapRunStatusTool(runtime) readTool := NewMapRunReadTool(runtime) - enqueue := decodeResultMap(t, tool.Execute(context.Background(), map[string]interface{}{ + enqueue := decodeResultMap(t, tool.Execute(t.Context(), map[string]interface{}{ "items": []interface{}{map[string]interface{}{"name": "x"}}, "task_template": "Handle {{index}} => {{item_json}}", "execution_mode": "worker", @@ -349,13 +356,13 @@ func TestAgenticMap_WorkerLifecycle(t *testing.T) { runID, _ := enqueue["run_id"].(string) require.NotEmpty(t, runID) - status := decodeResultMap(t, statusTool.Execute(context.Background(), map[string]interface{}{ + status := decodeResultMap(t, statusTool.Execute(t.Context(), map[string]interface{}{ "run_id": runID, "process_steps": float64(20), })) assert.Equal(t, mapRunStatusSucceeded, status["status"]) - read := decodeResultMap(t, readTool.Execute(context.Background(), map[string]interface{}{ + read := decodeResultMap(t, readTool.Execute(t.Context(), map[string]interface{}{ "run_id": runID, "format": "json", })) @@ -367,6 +374,7 @@ func TestAgenticMap_WorkerLifecycle(t *testing.T) { } func TestLLMMap_InvalidJSONLIngestFails(t *testing.T) { + t.Parallel() model := &scriptedMapLLM{ responses: []scriptedMapLLMResponse{{Text: `{"ok":true}`}}, } @@ -374,7 +382,7 @@ func TestLLMMap_InvalidJSONLIngestFails(t *testing.T) { mapTool := NewLLMMapTool(model, "mock-map-llm") mapTool.SetRuntime(runtime) - result := mapTool.Execute(context.Background(), map[string]interface{}{ + result := mapTool.Execute(t.Context(), map[string]interface{}{ "instruction": "normalize", "input_jsonl": "{\"name\":\"ok\"}\nnot-json", }) diff --git a/pkg/tools/message_test.go b/pkg/tools/message_test.go index 4bedbe79b..87de7ae78 100644 --- a/pkg/tools/message_test.go +++ b/pkg/tools/message_test.go @@ -1,12 +1,12 @@ package tools import ( - "context" "errors" "testing" ) func TestMessageTool_Execute_Success(t *testing.T) { + t.Parallel() tool := NewMessageTool() tool.SetContext("test-channel", "test-chat-id") @@ -18,7 +18,7 @@ func TestMessageTool_Execute_Success(t *testing.T) { return nil }) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "content": "Hello, world!", } @@ -59,6 +59,7 @@ func TestMessageTool_Execute_Success(t *testing.T) { } func TestMessageTool_Execute_WithCustomChannel(t *testing.T) { + t.Parallel() tool := NewMessageTool() tool.SetContext("default-channel", "default-chat-id") @@ -69,7 +70,7 @@ func TestMessageTool_Execute_WithCustomChannel(t *testing.T) { return nil }) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "content": "Test message", "channel": "custom-channel", @@ -95,6 +96,7 @@ func TestMessageTool_Execute_WithCustomChannel(t *testing.T) { } func TestMessageTool_Execute_SendFailure(t *testing.T) { + t.Parallel() tool := NewMessageTool() tool.SetContext("test-channel", "test-chat-id") @@ -103,7 +105,7 @@ func TestMessageTool_Execute_SendFailure(t *testing.T) { return sendErr }) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "content": "Test message", } @@ -132,10 +134,11 @@ func TestMessageTool_Execute_SendFailure(t *testing.T) { } func TestMessageTool_Execute_MissingContent(t *testing.T) { + t.Parallel() tool := NewMessageTool() tool.SetContext("test-channel", "test-chat-id") - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{} // content missing result := tool.Execute(ctx, args) @@ -150,6 +153,7 @@ func TestMessageTool_Execute_MissingContent(t *testing.T) { } func TestMessageTool_Execute_NoTargetChannel(t *testing.T) { + t.Parallel() tool := NewMessageTool() // No SetContext called, so defaultChannel and defaultChatID are empty @@ -157,7 +161,7 @@ func TestMessageTool_Execute_NoTargetChannel(t *testing.T) { return nil }) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "content": "Test message", } @@ -174,11 +178,12 @@ func TestMessageTool_Execute_NoTargetChannel(t *testing.T) { } func TestMessageTool_Execute_NotConfigured(t *testing.T) { + t.Parallel() tool := NewMessageTool() tool.SetContext("test-channel", "test-chat-id") // No SetSendCallback called - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "content": "Test message", } @@ -195,6 +200,7 @@ func TestMessageTool_Execute_NotConfigured(t *testing.T) { } func TestMessageTool_Name(t *testing.T) { + t.Parallel() tool := NewMessageTool() if tool.Name() != "message" { t.Errorf("Expected name 'message', got '%s'", tool.Name()) @@ -202,6 +208,7 @@ func TestMessageTool_Name(t *testing.T) { } func TestMessageTool_Description(t *testing.T) { + t.Parallel() tool := NewMessageTool() desc := tool.Description() if desc == "" { @@ -210,6 +217,7 @@ func TestMessageTool_Description(t *testing.T) { } func TestMessageTool_Parameters(t *testing.T) { + t.Parallel() tool := NewMessageTool() params := tool.Parameters() diff --git a/pkg/tools/obligation_test.go b/pkg/tools/obligation_test.go index 4a1655a4a..a31c673e6 100644 --- a/pkg/tools/obligation_test.go +++ b/pkg/tools/obligation_test.go @@ -1,7 +1,6 @@ package tools import ( - "context" "testing" "time" @@ -13,7 +12,8 @@ import ( ) func TestObligationTool_CreateAndList(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) require.NoError(t, del.Init(ctx)) @@ -46,7 +46,8 @@ func TestObligationTool_CreateAndList(t *testing.T) { } func TestObligationTool_StateMachineAndEvidence(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) require.NoError(t, del.Init(ctx)) @@ -115,7 +116,8 @@ func TestObligationTool_StateMachineAndEvidence(t *testing.T) { } func TestObligationTool_CollectDueObligations_TransitionsScheduledToDue(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) require.NoError(t, del.Init(ctx)) @@ -154,7 +156,8 @@ func TestObligationTool_CollectDueObligations_TransitionsScheduledToDue(t *testi } func TestObligationTool_CollectDueObligations_DoesNotDuplicateDueEvidence(t *testing.T) { - ctx := context.Background() + t.Parallel() + ctx := t.Context() del, err := delegate.NewLibSQLInMemory() require.NoError(t, err) require.NoError(t, del.Init(ctx)) diff --git a/pkg/tools/registry_progressive_test.go b/pkg/tools/registry_progressive_test.go index 5ad28b82e..db771dfce 100644 --- a/pkg/tools/registry_progressive_test.go +++ b/pkg/tools/registry_progressive_test.go @@ -7,6 +7,7 @@ import ( ) func TestProgressiveDisclosure_OnlyGatewayVisible(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read"}) r.Register(&stubTool{name: "write_file", desc: "Write"}) @@ -28,6 +29,7 @@ func TestProgressiveDisclosure_OnlyGatewayVisible(t *testing.T) { } func TestProgressiveDisclosure_MarkGateway(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read"}) r.Register(&stubTool{name: "memory", desc: "Memory tool"}) @@ -49,6 +51,7 @@ func TestProgressiveDisclosure_MarkGateway(t *testing.T) { } func TestProgressiveDisclosure_GetVisibleDefinitions(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read"}) r.Register(&stubTool{name: "write_file", desc: "Write"}) @@ -61,6 +64,7 @@ func TestProgressiveDisclosure_GetVisibleDefinitions(t *testing.T) { } func TestProgressiveDisclosure_AllToolsStillDispatchable(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read"}) r.RegisterMetaTools() @@ -84,6 +88,7 @@ func TestProgressiveDisclosure_AllToolsStillDispatchable(t *testing.T) { } func TestProgressiveDisclosure_SearchFindsHiddenTools(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read a file from filesystem"}) r.Register(&stubTool{name: "write_file", desc: "Write to a file"}) @@ -102,6 +107,7 @@ func TestProgressiveDisclosure_SearchFindsHiddenTools(t *testing.T) { } func TestProgressiveDisclosure_MarkNonexistentGateway(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.RegisterMetaTools() r.MarkGateway("nonexistent") diff --git a/pkg/tools/result_test.go b/pkg/tools/result_test.go index a20b3463e..3d4931dfb 100644 --- a/pkg/tools/result_test.go +++ b/pkg/tools/result_test.go @@ -5,98 +5,54 @@ import ( "testing" jsonv2 "github.com/go-json-experiment/json" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/assert" ) -func TestNewToolResult(t *testing.T) { - result := NewToolResult("test content") +func TestToolResultConstructors(t *testing.T) { + t.Parallel() + cases := []struct { + name string + got *ToolResult + want ToolResult + }{ + { + name: "new result", + got: NewToolResult("basic content"), + want: ToolResult{ForLLM: "basic content"}, + }, + { + name: "silent result", + got: SilentResult("silent content"), + want: ToolResult{ForLLM: "silent content", Silent: true}, + }, + { + name: "async result", + got: AsyncResult("async content"), + want: ToolResult{ForLLM: "async content", Async: true}, + }, + { + name: "error result", + got: ErrorResult("error content"), + want: ToolResult{ForLLM: "error content", IsError: true}, + }, + { + name: "user result", + got: UserResult("user visible content"), + want: ToolResult{ForLLM: "user visible content", ForUser: "user visible content"}, + }, + } - if result.ForLLM != "test content" { - t.Errorf("Expected ForLLM 'test content', got '%s'", result.ForLLM) - } - if result.Silent { - t.Error("Expected Silent to be false") - } - if result.IsError { - t.Error("Expected IsError to be false") - } - if result.Async { - t.Error("Expected Async to be false") - } -} - -func TestSilentResult(t *testing.T) { - result := SilentResult("silent operation") - - if result.ForLLM != "silent operation" { - t.Errorf("Expected ForLLM 'silent operation', got '%s'", result.ForLLM) - } - if !result.Silent { - t.Error("Expected Silent to be true") - } - if result.IsError { - t.Error("Expected IsError to be false") - } - if result.Async { - t.Error("Expected Async to be false") - } -} - -func TestAsyncResult(t *testing.T) { - result := AsyncResult("async task started") - - if result.ForLLM != "async task started" { - t.Errorf("Expected ForLLM 'async task started', got '%s'", result.ForLLM) - } - if result.Silent { - t.Error("Expected Silent to be false") - } - if result.IsError { - t.Error("Expected IsError to be false") - } - if !result.Async { - t.Error("Expected Async to be true") - } -} - -func TestErrorResult(t *testing.T) { - result := ErrorResult("operation failed") - - if result.ForLLM != "operation failed" { - t.Errorf("Expected ForLLM 'operation failed', got '%s'", result.ForLLM) - } - if result.Silent { - t.Error("Expected Silent to be false") - } - if !result.IsError { - t.Error("Expected IsError to be true") - } - if result.Async { - t.Error("Expected Async to be false") - } -} - -func TestUserResult(t *testing.T) { - content := "user visible message" - result := UserResult(content) - - if result.ForLLM != content { - t.Errorf("Expected ForLLM '%s', got '%s'", content, result.ForLLM) - } - if result.ForUser != content { - t.Errorf("Expected ForUser '%s', got '%s'", content, result.ForUser) - } - if result.Silent { - t.Error("Expected Silent to be false") - } - if result.IsError { - t.Error("Expected IsError to be false") - } - if result.Async { - t.Error("Expected Async to be false") + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + assert.Empty(t, cmp.Diff(tt.want, *tt.got, cmpopts.IgnoreFields(ToolResult{}, "Err"))) + }) } } func TestToolResultJSONSerialization(t *testing.T) { + t.Parallel() tests := []struct { name string result *ToolResult @@ -138,26 +94,15 @@ func TestToolResultJSONSerialization(t *testing.T) { } // Verify fields match (Err should be excluded) - if decoded.ForLLM != tt.result.ForLLM { - t.Errorf("ForLLM mismatch: got '%s', want '%s'", decoded.ForLLM, tt.result.ForLLM) - } - if decoded.ForUser != tt.result.ForUser { - t.Errorf("ForUser mismatch: got '%s', want '%s'", decoded.ForUser, tt.result.ForUser) - } - if decoded.Silent != tt.result.Silent { - t.Errorf("Silent mismatch: got %v, want %v", decoded.Silent, tt.result.Silent) - } - if decoded.IsError != tt.result.IsError { - t.Errorf("IsError mismatch: got %v, want %v", decoded.IsError, tt.result.IsError) - } - if decoded.Async != tt.result.Async { - t.Errorf("Async mismatch: got %v, want %v", decoded.Async, tt.result.Async) + if diff := cmp.Diff(*tt.result, decoded, cmpopts.IgnoreFields(ToolResult{}, "Err")); diff != "" { + t.Errorf("ToolResult mismatch (-want +got):\n%s", diff) } }) } } func TestToolResultWithErrors(t *testing.T) { + t.Parallel() err := errors.New("underlying error") result := ErrorResult("error message").WithError(err) @@ -185,6 +130,7 @@ func TestToolResultWithErrors(t *testing.T) { } func TestToolResultJSONStructure(t *testing.T) { + t.Parallel() result := UserResult("test content") data, err := jsonv2.Marshal(result) diff --git a/pkg/tools/retrieval_test.go b/pkg/tools/retrieval_test.go index 6fe7e9078..615b01212 100644 --- a/pkg/tools/retrieval_test.go +++ b/pkg/tools/retrieval_test.go @@ -1,7 +1,6 @@ package tools import ( - "context" "testing" "github.com/stretchr/testify/assert" @@ -9,6 +8,7 @@ import ( ) func TestKeywordSearchTool_Metadata(t *testing.T) { + t.Parallel() tool := &KeywordSearchTool{agentID: "test"} assert.Equal(t, "keyword_search", tool.Name()) assert.Contains(t, tool.Description(), "FTS5") @@ -21,12 +21,14 @@ func TestKeywordSearchTool_Metadata(t *testing.T) { } func TestKeywordSearchTool_MissingQuery(t *testing.T) { + t.Parallel() tool := &KeywordSearchTool{agentID: "test"} - result := tool.Execute(context.Background(), map[string]interface{}{}) + result := tool.Execute(t.Context(), map[string]interface{}{}) assert.Contains(t, result.ForLLM, "query is required") } func TestSemanticSearchTool_Metadata(t *testing.T) { + t.Parallel() tool := &SemanticSearchTool{agentID: "test"} assert.Equal(t, "semantic_search", tool.Name()) assert.Contains(t, tool.Description(), "semantic similarity") @@ -35,12 +37,14 @@ func TestSemanticSearchTool_Metadata(t *testing.T) { } func TestSemanticSearchTool_MissingQuery(t *testing.T) { + t.Parallel() tool := &SemanticSearchTool{agentID: "test"} - result := tool.Execute(context.Background(), map[string]interface{}{}) + result := tool.Execute(t.Context(), map[string]interface{}{}) assert.Contains(t, result.ForLLM, "query is required") } func TestChunkReadTool_Metadata(t *testing.T) { + t.Parallel() tool := &ChunkReadTool{agentID: "test"} assert.Equal(t, "chunk_read", tool.Name()) assert.Contains(t, tool.Description(), "full content") @@ -49,17 +53,20 @@ func TestChunkReadTool_Metadata(t *testing.T) { } func TestChunkReadTool_MissingID(t *testing.T) { + t.Parallel() tool := &ChunkReadTool{agentID: "test"} - result := tool.Execute(context.Background(), map[string]interface{}{}) + result := tool.Execute(t.Context(), map[string]interface{}{}) assert.Contains(t, result.ForLLM, "id is required") } func TestFormatSearchResults_Empty(t *testing.T) { + t.Parallel() output := formatSearchResults("keyword", "query", nil) assert.Contains(t, output, "No keyword results found") } func TestFormatSearchResults_WithResults(t *testing.T) { + t.Parallel() results := []struct { id string content string diff --git a/pkg/tools/search_test.go b/pkg/tools/search_test.go index 50b8f7eab..d1850a388 100644 --- a/pkg/tools/search_test.go +++ b/pkg/tools/search_test.go @@ -10,6 +10,7 @@ import ( ) func TestToolSearchTool_Name(t *testing.T) { + t.Parallel() r := NewToolRegistry() s := NewToolSearchTool(r) if s.Name() != "tool_search" { @@ -18,6 +19,7 @@ func TestToolSearchTool_Name(t *testing.T) { } func TestToolSearchTool_Description(t *testing.T) { + t.Parallel() r := NewToolRegistry() s := NewToolSearchTool(r) if s.Description() == "" { @@ -26,6 +28,7 @@ func TestToolSearchTool_Description(t *testing.T) { } func TestToolSearchTool_Parameters(t *testing.T) { + t.Parallel() r := NewToolRegistry() s := NewToolSearchTool(r) params := s.Parameters() @@ -42,13 +45,14 @@ func TestToolSearchTool_Parameters(t *testing.T) { } func TestToolSearchTool_ListAll(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read a file from disk"}) r.Register(&stubTool{name: "write_file", desc: "Write content to a file"}) r.Register(&stubTool{name: "web_search", desc: "Search the internet"}) s := NewToolSearchTool(r) - result := s.Execute(context.Background(), map[string]interface{}{}) + result := s.Execute(t.Context(), map[string]interface{}{}) if result.IsError { t.Fatalf("unexpected error: %s", result.ForLLM) @@ -65,12 +69,13 @@ func TestToolSearchTool_ListAll(t *testing.T) { } func TestToolSearchTool_EmptyQuery_ListsAll(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "alpha", desc: "First tool"}) r.Register(&stubTool{name: "beta", desc: "Second tool"}) s := NewToolSearchTool(r) - result := s.Execute(context.Background(), map[string]interface{}{"query": ""}) + result := s.Execute(t.Context(), map[string]interface{}{"query": ""}) var results []toolSearchResult if err := jsonv2.Unmarshal([]byte(result.ForLLM), &results); err != nil { @@ -88,13 +93,14 @@ func TestToolSearchTool_EmptyQuery_ListsAll(t *testing.T) { } func TestToolSearchTool_ExactNameMatch(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read a file"}) r.Register(&stubTool{name: "write_file", desc: "Write a file"}) r.Register(&stubTool{name: "list_dir", desc: "List directory contents"}) s := NewToolSearchTool(r) - result := s.Execute(context.Background(), map[string]interface{}{"query": "read_file"}) + result := s.Execute(t.Context(), map[string]interface{}{"query": "read_file"}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) @@ -110,13 +116,14 @@ func TestToolSearchTool_ExactNameMatch(t *testing.T) { } func TestToolSearchTool_PartialMatch(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read contents of a file from filesystem"}) r.Register(&stubTool{name: "write_file", desc: "Write content to a file on filesystem"}) r.Register(&stubTool{name: "web_search", desc: "Search the web for information"}) s := NewToolSearchTool(r) - result := s.Execute(context.Background(), map[string]interface{}{"query": "file"}) + result := s.Execute(t.Context(), map[string]interface{}{"query": "file"}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) @@ -128,12 +135,13 @@ func TestToolSearchTool_PartialMatch(t *testing.T) { } func TestToolSearchTool_DescriptionMatch(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "alpha", desc: "Search the internet for information"}) r.Register(&stubTool{name: "beta", desc: "Read a local file"}) s := NewToolSearchTool(r) - result := s.Execute(context.Background(), map[string]interface{}{"query": "internet"}) + result := s.Execute(t.Context(), map[string]interface{}{"query": "internet"}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) @@ -147,13 +155,14 @@ func TestToolSearchTool_DescriptionMatch(t *testing.T) { } func TestToolSearchTool_MultiTermQuery(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read contents of a file from disk"}) r.Register(&stubTool{name: "web_search", desc: "Search the web for information"}) r.Register(&stubTool{name: "web_fetch", desc: "Fetch content from a URL"}) s := NewToolSearchTool(r) - result := s.Execute(context.Background(), map[string]interface{}{"query": "web search"}) + result := s.Execute(t.Context(), map[string]interface{}{"query": "web search"}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) @@ -169,11 +178,12 @@ func TestToolSearchTool_MultiTermQuery(t *testing.T) { } func TestToolSearchTool_NoMatch(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read a file"}) s := NewToolSearchTool(r) - result := s.Execute(context.Background(), map[string]interface{}{"query": "zzzznonexistent"}) + result := s.Execute(t.Context(), map[string]interface{}{"query": "zzzznonexistent"}) if result.IsError { t.Error("should not be an error, just empty results message") @@ -184,12 +194,13 @@ func TestToolSearchTool_NoMatch(t *testing.T) { } func TestToolSearchTool_ExcludesMetaTools(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.RegisterMetaTools() // registers tool_search + tool_call r.Register(&stubTool{name: "read_file", desc: "Read a file"}) s, _ := r.Get("tool_search") - result := s.Execute(context.Background(), map[string]interface{}{}) + result := s.Execute(t.Context(), map[string]interface{}{}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) @@ -202,6 +213,7 @@ func TestToolSearchTool_ExcludesMetaTools(t *testing.T) { } func TestToolSearchTool_UnifiedSearch_IncludesSkills(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "read_file", desc: "Read a file"}) r.Register(&stubTool{name: "web_search", desc: "Search the web"}) @@ -216,7 +228,7 @@ func TestToolSearchTool_UnifiedSearch_IncludesSkills(t *testing.T) { s.SetSkillsLoader(sl) // Search for "git" — should find the skill but not the tools - result := s.Execute(context.Background(), map[string]interface{}{"query": "git"}) + result := s.Execute(t.Context(), map[string]interface{}{"query": "git"}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) @@ -233,6 +245,7 @@ func TestToolSearchTool_UnifiedSearch_IncludesSkills(t *testing.T) { } func TestToolSearchTool_UnifiedSearch_MixesToolsAndSkills(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "web_search", desc: "Search the web for information"}) @@ -244,7 +257,7 @@ func TestToolSearchTool_UnifiedSearch_MixesToolsAndSkills(t *testing.T) { s := NewToolSearchTool(r) s.SetSkillsLoader(sl) - result := s.Execute(context.Background(), map[string]interface{}{"query": "web"}) + result := s.Execute(t.Context(), map[string]interface{}{"query": "web"}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) @@ -265,6 +278,7 @@ func TestToolSearchTool_UnifiedSearch_MixesToolsAndSkills(t *testing.T) { } func TestToolSearchTool_ListAll_IncludesSkills(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubTool{name: "alpha", desc: "First tool"}) @@ -276,7 +290,7 @@ func TestToolSearchTool_ListAll_IncludesSkills(t *testing.T) { s := NewToolSearchTool(r) s.SetSkillsLoader(sl) - result := s.Execute(context.Background(), map[string]interface{}{}) + result := s.Execute(t.Context(), map[string]interface{}{}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) @@ -304,6 +318,7 @@ func createTestSkillsDir(t *testing.T, baseDir string, skillContents map[string] // --- fuzzyScore tests --- func TestFuzzyScore_ExactMatch(t *testing.T) { + t.Parallel() score := fuzzyScore("read_file", "Read a file", []string{"read_file"}) if score < 100 { t.Errorf("expected score >= 100 for exact match, got %d", score) @@ -311,6 +326,7 @@ func TestFuzzyScore_ExactMatch(t *testing.T) { } func TestFuzzyScore_ContainsMatch(t *testing.T) { + t.Parallel() score := fuzzyScore("read_file", "Read a file", []string{"read"}) if score < 50 { t.Errorf("expected score >= 50 for name contains, got %d", score) @@ -318,6 +334,7 @@ func TestFuzzyScore_ContainsMatch(t *testing.T) { } func TestFuzzyScore_DescriptionMatch(t *testing.T) { + t.Parallel() score := fuzzyScore("alpha_tool", "Search the internet", []string{"internet"}) if score < 20 { t.Errorf("expected score >= 20 for description match, got %d", score) @@ -325,6 +342,7 @@ func TestFuzzyScore_DescriptionMatch(t *testing.T) { } func TestFuzzyScore_NoMatch(t *testing.T) { + t.Parallel() score := fuzzyScore("read_file", "Read a file", []string{"zzzzz"}) if score != 0 { t.Errorf("expected 0 for no match, got %d", score) @@ -334,6 +352,7 @@ func TestFuzzyScore_NoMatch(t *testing.T) { // --- subsequenceMatch tests --- func TestSubsequenceMatch_True(t *testing.T) { + t.Parallel() tests := []struct { haystack, needle string expected bool @@ -352,6 +371,7 @@ func TestSubsequenceMatch_True(t *testing.T) { } func TestSubsequenceMatch_False(t *testing.T) { + t.Parallel() tests := []struct { haystack, needle string }{ @@ -368,6 +388,7 @@ func TestSubsequenceMatch_False(t *testing.T) { } func TestToolToSchema_WithExamples(t *testing.T) { + t.Parallel() tool := &stubToolWithExamples{ stubTool: stubTool{name: "create_ticket", desc: "Create a support ticket"}, examples: []map[string]interface{}{ @@ -389,6 +410,7 @@ func TestToolToSchema_WithExamples(t *testing.T) { } func TestToolToSchema_WithoutExamples(t *testing.T) { + t.Parallel() tool := &stubTool{name: "read_file", desc: "Read a file"} schema := ToolToSchema(tool) fn := schema["function"].(map[string]interface{}) @@ -400,12 +422,13 @@ func TestToolToSchema_WithoutExamples(t *testing.T) { // --- Discovery tracking tests --- func TestToolSearchTool_DiscoveryTracking(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubToolWithSchema{name: "edit_file", desc: "Edit a file"}) r.Register(&stubToolWithSchema{name: "web_search", desc: "Search the web"}) s := NewToolSearchTool(r) - s.Execute(context.Background(), map[string]interface{}{"query": "edit"}) + s.Execute(t.Context(), map[string]interface{}{"query": "edit"}) discovered := r.DrainDiscovered() if len(discovered) != 1 { @@ -423,12 +446,13 @@ func TestToolSearchTool_DiscoveryTracking(t *testing.T) { } func TestToolSearchTool_DiscoverySkipsGateway(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubToolWithSchema{name: "read_file", desc: "Read a file"}) r.MarkGateway("read_file") s := NewToolSearchTool(r) - s.Execute(context.Background(), map[string]interface{}{"query": "read"}) + s.Execute(t.Context(), map[string]interface{}{"query": "read"}) discovered := r.DrainDiscovered() if len(discovered) != 0 { @@ -437,11 +461,12 @@ func TestToolSearchTool_DiscoverySkipsGateway(t *testing.T) { } func TestToolSearchTool_DiscoverySkipsMetaTools(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.RegisterMetaTools() s, _ := r.Get("tool_search") - s.Execute(context.Background(), map[string]interface{}{}) + s.Execute(t.Context(), map[string]interface{}{}) discovered := r.DrainDiscovered() for _, d := range discovered { @@ -454,11 +479,12 @@ func TestToolSearchTool_DiscoverySkipsMetaTools(t *testing.T) { // --- Schema in search results tests --- func TestToolSearchTool_ReturnsSchema(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubToolWithSchema{name: "read_file", desc: "Read a file"}) s := NewToolSearchTool(r) - result := s.Execute(context.Background(), map[string]interface{}{"query": "read"}) + result := s.Execute(t.Context(), map[string]interface{}{"query": "read"}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) @@ -478,11 +504,12 @@ func TestToolSearchTool_ReturnsSchema(t *testing.T) { } func TestToolSearchTool_ListAll_ReturnsSchema(t *testing.T) { + t.Parallel() r := NewToolRegistry() r.Register(&stubToolWithSchema{name: "read_file", desc: "Read a file"}) s := NewToolSearchTool(r) - result := s.Execute(context.Background(), map[string]interface{}{}) + result := s.Execute(t.Context(), map[string]interface{}{}) var results []toolSearchResult jsonv2.Unmarshal([]byte(result.ForLLM), &results) diff --git a/pkg/tools/shell_test.go b/pkg/tools/shell_test.go index c12cffca3..3c1a6b478 100644 --- a/pkg/tools/shell_test.go +++ b/pkg/tools/shell_test.go @@ -1,7 +1,6 @@ package tools import ( - "context" "os" "path/filepath" "strings" @@ -11,9 +10,10 @@ import ( // TestShellTool_Success verifies successful command execution func TestShellTool_Success(t *testing.T) { + t.Parallel() tool := NewExecTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "command": "echo 'hello world'", } @@ -38,9 +38,10 @@ func TestShellTool_Success(t *testing.T) { // TestShellTool_Failure verifies failed command execution func TestShellTool_Failure(t *testing.T) { + t.Parallel() tool := NewExecTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "command": "ls /nonexistent_directory_12345", } @@ -65,10 +66,11 @@ func TestShellTool_Failure(t *testing.T) { // TestShellTool_Timeout verifies command timeout handling func TestShellTool_Timeout(t *testing.T) { + t.Parallel() tool := NewExecTool("", false) tool.SetTimeout(100 * time.Millisecond) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "command": "sleep 10", } @@ -88,14 +90,17 @@ func TestShellTool_Timeout(t *testing.T) { // TestShellTool_WorkingDir verifies custom working directory func TestShellTool_WorkingDir(t *testing.T) { + t.Parallel( // Create temp directory + ) + tmpDir := t.TempDir() testFile := filepath.Join(tmpDir, "test.txt") os.WriteFile(testFile, []byte("test content"), 0644) tool := NewExecTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "command": "cat test.txt", "working_dir": tmpDir, @@ -114,9 +119,10 @@ func TestShellTool_WorkingDir(t *testing.T) { // TestShellTool_DangerousCommand verifies safety guard blocks dangerous commands func TestShellTool_DangerousCommand(t *testing.T) { + t.Parallel() tool := NewExecTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "command": "rm -rf /", } @@ -135,9 +141,10 @@ func TestShellTool_DangerousCommand(t *testing.T) { // TestShellTool_MissingCommand verifies error handling for missing command func TestShellTool_MissingCommand(t *testing.T) { + t.Parallel() tool := NewExecTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{} result := tool.Execute(ctx, args) @@ -150,9 +157,10 @@ func TestShellTool_MissingCommand(t *testing.T) { // TestShellTool_StderrCapture verifies stderr is captured and included func TestShellTool_StderrCapture(t *testing.T) { + t.Parallel() tool := NewExecTool("", false) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "command": "sh -c 'echo stdout; echo stderr >&2'", } @@ -170,9 +178,10 @@ func TestShellTool_StderrCapture(t *testing.T) { // TestShellTool_OutputTruncation verifies long output is truncated func TestShellTool_OutputTruncation(t *testing.T) { + t.Parallel() tool := NewExecTool("", false) - ctx := context.Background() + ctx := t.Context() // Generate long output (>10000 chars) args := map[string]interface{}{ "command": "python3 -c \"print('x' * 20000)\" || echo " + strings.Repeat("x", 20000), @@ -188,11 +197,12 @@ func TestShellTool_OutputTruncation(t *testing.T) { // TestShellTool_RestrictToWorkspace verifies workspace restriction func TestShellTool_RestrictToWorkspace(t *testing.T) { + t.Parallel() tmpDir := t.TempDir() tool := NewExecTool(tmpDir, false) tool.SetRestrictToWorkspace(true) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "command": "cat ../../etc/passwd", } @@ -211,6 +221,7 @@ func TestShellTool_RestrictToWorkspace(t *testing.T) { // TestGuardCommand_DenyPatterns is a comprehensive table-driven test for all deny patterns func TestGuardCommand_DenyPatterns(t *testing.T) { + t.Parallel() tool := NewExecTool("/tmp", false) tests := []struct { @@ -306,6 +317,7 @@ func TestGuardCommand_DenyPatterns(t *testing.T) { // TestGuardCommand_AllowListMode tests the allowlist mode func TestGuardCommand_AllowListMode(t *testing.T) { + t.Parallel() tool := NewExecTool("/tmp", false) tool.SetMode(ShellModeAllowList) tool.SetAllowPatterns([]string{`^(echo|ls|cat)\b`}) @@ -340,10 +352,11 @@ func TestGuardCommand_AllowListMode(t *testing.T) { // TestGuardCommand_DisabledMode tests the disabled mode func TestShellTool_DisabledMode(t *testing.T) { + t.Parallel() tool := NewExecTool("/tmp", false) tool.SetMode(ShellModeDisabled) - ctx := context.Background() + ctx := t.Context() result := tool.Execute(ctx, map[string]interface{}{"command": "echo hello"}) if !result.IsError { @@ -356,6 +369,7 @@ func TestShellTool_DisabledMode(t *testing.T) { // TestGuardCommand_AllowListEmpty tests allowlist mode with no patterns configured func TestGuardCommand_AllowListEmpty(t *testing.T) { + t.Parallel() tool := NewExecTool("/tmp", false) tool.SetMode(ShellModeAllowList) // Don't set any allow patterns diff --git a/pkg/tools/skills_test.go b/pkg/tools/skills_test.go index a36eb11de..36a7e45ff 100644 --- a/pkg/tools/skills_test.go +++ b/pkg/tools/skills_test.go @@ -1,7 +1,6 @@ package tools import ( - "context" "os" "path/filepath" "testing" @@ -59,80 +58,83 @@ No links. } func TestSkillSearchTool(t *testing.T) { + t.Parallel() loader := setupTestSkills(t) tool := NewSkillSearchTool(loader) assert.Equal(t, "skill_search", tool.Name()) t.Run("finds matching skills", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{"query": "trading"}) + result := tool.Execute(t.Context(), map[string]interface{}{"query": "trading"}) assert.False(t, result.IsError) assert.Contains(t, result.ForLLM, "risk-management") assert.Contains(t, result.ForLLM, "position-sizing") }) t.Run("no results for unmatched query", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{"query": "kubernetes"}) + result := tool.Execute(t.Context(), map[string]interface{}{"query": "kubernetes"}) assert.False(t, result.IsError) assert.Contains(t, result.ForLLM, "No skills matched") }) t.Run("error on empty query", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{"query": ""}) + result := tool.Execute(t.Context(), map[string]interface{}{"query": ""}) assert.True(t, result.IsError) }) } func TestSkillReadTool(t *testing.T) { + t.Parallel() loader := setupTestSkills(t) tool := NewSkillReadTool(loader) assert.Equal(t, "skill_read", tool.Name()) t.Run("reads existing skill", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{"name": "risk-management"}) + result := tool.Execute(t.Context(), map[string]interface{}{"name": "risk-management"}) assert.False(t, result.IsError) assert.Contains(t, result.ForLLM, "Risk Management") assert.Contains(t, result.ForLLM, "position-sizing") }) t.Run("error on missing skill", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{"name": "nonexistent"}) + result := tool.Execute(t.Context(), map[string]interface{}{"name": "nonexistent"}) assert.True(t, result.IsError) assert.Contains(t, result.ForLLM, "not found") }) t.Run("error on empty name", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{"name": ""}) + result := tool.Execute(t.Context(), map[string]interface{}{"name": ""}) assert.True(t, result.IsError) }) } func TestSkillTraverseTool(t *testing.T) { + t.Parallel() loader := setupTestSkills(t) tool := NewSkillTraverseTool(loader) assert.Equal(t, "skill_traverse", tool.Name()) t.Run("traverses links at depth 1", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{"name": "risk-management"}) + result := tool.Execute(t.Context(), map[string]interface{}{"name": "risk-management"}) assert.False(t, result.IsError) assert.Contains(t, result.ForLLM, "position-sizing") }) t.Run("no links found", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{"name": "code-review"}) + result := tool.Execute(t.Context(), map[string]interface{}{"name": "code-review"}) assert.False(t, result.IsError) assert.Contains(t, result.ForLLM, "no outgoing links") }) t.Run("error on missing skill", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{"name": "nonexistent"}) + result := tool.Execute(t.Context(), map[string]interface{}{"name": "nonexistent"}) assert.True(t, result.IsError) }) t.Run("custom depth", func(t *testing.T) { - result := tool.Execute(context.Background(), map[string]interface{}{ + result := tool.Execute(t.Context(), map[string]interface{}{ "name": "risk-management", "depth": float64(2), }) diff --git a/pkg/tools/spawn_test.go b/pkg/tools/spawn_test.go index db114e67f..071f0dfa5 100644 --- a/pkg/tools/spawn_test.go +++ b/pkg/tools/spawn_test.go @@ -9,6 +9,7 @@ import ( ) func TestSpawnTool_Execute_NestedDelegationGuardrails(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) manager.SetRunLoop(func(_ context.Context, _ ToolLoopConfig, _, _, _, _ string) (*ToolLoopResult, error) { @@ -16,7 +17,7 @@ func TestSpawnTool_Execute_NestedDelegationGuardrails(t *testing.T) { }) tool := NewSpawnTool(manager) - ctx := withDelegationContext(context.Background(), "parent", 1) + ctx := withDelegationContext(t.Context(), "parent", 1) missingMetadata := tool.Execute(ctx, map[string]interface{}{ "task": "nested task", diff --git a/pkg/tools/subagent_manager_test.go b/pkg/tools/subagent_manager_test.go index 65a794c61..eead02c64 100644 --- a/pkg/tools/subagent_manager_test.go +++ b/pkg/tools/subagent_manager_test.go @@ -24,10 +24,11 @@ func waitForCondition(t *testing.T, timeout time.Duration, cond func() bool) { } func TestSubagentManager_SpawnRequiresRunLoop(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) - _, err := manager.Spawn(context.Background(), "task-without-loop", "label", "", "", "cli", "chat", nil) + _, err := manager.Spawn(t.Context(), "task-without-loop", "label", "", "", "cli", "chat", nil) if err == nil { t.Fatal("expected spawn to fail when run loop is not configured") } @@ -37,6 +38,7 @@ func TestSubagentManager_SpawnRequiresRunLoop(t *testing.T) { } func TestSubagentManager_SpawnDelegationGuardrails(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) manager.SetDelegationLimits(2, 1) @@ -47,23 +49,23 @@ func TestSubagentManager_SpawnDelegationGuardrails(t *testing.T) { return &ToolLoopResult{Content: "done", Iterations: 1}, nil }) - _, err := manager.Spawn(context.Background(), "task-1", "one", "", "", "cli", "chat", nil) + _, err := manager.Spawn(t.Context(), "task-1", "one", "", "", "cli", "chat", nil) if err != nil { t.Fatalf("first spawn should succeed: %v", err) } - _, err = manager.Spawn(context.Background(), "task-2", "two", "", "", "cli", "chat", nil) + _, err = manager.Spawn(t.Context(), "task-2", "two", "", "", "cli", "chat", nil) if err == nil || !strings.Contains(err.Error(), "delegation fanout exceeded") { t.Fatalf("expected fanout error, got: %v", err) } - nestedCtx := withDelegationContext(context.Background(), "parent", 1) + nestedCtx := withDelegationContext(t.Context(), "parent", 1) _, err = manager.Spawn(nestedCtx, "task-3", "three", "", "", "cli", "chat", nil) if err == nil || !strings.Contains(err.Error(), "nested delegation requires delegated_scope and kept_work") { t.Fatalf("expected nested delegation metadata error, got: %v", err) } - deepCtx := withDelegationContext(context.Background(), "parent", 2) + deepCtx := withDelegationContext(t.Context(), "parent", 2) _, err = manager.Spawn(deepCtx, "task-4", "four", "lookup", "synthesize", "cli", "chat", nil) if err == nil || !strings.Contains(err.Error(), "delegation depth exceeded") { t.Fatalf("expected depth error, got: %v", err) @@ -81,6 +83,7 @@ func TestSubagentManager_SpawnDelegationGuardrails(t *testing.T) { } func TestSubagentManager_ConcurrentSpawnRespectsFanout(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) manager.SetDelegationLimits(3, 2) @@ -105,7 +108,7 @@ func TestSubagentManager_ConcurrentSpawnRespectsFanout(t *testing.T) { go func(i int) { defer wg.Done() <-start - _, err := manager.Spawn(context.Background(), fmt.Sprintf("task-%d", i), fmt.Sprintf("label-%d", i), "", "", "cli", "chat", nil) + _, err := manager.Spawn(t.Context(), fmt.Sprintf("task-%d", i), fmt.Sprintf("label-%d", i), "", "", "cli", "chat", nil) mu.Lock() defer mu.Unlock() if err == nil { @@ -151,6 +154,7 @@ func TestSubagentManager_ConcurrentSpawnRespectsFanout(t *testing.T) { } func TestSubagentManager_SpawnAuditLineageAndRuntimeContext(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", bus.NewMessageBus()) @@ -171,7 +175,7 @@ func TestSubagentManager_SpawnAuditLineageAndRuntimeContext(t *testing.T) { eventsCh <- evt }) - parentCtx := withDelegationContext(context.Background(), "parent-9", 1) + parentCtx := withDelegationContext(t.Context(), "parent-9", 1) _, err := manager.Spawn(parentCtx, "task-a", "label-a", "collect facts", "final synthesis", "telegram", "chat-7", nil) if err != nil { t.Fatalf("spawn failed: %v", err) diff --git a/pkg/tools/subagent_tool_test.go b/pkg/tools/subagent_tool_test.go index e5f8205b5..e26bc4593 100644 --- a/pkg/tools/subagent_tool_test.go +++ b/pkg/tools/subagent_tool_test.go @@ -33,8 +33,8 @@ func (m *MockLanguageModel) Generate(_ context.Context, call fantasy.Call) (*fan }, nil } -func (m *MockLanguageModel) Stream(_ context.Context, call fantasy.Call) (fantasy.StreamResponse, error) { - resp, err := m.Generate(context.Background(), call) +func (m *MockLanguageModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.StreamResponse, error) { + resp, err := m.Generate(ctx, call) if err != nil { return nil, err } @@ -59,6 +59,7 @@ func (m *MockLanguageModel) Model() string { return "test-model" } // TestSubagentTool_Name verifies tool name func TestSubagentTool_Name(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", nil) tool := NewSubagentTool(manager) @@ -70,6 +71,7 @@ func TestSubagentTool_Name(t *testing.T) { // TestSubagentTool_Description verifies tool description func TestSubagentTool_Description(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", nil) tool := NewSubagentTool(manager) @@ -85,6 +87,7 @@ func TestSubagentTool_Description(t *testing.T) { // TestSubagentTool_Parameters verifies tool parameters schema func TestSubagentTool_Parameters(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", nil) tool := NewSubagentTool(manager) @@ -153,6 +156,7 @@ func TestSubagentTool_Parameters(t *testing.T) { // TestSubagentTool_SetContext verifies context setting func TestSubagentTool_SetContext(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", nil) tool := NewSubagentTool(manager) @@ -166,6 +170,7 @@ func TestSubagentTool_SetContext(t *testing.T) { // TestSubagentTool_Execute_Success tests successful execution func TestSubagentTool_Execute_Success(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} msgBus := bus.NewMessageBus() manager := NewSubagentManager(provider, "test-model", "/tmp/test", msgBus) @@ -178,7 +183,7 @@ func TestSubagentTool_Execute_Success(t *testing.T) { tool := NewSubagentTool(manager) tool.SetContext("telegram", "chat-123") - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "task": "Write a haiku about coding", "label": "haiku-task", @@ -228,6 +233,7 @@ func TestSubagentTool_Execute_Success(t *testing.T) { // TestSubagentTool_Execute_NoLabel tests execution without label func TestSubagentTool_Execute_NoLabel(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} msgBus := bus.NewMessageBus() manager := NewSubagentManager(provider, "test-model", "/tmp/test", msgBus) @@ -236,7 +242,7 @@ func TestSubagentTool_Execute_NoLabel(t *testing.T) { }) tool := NewSubagentTool(manager) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "task": "Test task without label", } @@ -255,11 +261,12 @@ func TestSubagentTool_Execute_NoLabel(t *testing.T) { // TestSubagentTool_Execute_MissingTask tests error handling for missing task func TestSubagentTool_Execute_MissingTask(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} manager := NewSubagentManager(provider, "test-model", "/tmp/test", nil) tool := NewSubagentTool(manager) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "label": "test", } @@ -284,9 +291,10 @@ func TestSubagentTool_Execute_MissingTask(t *testing.T) { // TestSubagentTool_Execute_NilManager tests error handling for nil manager func TestSubagentTool_Execute_NilManager(t *testing.T) { + t.Parallel() tool := NewSubagentTool(nil) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "task": "test task", } @@ -305,6 +313,7 @@ func TestSubagentTool_Execute_NilManager(t *testing.T) { // TestSubagentTool_Execute_ContextPassing verifies context is properly used func TestSubagentTool_Execute_ContextPassing(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} msgBus := bus.NewMessageBus() manager := NewSubagentManager(provider, "test-model", "/tmp/test", msgBus) @@ -318,7 +327,7 @@ func TestSubagentTool_Execute_ContextPassing(t *testing.T) { chatID := "test-chat" tool.SetContext(channel, chatID) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "task": "Test context passing", } @@ -335,6 +344,7 @@ func TestSubagentTool_Execute_ContextPassing(t *testing.T) { } func TestSubagentTool_Execute_NestedDelegationRequiresScopeAndKeptWork(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} msgBus := bus.NewMessageBus() manager := NewSubagentManager(provider, "test-model", "/tmp/test", msgBus) @@ -344,7 +354,7 @@ func TestSubagentTool_Execute_NestedDelegationRequiresScopeAndKeptWork(t *testin tool := NewSubagentTool(manager) // Simulate nested delegation (depth > 0) without delegated scope metadata. - ctx := withDelegationContext(context.Background(), "parent-task", 1) + ctx := withDelegationContext(t.Context(), "parent-task", 1) result := tool.Execute(ctx, map[string]interface{}{ "task": "nested task", "label": "nested", @@ -359,6 +369,7 @@ func TestSubagentTool_Execute_NestedDelegationRequiresScopeAndKeptWork(t *testin } func TestSubagentTool_Execute_NestedDelegationWithMetadataSucceeds(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} msgBus := bus.NewMessageBus() manager := NewSubagentManager(provider, "test-model", "/tmp/test", msgBus) @@ -367,7 +378,7 @@ func TestSubagentTool_Execute_NestedDelegationWithMetadataSucceeds(t *testing.T) }) tool := NewSubagentTool(manager) - ctx := withDelegationContext(context.Background(), "parent-task", 1) + ctx := withDelegationContext(t.Context(), "parent-task", 1) result := tool.Execute(ctx, map[string]interface{}{ "task": "nested task", "label": "nested", @@ -382,6 +393,7 @@ func TestSubagentTool_Execute_NestedDelegationWithMetadataSucceeds(t *testing.T) // TestSubagentTool_ForUserTruncation verifies long content is truncated for user func TestSubagentTool_ForUserTruncation(t *testing.T) { + t.Parallel() provider := &MockLanguageModel{} msgBus := bus.NewMessageBus() manager := NewSubagentManager(provider, "test-model", "/tmp/test", msgBus) @@ -390,7 +402,7 @@ func TestSubagentTool_ForUserTruncation(t *testing.T) { }) tool := NewSubagentTool(manager) - ctx := context.Background() + ctx := t.Context() // Create a task that will generate long response longTask := strings.Repeat("This is a very long task description. ", 100) diff --git a/pkg/tools/toolloop_test.go b/pkg/tools/toolloop_test.go index dc21efef4..2395c0826 100644 --- a/pkg/tools/toolloop_test.go +++ b/pkg/tools/toolloop_test.go @@ -1,13 +1,13 @@ package tools import ( - "context" "errors" "testing" ) func TestRunToolLoop_ReturnsContractError(t *testing.T) { - result, err := RunToolLoop(context.Background(), ToolLoopConfig{}, "", "", "", "") + t.Parallel() + result, err := RunToolLoop(t.Context(), ToolLoopConfig{}, "", "", "", "") if result != nil { t.Fatalf("expected nil result when run loop is not configured, got %#v", result) } diff --git a/pkg/tools/web_test.go b/pkg/tools/web_test.go index 99b31ebbf..8902399c5 100644 --- a/pkg/tools/web_test.go +++ b/pkg/tools/web_test.go @@ -1,7 +1,6 @@ package tools import ( - "context" "net/http" "net/http/httptest" "strings" @@ -13,6 +12,7 @@ import ( // TestWebTool_WebFetch_Success verifies successful URL fetching func TestWebTool_WebFetch_Success(t *testing.T) { + t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) @@ -21,7 +21,7 @@ func TestWebTool_WebFetch_Success(t *testing.T) { defer server.Close() tool := NewWebFetchTool(50000) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "url": server.URL, } @@ -46,6 +46,7 @@ func TestWebTool_WebFetch_Success(t *testing.T) { // TestWebTool_WebFetch_JSON verifies JSON content handling func TestWebTool_WebFetch_JSON(t *testing.T) { + t.Parallel() testData := map[string]string{"key": "value", "number": "123"} expectedJSON, _ := jsonv2.Marshal(testData, jsontext.WithIndent(" ")) @@ -57,7 +58,7 @@ func TestWebTool_WebFetch_JSON(t *testing.T) { defer server.Close() tool := NewWebFetchTool(50000) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "url": server.URL, } @@ -77,8 +78,9 @@ func TestWebTool_WebFetch_JSON(t *testing.T) { // TestWebTool_WebFetch_InvalidURL verifies error handling for invalid URL func TestWebTool_WebFetch_InvalidURL(t *testing.T) { + t.Parallel() tool := NewWebFetchTool(50000) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "url": "not-a-valid-url", } @@ -98,8 +100,9 @@ func TestWebTool_WebFetch_InvalidURL(t *testing.T) { // TestWebTool_WebFetch_UnsupportedScheme verifies error handling for non-http URLs func TestWebTool_WebFetch_UnsupportedScheme(t *testing.T) { + t.Parallel() tool := NewWebFetchTool(50000) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "url": "ftp://example.com/file.txt", } @@ -119,8 +122,9 @@ func TestWebTool_WebFetch_UnsupportedScheme(t *testing.T) { // TestWebTool_WebFetch_MissingURL verifies error handling for missing URL func TestWebTool_WebFetch_MissingURL(t *testing.T) { + t.Parallel() tool := NewWebFetchTool(50000) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{} result := tool.Execute(ctx, args) @@ -138,6 +142,7 @@ func TestWebTool_WebFetch_MissingURL(t *testing.T) { // TestWebTool_WebFetch_Truncation verifies content truncation func TestWebTool_WebFetch_Truncation(t *testing.T) { + t.Parallel() longContent := strings.Repeat("x", 20000) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -148,7 +153,7 @@ func TestWebTool_WebFetch_Truncation(t *testing.T) { defer server.Close() tool := NewWebFetchTool(1000) // Limit to 1000 chars - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "url": server.URL, } @@ -177,6 +182,7 @@ func TestWebTool_WebFetch_Truncation(t *testing.T) { // TestWebTool_WebSearch_NoApiKey verifies that no tool is created when API key is missing func TestWebTool_WebSearch_NoApiKey(t *testing.T) { + t.Parallel() tool := NewWebSearchTool(WebSearchToolOptions{BraveEnabled: true, BraveAPIKey: ""}) if tool != nil { t.Errorf("Expected nil tool when Brave API key is empty") @@ -191,8 +197,9 @@ func TestWebTool_WebSearch_NoApiKey(t *testing.T) { // TestWebTool_WebSearch_MissingQuery verifies error handling for missing query func TestWebTool_WebSearch_MissingQuery(t *testing.T) { + t.Parallel() tool := NewWebSearchTool(WebSearchToolOptions{BraveEnabled: true, BraveAPIKey: "test-key", BraveMaxResults: 5}) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{} result := tool.Execute(ctx, args) @@ -205,6 +212,7 @@ func TestWebTool_WebSearch_MissingQuery(t *testing.T) { // TestWebTool_WebFetch_HTMLExtraction verifies HTML text extraction func TestWebTool_WebFetch_HTMLExtraction(t *testing.T) { + t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) @@ -213,7 +221,7 @@ func TestWebTool_WebFetch_HTMLExtraction(t *testing.T) { defer server.Close() tool := NewWebFetchTool(50000) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "url": server.URL, } @@ -238,8 +246,9 @@ func TestWebTool_WebFetch_HTMLExtraction(t *testing.T) { // TestWebTool_WebFetch_MissingDomain verifies error handling for URL without domain func TestWebTool_WebFetch_MissingDomain(t *testing.T) { + t.Parallel() tool := NewWebFetchTool(50000) - ctx := context.Background() + ctx := t.Context() args := map[string]interface{}{ "url": "https://", } diff --git a/pkg/worker/worker_test.go b/pkg/worker/worker_test.go index 96c66a240..91e2e6b0d 100644 --- a/pkg/worker/worker_test.go +++ b/pkg/worker/worker_test.go @@ -20,7 +20,7 @@ func newTestDB(t *testing.T) *sqlc.Queries { t.Helper() d, err := delegate.NewLibSQLInMemory() require.NoError(t, err) - require.NoError(t, d.Init(context.Background())) + require.NoError(t, d.Init(t.Context())) t.Cleanup(func() { _ = d.Close() }) return d.Queries() } @@ -28,7 +28,7 @@ func newTestDB(t *testing.T) *sqlc.Queries { func enqueueJob(t *testing.T, q *sqlc.Queries, kind, dedupeKey string, maxAttempts int64) sqlc.Job { t.Helper() past := time.Now().UTC().Add(-time.Second) - job, err := q.EnqueueJob(context.Background(), sqlc.EnqueueJobParams{ + job, err := q.EnqueueJob(t.Context(), sqlc.EnqueueJobParams{ ID: ids.New(), Kind: kind, DedupeKey: &dedupeKey, @@ -41,14 +41,16 @@ func enqueueJob(t *testing.T, q *sqlc.Queries, kind, dedupeKey string, maxAttemp } func TestWorker_RunOnce_NoJobs(t *testing.T) { + t.Parallel() q := newTestDB(t) - err := worker.RunOnce(context.Background(), q, nil) + err := worker.RunOnce(t.Context(), q, nil) assert.NoError(t, err, "empty queue should not error") } func TestWorker_RunOnce_HandlerCalled(t *testing.T) { + t.Parallel() q := newTestDB(t) - ctx := context.Background() + ctx := t.Context() enqueueJob(t, q, "greet", "greet-1", 3) @@ -69,8 +71,9 @@ func TestWorker_RunOnce_HandlerCalled(t *testing.T) { } func TestWorker_RunOnce_JobMarkedSucceeded(t *testing.T) { + t.Parallel() q := newTestDB(t) - ctx := context.Background() + ctx := t.Context() job := enqueueJob(t, q, "ping", "ping-1", 3) @@ -91,8 +94,9 @@ func TestWorker_RunOnce_JobMarkedSucceeded(t *testing.T) { } func TestWorker_RunOnce_HandlerError_Requeued(t *testing.T) { + t.Parallel() q := newTestDB(t) - ctx := context.Background() + ctx := t.Context() job := enqueueJob(t, q, "fail", "fail-1", 3) @@ -116,8 +120,9 @@ func TestWorker_RunOnce_HandlerError_Requeued(t *testing.T) { } func TestWorker_RunOnce_MaxAttemptsExhausted_MarkedFailed(t *testing.T) { + t.Parallel() q := newTestDB(t) - ctx := context.Background() + ctx := t.Context() enqueueJob(t, q, "exhaust", "exhaust-1", 1) @@ -145,8 +150,9 @@ func TestWorker_RunOnce_MaxAttemptsExhausted_MarkedFailed(t *testing.T) { } func TestWorker_RunOnce_UnknownKind_MarkedFailed(t *testing.T) { + t.Parallel() q := newTestDB(t) - ctx := context.Background() + ctx := t.Context() enqueueJob(t, q, "unknown-kind", "unk-1", 3) @@ -169,13 +175,15 @@ func TestWorker_RunOnce_UnknownKind_MarkedFailed(t *testing.T) { } func TestWorker_RunOnce_NilQueries(t *testing.T) { - err := worker.RunOnce(context.Background(), nil, nil) + t.Parallel() + err := worker.RunOnce(t.Context(), nil, nil) assert.Error(t, err, "nil queries should return an error") } func TestWorker_RunLoop_StopsOnCancel(t *testing.T) { + t.Parallel() q := newTestDB(t) - ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + ctx, cancel := context.WithTimeout(t.Context(), 200*time.Millisecond) defer cancel() opts := &worker.Options{ @@ -189,6 +197,7 @@ func TestWorker_RunLoop_StopsOnCancel(t *testing.T) { } func TestWorker_RunLoop_ProcessesJobs(t *testing.T) { + t.Parallel() q := newTestDB(t) var processed atomic.Int32 @@ -197,7 +206,7 @@ func TestWorker_RunLoop_ProcessesJobs(t *testing.T) { enqueueJob(t, q, "batch", "batch-"+string(rune('A'+i)), 3) } - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second) defer cancel() opts := &worker.Options{