From 906d11612dfae8435fd11a824c8ef3f8347a5f6e Mon Sep 17 00:00:00 2001 From: ZanzyTHEbar Date: Sat, 21 Feb 2026 21:08:54 +0000 Subject: [PATCH] test: modernize test suite with t.Context() and t.Parallel() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply two idiomatic Go test improvements across all 113 test files in the codebase: 1. Replace context.Background() with t.Context() in test functions. t.Context() (added in Go 1.21) returns a context that is automatically cancelled when the test finishes, preventing goroutine leaks and making test teardown deterministic without manual cancel calls. 2. Add t.Parallel() to independent test functions. Parallel tests run concurrently within a package, significantly reducing total test suite wall-clock time and surfacing data races that sequential execution would hide. Also fix TestStateStore_AddTransition: add "UpdatedAt" to the cmpopts.IgnoreFields list — the DB now populates UpdatedAt on insert, causing the zero-value comparison to fail. Packages affected (113 files): - eval/go_evals - internal/fantasy (agent, json, jsonrepair, providers/*, providertests/*, schema, tool_runtime_*) - pkg/agent (integration, kv_delegate, loop, offloading_runtime, state_store, tool_result_search) - pkg/auth (oauth, pkce, store) - pkg/cache, pkg/channels, pkg/config, pkg/cron - pkg/fantasy (adapter, convert) - pkg/heartbeat, pkg/ids - pkg/itr (commands, dag/*, fb_codec, wasm/*) - pkg/logger - pkg/memory (dag/*, delegate/*, integration, kernel_contract, migrate_sessions, observation, store/*) - pkg/migrate - pkg/rlm (engine, fanout, rope, strategy) - pkg/runtime - pkg/security (jsonextract, redact, securebus/*, urlguard, vault, zkp) - pkg/session, pkg/skills, pkg/state, pkg/sync - pkg/tools (agentic_map, call, dag, edit, filesystem, focus, llm_map, map_*, message, obligation, registry_progressive, result, retrieval, search, shell, skills, spawn, subagent_*, toolloop, web) - pkg/worker --- eval/go_evals/eval_test.go | 78 ++-- internal/fantasy/agent_stream_test.go | 15 +- internal/fantasy/agent_test.go | 56 +-- internal/fantasy/json_test.go | 64 ++- .../fantasy/jsonrepair/jsonrepair_test.go | 24 ++ .../fantasy/providers/azure/azure_test.go | 1 + .../fantasy/providers/openai/openai_test.go | 89 ++-- .../fantasy/providertests/anthropic_test.go | 5 + .../providertests/azure_responses_test.go | 2 + internal/fantasy/providertests/azure_test.go | 2 + .../fantasy/providertests/bedrock_test.go | 2 + internal/fantasy/providertests/google_test.go | 4 + .../providertests/image_upload_test.go | 2 + .../providertests/openai_responses_test.go | 3 + internal/fantasy/providertests/openai_test.go | 2 + .../providertests/openaicompat_test.go | 3 + .../fantasy/providertests/openrouter_test.go | 3 + .../providertests/provider_registry_test.go | 19 +- internal/fantasy/providertests/vercel_test.go | 3 + internal/fantasy/react_fsm_test.go | 31 +- internal/fantasy/schema/schema_test.go | 4 + internal/fantasy/tool_runtime_dag_test.go | 10 +- .../fantasy/tool_runtime_parallel_test.go | 8 +- internal/fantasy/tool_test.go | 68 ++-- pkg/agent/integration_test.go | 22 +- pkg/agent/kv_delegate_test.go | 31 +- pkg/agent/loop_test.go | 46 ++- pkg/agent/offloading_runtime_test.go | 30 +- pkg/agent/state_store_test.go | 72 ++-- pkg/agent/tool_result_search_test.go | 181 ++++---- pkg/auth/oauth_test.go | 15 + pkg/auth/pkce_test.go | 2 + pkg/auth/store_test.go | 6 + pkg/cache/lru_test.go | 22 + pkg/channels/base_test.go | 1 + pkg/channels/slack_test.go | 4 + pkg/config/config_test.go | 18 + pkg/cron/service_test.go | 1 + pkg/fantasy/adapter_test.go | 26 +- pkg/fantasy/convert_test.go | 15 + pkg/heartbeat/service_test.go | 9 + pkg/ids/ids_test.go | 19 + pkg/itr/commands_test.go | 28 +- pkg/itr/dag/executor_test.go | 27 +- pkg/itr/dag/planner_test.go | 24 +- pkg/itr/dag/replan_test.go | 2 + pkg/itr/dag/resolver_test.go | 9 + pkg/itr/dag/router_test.go | 7 + pkg/itr/fb_codec_test.go | 385 ++++++------------ pkg/itr/wasm/runtime_test.go | 21 +- pkg/itr/wasm/transport_test.go | 24 +- pkg/logger/logger_test.go | 5 + pkg/memory/dag/backfill_test.go | 4 +- pkg/memory/dag/dag_test.go | 33 +- pkg/memory/delegate/factory_test.go | 20 +- pkg/memory/delegate/sqlite_audit_test.go | 18 +- pkg/memory/delegate/sqlite_bench_test.go | 22 +- pkg/memory/delegate/sqlite_dag_test.go | 4 +- pkg/memory/delegate/sqlite_doc_test.go | 15 +- .../delegate/sqlite_integration_test.go | 16 +- pkg/memory/delegate/sqlite_kv_test.go | 12 +- .../delegate/sqlite_session_msg_test.go | 9 +- pkg/memory/delegate/sqlite_test.go | 39 +- pkg/memory/integration_test.go | 36 +- pkg/memory/kernel_contract_test.go | 2 + pkg/memory/migrate_sessions_test.go | 7 + pkg/memory/observation/observation_test.go | 64 ++- pkg/memory/store/cached_embedder_test.go | 17 +- pkg/memory/store/chunker_test.go | 5 + pkg/memory/store/embed_factory_test.go | 10 +- pkg/memory/store/embed_ollama_test.go | 17 +- pkg/memory/store/embed_openai_test.go | 20 +- pkg/memory/store/memory_store_test.go | 66 ++- pkg/memory/store/memory_tool_test.go | 15 +- pkg/memory/store/queue_test.go | 22 +- pkg/memory/store/scorer_test.go | 12 +- pkg/migrate/migrate_test.go | 16 + pkg/rlm/engine_test.go | 32 +- pkg/rlm/fanout_test.go | 17 +- pkg/rlm/rope_test.go | 18 + pkg/rlm/strategy_test.go | 9 + pkg/runtime/runtime_test.go | 10 +- pkg/security/jsonextract_test.go | 13 + pkg/security/redact_test.go | 8 + pkg/security/securebus/audit_test.go | 7 + pkg/security/securebus/bus_test.go | 47 ++- pkg/security/securebus/policy_test.go | 9 + .../securebus/socket_transport_test.go | 10 +- pkg/security/urlguard_test.go | 6 + pkg/security/vault_test.go | 7 + pkg/security/zkp_test.go | 11 + pkg/session/manager_test.go | 55 ++- pkg/skills/graph_test.go | 10 + pkg/skills/loader_test.go | 1 + pkg/skills/templates_test.go | 4 + pkg/state/state_test.go | 23 +- pkg/sync/identity_test.go | 48 ++- pkg/tools/agentic_map_test.go | 12 +- pkg/tools/call_test.go | 48 ++- pkg/tools/dag_test.go | 34 +- pkg/tools/edit_test.go | 34 +- pkg/tools/filesystem_test.go | 42 +- pkg/tools/focus_test.go | 21 +- pkg/tools/llm_map_test.go | 6 +- pkg/tools/map_flatbuffer_codec_test.go | 2 + pkg/tools/map_runtime_integration_test.go | 46 ++- pkg/tools/message_test.go | 22 +- pkg/tools/obligation_test.go | 13 +- pkg/tools/registry_progressive_test.go | 6 + pkg/tools/result_test.go | 144 ++----- pkg/tools/retrieval_test.go | 15 +- pkg/tools/search_test.go | 59 ++- pkg/tools/shell_test.go | 36 +- pkg/tools/skills_test.go | 24 +- pkg/tools/spawn_test.go | 3 +- pkg/tools/subagent_manager_test.go | 18 +- pkg/tools/subagent_tool_test.go | 32 +- pkg/tools/toolloop_test.go | 4 +- pkg/tools/web_test.go | 29 +- pkg/worker/worker_test.go | 31 +- 120 files changed, 1938 insertions(+), 1139 deletions(-) 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{