diff --git a/internal/config/unknownfields.go b/internal/config/unknownfields.go index e5c36901d..7341a098a 100644 --- a/internal/config/unknownfields.go +++ b/internal/config/unknownfields.go @@ -131,7 +131,7 @@ type knownField struct { } func derefType(t reflect.Type) reflect.Type { - for t != nil && t.Kind() == reflect.Ptr { + for t != nil && t.Kind() == reflect.Pointer { t = t.Elem() } return t diff --git a/internal/providers/anthropic/cache_breakpoints_test.go b/internal/providers/anthropic/cache_breakpoints_test.go index 40f984233..adbf5f450 100644 --- a/internal/providers/anthropic/cache_breakpoints_test.go +++ b/internal/providers/anthropic/cache_breakpoints_test.go @@ -16,6 +16,7 @@ import ( // unmarked (Anthropic caps breakpoints at 4 per request: system, tools, and // these two). func TestAnthropicRequestMarksLastTwoMessagesForCaching(t *testing.T) { + t.Parallel() var gotBody map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil { @@ -73,6 +74,7 @@ func TestAnthropicRequestMarksLastTwoMessagesForCaching(t *testing.T) { // the breakpoint, and thinking blocks must never carry cache_control (the API // rejects them) — the marker goes on the last cacheable block instead. func TestApplyMessageCacheBreakpointsSkipsThinkingBlocks(t *testing.T) { + t.Parallel() messages := []anthropicMessage{ {Role: "user", Content: "plain string"}, {Role: "assistant", Content: []map[string]any{ diff --git a/internal/providers/anthropic/dropped_test.go b/internal/providers/anthropic/dropped_test.go index a5b25fb0b..3c1967e5c 100644 --- a/internal/providers/anthropic/dropped_test.go +++ b/internal/providers/anthropic/dropped_test.go @@ -13,6 +13,7 @@ import ( // can ask the model to retry, mirroring the OpenAI provider's behavior, instead // of silently dropping it. func TestStreamCompletionEmitsDroppedOnNamelessToolUseBlock(t *testing.T) { + t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "content_block_start", `{"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"","name":""}}`) writeSSEEvent(w, "content_block_stop", `{"type":"content_block_stop","index":0}`) @@ -45,6 +46,7 @@ func TestStreamCompletionEmitsDroppedOnNamelessToolUseBlock(t *testing.T) { // A well-formed tool_use block must NOT emit a dropped signal. func TestStreamCompletionDoesNotDropValidToolUseBlock(t *testing.T) { + t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "content_block_start", `{"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_1","name":"read_file"}}`) writeSSEEvent(w, "content_block_stop", `{"type":"content_block_stop","index":0}`) diff --git a/internal/providers/anthropic/finish_reason_test.go b/internal/providers/anthropic/finish_reason_test.go index 757d9752b..a84f5eea3 100644 --- a/internal/providers/anthropic/finish_reason_test.go +++ b/internal/providers/anthropic/finish_reason_test.go @@ -11,6 +11,7 @@ import ( // truncated at the output cap. The provider must surface it on the done event so // the agent does not treat a clipped answer as complete. func TestStreamCompletionSurfacesMaxTokensStopReason(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "message_start", `{"type":"message_start","message":{"usage":{"input_tokens":5,"output_tokens":0}}}`) writeSSEEvent(w, "content_block_delta", `{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"cut"}}`) @@ -37,6 +38,7 @@ func TestStreamCompletionSurfacesMaxTokensStopReason(t *testing.T) { // A normal end_turn stop_reason must leave the done event's FinishReason empty. func TestStreamCompletionNormalStopReasonHasNoFinishReason(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "content_block_delta", `{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"ok"}}`) writeSSEEvent(w, "message_delta", `{"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":2}}`) diff --git a/internal/providers/anthropic/idle_test.go b/internal/providers/anthropic/idle_test.go index c8c4c90cf..79b1e629d 100644 --- a/internal/providers/anthropic/idle_test.go +++ b/internal/providers/anthropic/idle_test.go @@ -15,6 +15,7 @@ import ( // message_stop or closing) must abort on the idle timeout instead of blocking // the agent forever. func TestStreamCompletionIdleTimeoutAbortsStalledStream(t *testing.T) { + t.Parallel() released := make(chan struct{}) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "content_block_delta", `{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"hi"}}`) diff --git a/internal/providers/anthropic/image_test.go b/internal/providers/anthropic/image_test.go index 4ecea2b10..96124f821 100644 --- a/internal/providers/anthropic/image_test.go +++ b/internal/providers/anthropic/image_test.go @@ -60,6 +60,7 @@ func firstUserContentBlocks(t *testing.T, body map[string]any) []any { // TestUserTextOnlyTurnUnchanged pins the text-only wire shape: a single // user message whose content is a one-element text-block array. func TestUserTextOnlyTurnUnchanged(t *testing.T) { + t.Parallel() body := captureRequestBody(t, zeroruntime.CompletionRequest{ Messages: []zeroruntime.Message{ {Role: zeroruntime.MessageRoleUser, Content: "Describe this."}, @@ -78,6 +79,7 @@ func TestUserTextOnlyTurnUnchanged(t *testing.T) { // TestUserImagePlusTextTurn asserts a text block followed by one image // source block carrying base64 of the RAW bytes. func TestUserImagePlusTextTurn(t *testing.T) { + t.Parallel() raw := []byte{0x89, 0x50, 0x4e, 0x47, 0x01, 0x02} body := captureRequestBody(t, zeroruntime.CompletionRequest{ Messages: []zeroruntime.Message{ @@ -112,6 +114,7 @@ func TestUserImagePlusTextTurn(t *testing.T) { // TestUserImageOnlyTurnEmits asserts an image-only user turn (empty Content) // still produces a user message with a single image block. func TestUserImageOnlyTurnEmits(t *testing.T) { + t.Parallel() raw := []byte{0xff, 0xd8, 0xff, 0xe0} body := captureRequestBody(t, zeroruntime.CompletionRequest{ Messages: []zeroruntime.Message{ diff --git a/internal/providers/anthropic/provider_test.go b/internal/providers/anthropic/provider_test.go index d35d3ee41..48c1c3ddb 100644 --- a/internal/providers/anthropic/provider_test.go +++ b/internal/providers/anthropic/provider_test.go @@ -9,10 +9,16 @@ import ( "strings" "testing" + "github.com/Gitlawb/zero/internal/providers/providerio" "github.com/Gitlawb/zero/internal/zeroruntime" ) +func init() { + providerio.ShrinkBackoffForTest() +} + func TestStreamCompletionPostsMessagesRequest(t *testing.T) { + t.Parallel() var gotPath string var gotAPIKey string var gotVersion string @@ -123,6 +129,7 @@ func TestStreamCompletionPostsMessagesRequest(t *testing.T) { } func TestStreamCompletionAppliesCustomAuthAndHeaders(t *testing.T) { + t.Parallel() var gotDefaultAuth string var gotCustomAuth string var gotTenant string @@ -165,6 +172,7 @@ func TestStreamCompletionAppliesCustomAuthAndHeaders(t *testing.T) { } func TestStreamCompletionEmitsTextUsageAndDone(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "message_start", `{"type":"message_start","message":{"usage":{"input_tokens":25}}}`) writeSSEEvent(w, "content_block_delta", `{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}`) @@ -186,6 +194,7 @@ func TestStreamCompletionEmitsTextUsageAndDone(t *testing.T) { } func TestStreamCompletionReportsCacheTokens(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "message_start", `{"type":"message_start","message":{"usage":{"input_tokens":10,"cache_read_input_tokens":200,"cache_creation_input_tokens":40}}}`) writeSSEEvent(w, "content_block_delta", `{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"hi"}}`) @@ -217,6 +226,7 @@ func TestStreamCompletionReportsCacheTokens(t *testing.T) { } func TestStreamCompletionEnablesThinkingWhenEffortRequested(t *testing.T) { + t.Parallel() var gotBody map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil { @@ -257,6 +267,7 @@ func TestStreamCompletionEnablesThinkingWhenEffortRequested(t *testing.T) { } func TestStreamCompletionOmitsThinkingWithoutEffort(t *testing.T) { + t.Parallel() var gotBody map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil { @@ -287,6 +298,7 @@ func TestStreamCompletionOmitsThinkingWithoutEffort(t *testing.T) { } func TestStreamCompletionCapturesThinkingBlocksForReplay(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "content_block_start", `{"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}`) writeSSEEvent(w, "content_block_delta", `{"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"Let me think"}}`) @@ -312,6 +324,7 @@ func TestStreamCompletionCapturesThinkingBlocksForReplay(t *testing.T) { } func TestStreamCompletionPreservesUnclosedThinkingBlockAtStreamEnd(t *testing.T) { + t.Parallel() // The SSE ends after thinking_delta/signature_delta but BEFORE the thinking // block's content_block_stop. The open buffer must still be finalized into the // done event's ReasoningBlocks (via closeOpen), or the next Anthropic replay @@ -339,6 +352,7 @@ func TestStreamCompletionPreservesUnclosedThinkingBlockAtStreamEnd(t *testing.T) } func TestAnthropicRequestReplaysThinkingBlocksFirst(t *testing.T) { + t.Parallel() var gotBody map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil { @@ -392,6 +406,7 @@ func TestAnthropicRequestReplaysThinkingBlocksFirst(t *testing.T) { } func TestStreamCompletionEmitsToolUseBlocks(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "content_block_start", `{"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_1","name":"read_file","input":{}}}`) writeSSEEvent(w, "content_block_delta", `{"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"path\":"}}`) @@ -414,6 +429,7 @@ func TestStreamCompletionEmitsToolUseBlocks(t *testing.T) { } func TestStreamCompletionClosesOpenToolCallOnEOF(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "content_block_start", `{"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_1","name":"grep","input":{}}}`) writeSSEEvent(w, "content_block_delta", `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\"pattern\":\"Zero\"}"}}`) @@ -429,6 +445,7 @@ func TestStreamCompletionClosesOpenToolCallOnEOF(t *testing.T) { } func TestStreamCompletionClassifiesHTTPErrorsAndRedactsToken(t *testing.T) { + t.Parallel() cases := []struct { name string status int @@ -464,6 +481,7 @@ func TestStreamCompletionClassifiesHTTPErrorsAndRedactsToken(t *testing.T) { } func TestStreamCompletionEmitsStreamErrorObject(t *testing.T) { + t.Parallel() provider := newTestProviderWithKey(t, "sk-ant", func(w http.ResponseWriter, r *http.Request) { writeSSEEvent(w, "error", `{"type":"error","error":{"message":"stream failed sk-ant","type":"overloaded_error"}}`) }) @@ -481,6 +499,7 @@ func TestStreamCompletionEmitsStreamErrorObject(t *testing.T) { } func TestStreamCompletionRejectsMalformedHistoryBeforeDispatch(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { t.Fatal("provider should not dispatch malformed history") }) @@ -507,6 +526,7 @@ func TestStreamCompletionRejectsMalformedHistoryBeforeDispatch(t *testing.T) { } func TestNewRequiresModelAndPositiveMaxTokens(t *testing.T) { + t.Parallel() if _, err := New(Options{}); err == nil { t.Fatal("New without model returned nil error") } diff --git a/internal/providers/anthropic/stop_reason_test.go b/internal/providers/anthropic/stop_reason_test.go index ecd887b2f..d881acba5 100644 --- a/internal/providers/anthropic/stop_reason_test.go +++ b/internal/providers/anthropic/stop_reason_test.go @@ -7,6 +7,7 @@ import ( ) func TestMapStopReasonRefusal(t *testing.T) { + t.Parallel() if got := mapStopReason("refusal"); got != zeroruntime.FinishReasonContentFilter { t.Errorf("refusal → %q, want content_filter (M4)", got) } diff --git a/internal/providers/factory_turn_session_test.go b/internal/providers/factory_turn_session_test.go index 03b8a8dc7..050f20c33 100644 --- a/internal/providers/factory_turn_session_test.go +++ b/internal/providers/factory_turn_session_test.go @@ -14,6 +14,7 @@ import ( // wraps into the default TurnSessionProvider: construction succeeds, a session // opens, and Compact reports unsupported (the default adapter contract). func TestNewTurnSessionProviderForEveryKind(t *testing.T) { + t.Parallel() kinds := []config.ProviderKind{ config.ProviderKindOpenAI, config.ProviderKindOpenAICompatible, @@ -65,6 +66,7 @@ func TestNewTurnSessionProviderForEveryKind(t *testing.T) { // projects the model-registry entry (context limits, capability flags, // reasoning efforts) into the flat ProviderCapabilities. func TestNewTurnSessionProviderProjectsRegistryCapabilities(t *testing.T) { + t.Parallel() registry, err := modelregistry.NewRegistry([]modelregistry.ModelEntry{{ ID: "pr7-caps-model", DisplayName: "PR7 Capability Probe", @@ -137,6 +139,7 @@ func TestNewTurnSessionProviderProjectsRegistryCapabilities(t *testing.T) { // entry that enumerates no efforts of its own still reports the name-inferred // effective tiers the /effort picker and run-time resolver advertise. func TestNewTurnSessionProviderUsesEffectiveReasoningEfforts(t *testing.T) { + t.Parallel() registry, err := modelregistry.NewRegistry([]modelregistry.ModelEntry{{ // A gpt-5-family id with NO ReasoningEfforts listed: the effective // efforts come from name inference, differing from the raw entry. diff --git a/internal/providers/gemini/done_test.go b/internal/providers/gemini/done_test.go index bf19903fb..074e22576 100644 --- a/internal/providers/gemini/done_test.go +++ b/internal/providers/gemini/done_test.go @@ -10,6 +10,7 @@ import ( // emitDone must mark the shared state done so callers observe it through the // pointer (a by-value receiver would make state.done a dead store). func TestEmitDoneMarksStateDoneThroughPointer(t *testing.T) { + t.Parallel() provider, err := New(Options{Model: "gemini-test"}) if err != nil { t.Fatalf("New returned error: %v", err) diff --git a/internal/providers/gemini/finish_reason_test.go b/internal/providers/gemini/finish_reason_test.go index 088aefc88..5328bcbf2 100644 --- a/internal/providers/gemini/finish_reason_test.go +++ b/internal/providers/gemini/finish_reason_test.go @@ -12,6 +12,7 @@ import ( // MAX_TOKENS to length, and every other non-STOP reason surfaces its raw value // (M3) so the turn is not mistaken for a clean completion. func TestMapFinishReasonNonNormal(t *testing.T) { + t.Parallel() for _, normal := range []string{"", "STOP", "FINISH_REASON_UNSPECIFIED"} { if got := mapFinishReason(normal); got != "" { t.Errorf("%q should be a normal stop (empty), got %q", normal, got) @@ -35,6 +36,7 @@ func TestMapFinishReasonNonNormal(t *testing.T) { // A candidate finishReason of MAX_TOKENS means the response was truncated at the // output cap. The provider must surface it on the done event. func TestStreamCompletionSurfacesMaxTokensFinishReason(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"candidates":[{"content":{"role":"model","parts":[{"text":"cut"}]},"finishReason":"MAX_TOKENS"}]}`) }) @@ -58,6 +60,7 @@ func TestStreamCompletionSurfacesMaxTokensFinishReason(t *testing.T) { // A SAFETY finishReason maps to the runtime's content-filter reason. func TestStreamCompletionSurfacesSafetyFinishReason(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"candidates":[{"content":{"role":"model","parts":[{"text":""}]},"finishReason":"SAFETY"}]}`) }) @@ -81,6 +84,7 @@ func TestStreamCompletionSurfacesSafetyFinishReason(t *testing.T) { // M3 maps it to content_filter. This exercises that fix through the full // SSE → done-event wiring, not just mapFinishReason in isolation. func TestStreamCompletionSurfacesRecitationFinishReason(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"candidates":[{"content":{"role":"model","parts":[{"text":""}]},"finishReason":"RECITATION"}]}`) }) @@ -102,6 +106,7 @@ func TestStreamCompletionSurfacesRecitationFinishReason(t *testing.T) { // A normal STOP finishReason must leave the done event's FinishReason empty. func TestStreamCompletionNormalFinishReasonHasNoReason(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"candidates":[{"content":{"role":"model","parts":[{"text":"ok"}]},"finishReason":"STOP"}]}`) }) @@ -125,6 +130,7 @@ func TestStreamCompletionNormalFinishReasonHasNoReason(t *testing.T) { // signal a dropped tool call (once) so the agent can ask the model to retry, // rather than silently skipping it. func TestStreamCompletionEmitsDroppedOnNamelessFunctionCallPart(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"candidates":[{"content":{"role":"model","parts":[{"functionCall":{"name":"","args":{"a":1}}}]}}]}`) }) @@ -149,6 +155,7 @@ func TestStreamCompletionEmitsDroppedOnNamelessFunctionCallPart(t *testing.T) { // A nameless top-level functionCall must also be signalled as dropped. func TestStreamCompletionEmitsDroppedOnNamelessTopLevelFunctionCall(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"functionCalls":[{"name":"","args":{"a":1}}]}`) }) @@ -173,6 +180,7 @@ func TestStreamCompletionEmitsDroppedOnNamelessTopLevelFunctionCall(t *testing.T // A well-formed functionCall must NOT emit a dropped signal. func TestStreamCompletionDoesNotDropValidFunctionCall(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"candidates":[{"content":{"role":"model","parts":[{"functionCall":{"name":"read_file","args":{"path":"x"}}}]}}]}`) }) diff --git a/internal/providers/gemini/idle_test.go b/internal/providers/gemini/idle_test.go index 2881022cc..9498eb975 100644 --- a/internal/providers/gemini/idle_test.go +++ b/internal/providers/gemini/idle_test.go @@ -14,6 +14,7 @@ import ( // A stalled-but-open Gemini upstream (sends one chunk, then hangs without // closing) must abort on the idle timeout instead of blocking the agent forever. func TestStreamCompletionIdleTimeoutAbortsStalledStream(t *testing.T) { + t.Parallel() released := make(chan struct{}) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"candidates":[{"content":{"parts":[{"text":"hi"}]}}]}`) diff --git a/internal/providers/gemini/images_test.go b/internal/providers/gemini/images_test.go index 678d16291..6c6d5a9a4 100644 --- a/internal/providers/gemini/images_test.go +++ b/internal/providers/gemini/images_test.go @@ -9,6 +9,7 @@ import ( ) func TestGeminiPartTextOnlySerializationOmitsInlineData(t *testing.T) { + t.Parallel() part := geminiPart{Text: "hello"} got, err := json.Marshal(part) if err != nil { @@ -20,6 +21,7 @@ func TestGeminiPartTextOnlySerializationOmitsInlineData(t *testing.T) { } func TestGeminiInlineDataSerialization(t *testing.T) { + t.Parallel() part := geminiPart{InlineData: &geminiInlineData{MimeType: "image/png", Data: "QUJD"}} got, err := json.Marshal(part) if err != nil { @@ -31,6 +33,7 @@ func TestGeminiInlineDataSerialization(t *testing.T) { } func TestMapMessagesTextOnlyUserUnchanged(t *testing.T) { + t.Parallel() _, contents, err := mapMessages([]zeroruntime.Message{ {Role: zeroruntime.MessageRoleUser, Content: "Read the file."}, }) @@ -47,6 +50,7 @@ func TestMapMessagesTextOnlyUserUnchanged(t *testing.T) { } func TestMapMessagesImageAndTextUserTurn(t *testing.T) { + t.Parallel() raw := []byte("ABC") _, contents, err := mapMessages([]zeroruntime.Message{ { @@ -80,6 +84,7 @@ func TestMapMessagesImageAndTextUserTurn(t *testing.T) { } func TestMapMessagesImageOnlyUserTurn(t *testing.T) { + t.Parallel() _, contents, err := mapMessages([]zeroruntime.Message{ { Role: zeroruntime.MessageRoleUser, diff --git a/internal/providers/gemini/provider_test.go b/internal/providers/gemini/provider_test.go index 7c83c7148..c407e8e41 100644 --- a/internal/providers/gemini/provider_test.go +++ b/internal/providers/gemini/provider_test.go @@ -14,6 +14,7 @@ import ( ) func TestStreamCompletionPostsGenerateContentRequest(t *testing.T) { + t.Parallel() var gotPath string var gotQuery string var gotAPIKey string @@ -118,6 +119,7 @@ func TestStreamCompletionPostsGenerateContentRequest(t *testing.T) { } func TestStreamCompletionEnablesThinkingWhenEffortRequested(t *testing.T) { + t.Parallel() var gotBody map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil { @@ -151,6 +153,7 @@ func TestStreamCompletionEnablesThinkingWhenEffortRequested(t *testing.T) { } func TestStreamCompletionOmitsThinkingWithoutEffort(t *testing.T) { + t.Parallel() var gotBody map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil { @@ -179,6 +182,7 @@ func TestStreamCompletionOmitsThinkingWithoutEffort(t *testing.T) { } func TestStreamCompletionCapturesThoughtSignatureAndSkipsThoughtText(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { // A thought-summary part (must not surface as answer text) followed by a // functionCall part carrying its thoughtSignature. @@ -201,6 +205,7 @@ func TestStreamCompletionCapturesThoughtSignatureAndSkipsThoughtText(t *testing. } func TestGeminiRequestReplaysThoughtSignature(t *testing.T) { + t.Parallel() var gotBody map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil { @@ -238,6 +243,7 @@ func TestGeminiRequestReplaysThoughtSignature(t *testing.T) { } func TestStreamCompletionAppliesCustomAuthAndHeaders(t *testing.T) { + t.Parallel() var gotDefaultAuth string var gotCustomAuth string var gotTenant string @@ -280,6 +286,7 @@ func TestStreamCompletionAppliesCustomAuthAndHeaders(t *testing.T) { } func TestStreamCompletionEmitsTextUsageAndReasoningTokens(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"candidates":[{"content":{"parts":[{"text":"Hello"}]}}]}`) writeSSE(w, `{"candidates":[{"content":{"parts":[{"text":" Zero"}]}}],"usageMetadata":{"promptTokenCount":25,"candidatesTokenCount":15,"thoughtsTokenCount":3,"cachedContentTokenCount":7}}`) @@ -298,6 +305,7 @@ func TestStreamCompletionEmitsTextUsageAndReasoningTokens(t *testing.T) { } func TestStreamCompletionEmitsCandidateFunctionCalls(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"candidates":[{"content":{"parts":[{"functionCall":{"id":"call_1","name":"read_file","args":{"path":"src/index.ts"}}},{"functionCall":{"id":"call_2","name":"grep","args":{"pattern":"Zero"}}}]}}]}`) }) @@ -318,6 +326,7 @@ func TestStreamCompletionEmitsCandidateFunctionCalls(t *testing.T) { } func TestStreamCompletionEmitsTopLevelFunctionCalls(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"functionCalls":[{"id":"call_1","name":"read_file","args":{"path":"README.md"}}]}`) }) @@ -335,6 +344,7 @@ func TestStreamCompletionEmitsTopLevelFunctionCalls(t *testing.T) { } func TestStreamCompletionUsesSyntheticToolIDsWhenMissing(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"functionCalls":[{"name":"grep","args":{"pattern":"Zero"}}]}`) }) @@ -346,6 +356,7 @@ func TestStreamCompletionUsesSyntheticToolIDsWhenMissing(t *testing.T) { } func TestStreamCompletionClassifiesHTTPAndPromptBlockErrors(t *testing.T) { + t.Parallel() authProvider := newTestProviderWithKey(t, "sk-google", func(w http.ResponseWriter, r *http.Request) { http.Error(w, `{"error":{"message":"API key not valid"}}`, http.StatusUnauthorized) }) @@ -373,6 +384,7 @@ func TestStreamCompletionClassifiesHTTPAndPromptBlockErrors(t *testing.T) { // A 401 with an OAuth resolver is retried once with a force-refreshed token; the // replayed request carries the refreshed bearer and succeeds. func TestStreamCompletionRetries401WithRefreshedToken(t *testing.T) { + t.Parallel() var attempts int var secondAuth string server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -422,6 +434,7 @@ func TestStreamCompletionRetries401WithRefreshedToken(t *testing.T) { } func TestStreamCompletionEmitsStreamErrorObject(t *testing.T) { + t.Parallel() provider := newTestProviderWithKey(t, "sk-google", func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"error":{"code":429,"message":"stream failed sk-google","status":"RESOURCE_EXHAUSTED"}}`) }) @@ -439,6 +452,7 @@ func TestStreamCompletionEmitsStreamErrorObject(t *testing.T) { } func TestStreamCompletionStopsOnMalformedStreamToolArgs(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"functionCalls":[{"id":"call_1","name":"grep","args":"raw"}]}`) writeSSE(w, `{"candidates":[{"content":{"parts":[{"text":"should not emit"}]}}]}`) @@ -457,6 +471,7 @@ func TestStreamCompletionStopsOnMalformedStreamToolArgs(t *testing.T) { } func TestStreamCompletionRejectsMalformedHistoryBeforeDispatch(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { t.Fatal("provider should not dispatch malformed history") }) @@ -483,6 +498,7 @@ func TestStreamCompletionRejectsMalformedHistoryBeforeDispatch(t *testing.T) { } func TestNewRequiresModelAndPositiveMaxTokens(t *testing.T) { + t.Parallel() if _, err := New(Options{}); err == nil { t.Fatal("New without model returned nil error") } @@ -562,6 +578,7 @@ func eventsOfType(events []zeroruntime.StreamEvent, eventType zeroruntime.Stream // OpenAI-ism (additionalProperties, $schema, patternProperties) survives at any // depth while legitimate fields (type/description/enum/required/default) stay. func TestSanitizeGeminiSchemaStripsUnsupportedFields(t *testing.T) { + t.Parallel() in := map[string]any{ "type": "object", "additionalProperties": false, @@ -634,6 +651,7 @@ func assertNoAdditionalProps(t *testing.T, node any, path string) { // ("Unknown name additionalProperties"), which broke all tool-using exec calls // against Google (issue #373). func TestGeminiRequestOmitsAdditionalPropertiesInToolSchema(t *testing.T) { + t.Parallel() var gotBody map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil { diff --git a/internal/providers/openai/codex_terminal_test.go b/internal/providers/openai/codex_terminal_test.go index 645483a48..f84720e5d 100644 --- a/internal/providers/openai/codex_terminal_test.go +++ b/internal/providers/openai/codex_terminal_test.go @@ -10,6 +10,7 @@ import ( ) func TestToolCallKeyOutputIndexZero(t *testing.T) { + t.Parallel() p := &CodexProvider{} zero, two := 0, 2 // output_index 0 with no item_id must produce a key (it was dropped before M1). @@ -148,6 +149,7 @@ func TestCodexPreservesCustomApplyPatchFunction(t *testing.T) { } func TestHandleTerminalResponseNilPayload(t *testing.T) { + t.Parallel() p := &CodexProvider{} // response.failed with no Response payload must emit an error, not a silent done. @@ -180,6 +182,7 @@ func TestHandleTerminalResponseNilPayload(t *testing.T) { } func TestHandleTerminalResponseFailedPayloadWithoutError(t *testing.T) { + t.Parallel() p := &CodexProvider{} // A response.failed carrying a payload whose error object is null/omitted (the diff --git a/internal/providers/openai/codex_test.go b/internal/providers/openai/codex_test.go index 535993d56..2d61d4158 100644 --- a/internal/providers/openai/codex_test.go +++ b/internal/providers/openai/codex_test.go @@ -82,6 +82,7 @@ func drainCodexEvents(t *testing.T, stream <-chan zeroruntime.StreamEvent) { } func TestCodexProviderSetsExpectedHeaders(t *testing.T) { + t.Parallel() var rec codexRequest srv := newCodexTestServer(t, &rec) defer srv.Close() @@ -130,6 +131,7 @@ func TestCodexProviderSetsExpectedHeaders(t *testing.T) { } func TestCodexProviderUsesConfiguredBaseURL(t *testing.T) { + t.Parallel() var rec codexRequest // Run the test server on a custom path to confirm the request goes // through {BaseURL}/responses, not a hard-coded host. @@ -175,6 +177,7 @@ func TestCodexProviderUsesConfiguredBaseURL(t *testing.T) { } func TestCodexProviderDefaultsOriginator(t *testing.T) { + t.Parallel() var rec codexRequest srv := newCodexTestServer(t, &rec) defer srv.Close() @@ -204,6 +207,7 @@ func TestCodexProviderDefaultsOriginator(t *testing.T) { } func TestCodexProviderBrandsUserAgent(t *testing.T) { + t.Parallel() var rec codexRequest srv := newCodexTestServer(t, &rec) defer srv.Close() @@ -236,6 +240,7 @@ func TestCodexProviderBrandsUserAgent(t *testing.T) { } func TestCodexProviderAccountResolverIsUsedWhenAccountIDEmpty(t *testing.T) { + t.Parallel() var rec codexRequest srv := newCodexTestServer(t, &rec) defer srv.Close() @@ -272,6 +277,7 @@ func TestCodexProviderAccountResolverIsUsedWhenAccountIDEmpty(t *testing.T) { } func TestCodexProviderResolverIsConsultedOnEveryRequest(t *testing.T) { + t.Parallel() // The factory wires the AccountResolver from the OAuth store so a refresh // that updates the stored token's Account field takes effect on the next // outgoing request — not just the first. This test asserts the resolver @@ -354,6 +360,7 @@ func TestCodexProviderResolverIsConsultedOnEveryRequest(t *testing.T) { } func TestCodexProviderOmitsAccountIDWhenResolverSaysNo(t *testing.T) { + t.Parallel() var rec codexRequest srv := newCodexTestServer(t, &rec) defer srv.Close() @@ -384,6 +391,7 @@ func TestCodexProviderOmitsAccountIDWhenResolverSaysNo(t *testing.T) { } func TestCodexProviderSendsResponsesRequestShape(t *testing.T) { + t.Parallel() // The Codex provider speaks the Responses API, not the chat-completions // API. The request body must carry top-level `instructions`, `input` items // (not `messages`), a `stream: true` flag, and `tools` (when the caller @@ -455,6 +463,7 @@ func TestCodexProviderSendsResponsesRequestShape(t *testing.T) { } func TestCodexProviderForwardsReasoningEffort(t *testing.T) { + t.Parallel() // A reasoning effort must reach the Responses backend nested under // `reasoning.effort` (where the chat-completions `reasoning_effort` moved). // Without this the user's chosen effort is silently dropped for Codex models. @@ -493,6 +502,7 @@ func TestCodexProviderForwardsReasoningEffort(t *testing.T) { } func TestCodexProviderNormalizesServiceTier(t *testing.T) { + t.Parallel() for _, test := range []struct { name string input string @@ -536,6 +546,7 @@ func TestCodexProviderNormalizesServiceTier(t *testing.T) { } func TestCodexProviderStreamsReasoningSummaryDeltas(t *testing.T) { + t.Parallel() // reasoning_summary_text deltas must surface as StreamEventReasoning (live // "thinking"), in order, alongside the normal text output. Without this a long // reasoning phase produces zero visible output and reads as a hang. @@ -583,6 +594,7 @@ func TestCodexProviderStreamsReasoningSummaryDeltas(t *testing.T) { } func TestCodexProviderOmitsReasoningWhenUnset(t *testing.T) { + t.Parallel() // No effort (and unsupported values) must omit the `reasoning` field entirely // rather than send an empty object, which the backend would reject. var rec codexRequest @@ -611,6 +623,7 @@ func TestCodexProviderOmitsReasoningWhenUnset(t *testing.T) { } func TestCodexProviderRetriesHeadersAfter401(t *testing.T) { + t.Parallel() var hits atomic.Int32 var rec1, rec2 codexRequest mux := http.NewServeMux() @@ -685,6 +698,7 @@ func TestCodexProviderRetriesHeadersAfter401(t *testing.T) { } func TestCodexProviderRequiresBaseURL(t *testing.T) { + t.Parallel() // An empty baseURL falls back to the openai provider's default // (https://api.openai.com/v1). The Codex provider is designed to be // wired by the factory with the catalog's Codex baseURL, so the @@ -704,6 +718,7 @@ func TestCodexProviderRequiresBaseURL(t *testing.T) { } func TestCodexProviderRejectsBadBaseURL(t *testing.T) { + t.Parallel() _, err := NewCodexProvider(CodexOptions{ Options: Options{APIKey: "sk", Model: "gpt-5", BaseURL: "://not a url"}, AccountID: "acc-x", @@ -717,6 +732,7 @@ func TestCodexProviderRejectsBadBaseURL(t *testing.T) { } func TestValidateAccount(t *testing.T) { + t.Parallel() if err := ValidateAccount(""); err == nil { t.Fatal("empty account id should be rejected") } @@ -729,9 +745,9 @@ func TestValidateAccount(t *testing.T) { } func TestCodexProviderStreamIdleTimeoutPropagates(t *testing.T) { - // Sanity check: the wrapped openai provider's StreamIdleTimeout flows - // through. The default is 90s; we override to a small value so a real - // hang surfaces in the test. + // Serial: StreamIdleTimeout arms a real wall-clock idle timer. Under + // t.Parallel() the 50ms watchdog can fire before response.completed is + // consumed. This test only checks that the option is accepted. var rec codexRequest srv := newCodexTestServer(t, &rec) defer srv.Close() @@ -760,6 +776,7 @@ func TestCodexProviderStreamIdleTimeoutPropagates(t *testing.T) { } func TestCodexProviderParsesResponsesTextDeltas(t *testing.T) { + t.Parallel() // The Codex backend streams text as a series of // `response.output_text.delta` events. Each delta must surface as a // runtime text event in order, and the terminal `response.completed` @@ -818,6 +835,7 @@ func TestCodexProviderParsesResponsesTextDeltas(t *testing.T) { } func TestCodexProviderParsesResponsesToolCalls(t *testing.T) { + t.Parallel() // The Codex backend streams function calls as three coordinated event // types: `response.output_item.added` (carries the call id and name), // `response.function_call_arguments.delta` (one or more events that @@ -886,6 +904,7 @@ func TestCodexProviderParsesResponsesToolCalls(t *testing.T) { } func TestCodexProviderSendsAssistantToolCallsAsInputItems(t *testing.T) { + t.Parallel() // When the runtime replays a prior assistant turn that issued a tool // call, the Codex provider must serialize the turn as BOTH the // assistant message (its text) AND a function_call item for the call. @@ -957,6 +976,7 @@ func TestCodexProviderSendsAssistantToolCallsAsInputItems(t *testing.T) { } func TestCodexProviderEmitsErrorOnResponseErrorEvent(t *testing.T) { + t.Parallel() // A `response.error` event from the Codex backend is the stream-level // error signal. It must surface as a single StreamEventError and stop // the scan so the runtime doesn't hang waiting for a completion. @@ -995,6 +1015,7 @@ func TestCodexProviderEmitsErrorOnResponseErrorEvent(t *testing.T) { } func TestCodexProviderEmitsErrorOnMalformedStream(t *testing.T) { + t.Parallel() // A non-JSON data payload (or a payload with a missing `type` field) // must be reported as a stream error rather than silently dropped — // otherwise the runtime would hang waiting for a completion event @@ -1031,6 +1052,7 @@ func TestCodexProviderEmitsErrorOnMalformedStream(t *testing.T) { } func TestCodexProviderEmitsLengthFinishWhenStreamEndsWithoutCompletion(t *testing.T) { + t.Parallel() // The Codex backend may close the SSE stream without emitting // `response.completed` (e.g. an internal truncation). The wrapper // must surface a StreamEventDone with FinishReasonLength so the diff --git a/internal/providers/openai/provider_test.go b/internal/providers/openai/provider_test.go index 8f1e817a0..a59e1a634 100644 --- a/internal/providers/openai/provider_test.go +++ b/internal/providers/openai/provider_test.go @@ -11,10 +11,16 @@ import ( "testing" "time" + "github.com/Gitlawb/zero/internal/providers/providerio" "github.com/Gitlawb/zero/internal/zeroruntime" ) +func init() { + providerio.ShrinkBackoffForTest() +} + func TestStreamCompletionPostsChatCompletionRequest(t *testing.T) { + t.Parallel() var gotPath string var gotAuth string var gotUserAgent string @@ -120,6 +126,7 @@ func TestStreamCompletionPostsChatCompletionRequest(t *testing.T) { // "properties":null, because strict OpenAI-compatible servers (LM Studio) // reject the null form. func TestStreamCompletionSerializesTypedNilPropertiesAsEmptyObject(t *testing.T) { + t.Parallel() var gotBody map[string]any var gotRaw []byte server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -175,6 +182,7 @@ func TestStreamCompletionSerializesTypedNilPropertiesAsEmptyObject(t *testing.T) } func TestNewRequiresModelButNotAPIKey(t *testing.T) { + t.Parallel() if _, err := New(Options{}); err == nil { t.Fatal("New without model returned nil error") } @@ -184,6 +192,7 @@ func TestNewRequiresModelButNotAPIKey(t *testing.T) { } func TestStreamCompletionOmitsAuthAndToolsWhenEmpty(t *testing.T) { + t.Parallel() var gotAuth string var gotBody map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -216,6 +225,7 @@ func TestStreamCompletionOmitsAuthAndToolsWhenEmpty(t *testing.T) { } func TestStreamCompletionAppliesCustomAuthAndHeaders(t *testing.T) { + t.Parallel() var gotAuth string var gotAltAuth string var gotReferer string @@ -258,6 +268,7 @@ func TestStreamCompletionAppliesCustomAuthAndHeaders(t *testing.T) { } func TestStreamCompletionEmitsTextUsageAndDone(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"content":"hello "}}]}`) writeSSE(w, `{"choices":[{"delta":{"content":"zero"}}],"usage":{"prompt_tokens":12,"completion_tokens":5,"prompt_tokens_details":{"cached_tokens":3,"cache_write_tokens":2}}}`) @@ -276,6 +287,7 @@ func TestStreamCompletionEmitsTextUsageAndDone(t *testing.T) { } func TestStreamCompletionEmitsReasoningContentDeltas(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"reasoning_content":"Thinking. "}}]}`) writeSSE(w, `{"choices":[{"delta":{"reasoning_content":"Answering now."}}]}`) @@ -296,6 +308,7 @@ func TestStreamCompletionEmitsReasoningContentDeltas(t *testing.T) { } func TestStreamCompletionEmitsReasoningAliasDeltas(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"reasoning":"Thinking. "}}]}`) writeSSE(w, `{"choices":[{"delta":{"reasoning":"Answering now."}}]}`) @@ -316,6 +329,7 @@ func TestStreamCompletionEmitsReasoningAliasDeltas(t *testing.T) { } func TestStreamCompletionPrefersReasoningContentOverAlias(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"reasoning_content":"standard","reasoning":"alias"}}]}`) writeSSE(w, `[DONE]`) @@ -329,6 +343,7 @@ func TestStreamCompletionPrefersReasoningContentOverAlias(t *testing.T) { } func TestStreamCompletionEmitsReasoningBeforeRegularContent(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"reasoning_content":"Thinking. ","content":"Answer."}}]}`) writeSSE(w, `[DONE]`) @@ -346,6 +361,7 @@ func TestStreamCompletionEmitsReasoningBeforeRegularContent(t *testing.T) { } func TestStreamCompletionPreservesLiteralThinkTagsByDefault(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"content":"show literal markup"}}]}`) writeSSE(w, `[DONE]`) @@ -359,6 +375,7 @@ func TestStreamCompletionPreservesLiteralThinkTagsByDefault(t *testing.T) { } func TestStreamCompletionSplitsInlineThinkTagsFromContent(t *testing.T) { + t.Parallel() provider := newTestProviderWithThinkTags(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"content":"private reasoningpublic answer"}}]}`) writeSSE(w, `[DONE]`) @@ -373,6 +390,7 @@ func TestStreamCompletionSplitsInlineThinkTagsFromContent(t *testing.T) { } func TestStreamCompletionSplitsInlineThinkTagsAcrossChunks(t *testing.T) { + t.Parallel() provider := newTestProviderWithThinkTags(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"content":"reason"}}]}`) @@ -393,6 +411,7 @@ func TestStreamCompletionSplitsInlineThinkTagsAcrossChunks(t *testing.T) { } func TestStreamCompletionBuffersToolArgsUntilIDAndNameArrive(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"path\":"}}]}}]}`) writeSSE(w, `{"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"read_file","arguments":"\"README.md\"}"}}]},"finish_reason":"tool_calls"}]}`) @@ -418,6 +437,7 @@ func TestStreamCompletionBuffersToolArgsUntilIDAndNameArrive(t *testing.T) { } func TestStreamCompletionTracksMultipleToolCallsByIndex(t *testing.T) { + t.Parallel() provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{\"query\":"}},{"index":0,"id":"call_a","function":{"name":"read_file","arguments":"{\"path\":\"a\"}"}}]}}]}`) writeSSE(w, `{"choices":[{"delta":{"tool_calls":[{"index":1,"id":"call_b","function":{"name":"grep","arguments":"\"zero\"}"}}]},"finish_reason":"tool_calls"}]}`) @@ -440,6 +460,7 @@ func TestStreamCompletionTracksMultipleToolCallsByIndex(t *testing.T) { } func TestStreamCompletionClassifiesHTTPErrorsAndRedactsToken(t *testing.T) { + t.Parallel() cases := []struct { name string status int @@ -480,6 +501,7 @@ func TestStreamCompletionClassifiesHTTPErrorsAndRedactsToken(t *testing.T) { } func TestStreamCompletionHumanizesUpstreamUnreachableGatewayError(t *testing.T) { + t.Parallel() // A local Ollama daemon serving a "-cloud" model answers on localhost but // returns HTTP 502 with an opaque proxied transport error when it cannot reach // its cloud backend. The adapter must surface a clear connectivity message @@ -507,6 +529,7 @@ func TestStreamCompletionHumanizesUpstreamUnreachableGatewayError(t *testing.T) } func TestStreamCompletionEmitsStreamErrorObject(t *testing.T) { + t.Parallel() provider := newTestProviderWithKey(t, "sk-secret", func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"error":{"message":"stream failed sk-secret","type":"server_error"}}`) }) @@ -524,6 +547,7 @@ func TestStreamCompletionEmitsStreamErrorObject(t *testing.T) { } func TestStreamCompletionClassifiesStreamErrorCode(t *testing.T) { + t.Parallel() // The error arrives inside a 200 OK SSE payload's "code" field, not the // HTTP status, so this exercises openAIStreamErrorStatusByCode directly: // both the numeric-string codes some providers send and the semantic @@ -575,6 +599,7 @@ func TestStreamCompletionEmitsErrorForMalformedJSON(t *testing.T) { } func TestStreamCompletionEmitsErrorWhenContextCancels(t *testing.T) { + t.Parallel() requestStarted := make(chan struct{}) release := make(chan struct{}) provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { @@ -605,6 +630,7 @@ func TestStreamCompletionEmitsErrorWhenContextCancels(t *testing.T) { // caller timeout, NOT humanized into an "upstream unreachable" outage (the host // is reachable; the caller's clock ran out). func TestStreamCompletionContextDeadlineNotHumanizedAsUpstream(t *testing.T) { + t.Parallel() release := make(chan struct{}) defer close(release) provider := newTestProvider(t, func(w http.ResponseWriter, r *http.Request) { @@ -631,6 +657,7 @@ func TestStreamCompletionContextDeadlineNotHumanizedAsUpstream(t *testing.T) { } func TestStreamCompletionFlushesBufferedContentWhenContextCancels(t *testing.T) { + t.Parallel() release := make(chan struct{}) provider := newTestProviderWithThinkTags(t, func(w http.ResponseWriter, r *http.Request) { writeSSE(w, `{"choices":[{"delta":{"content":"visible