diff --git a/internal/proxy/bodydispatch_test.go b/internal/proxy/bodydispatch_test.go index c9db4ea..a00f93b 100644 --- a/internal/proxy/bodydispatch_test.go +++ b/internal/proxy/bodydispatch_test.go @@ -76,7 +76,10 @@ func TestBodyBasedDispatch(t *testing.T) { reasoningCalled = false ornithCalled = false requestBody := `{"model":"reasoning","messages":[{"role":"user","content":"hi"}]}` - resp, _ := http.Post(server.URL+"/v1/chat/completions", "application/json", strings.NewReader(requestBody)) + resp, err := http.Post(server.URL+"/v1/chat/completions", "application/json", strings.NewReader(requestBody)) + if err != nil { + t.Fatalf("request failed: %v", err) + } resp.Body.Close() if !reasoningCalled { @@ -151,7 +154,10 @@ func TestBodyPreservedUnmodified(t *testing.T) { // Send a request with specific body content originalBody := `{"model":"reasoning","stream":true,"messages":[{"role":"user","content":"hello world"}],"temperature":0.7}` - resp, _ := http.Post(server.URL+"/v1/chat/completions", "application/json", strings.NewReader(originalBody)) + resp, err := http.Post(server.URL+"/v1/chat/completions", "application/json", strings.NewReader(originalBody)) + if err != nil { + t.Fatalf("request failed: %v", err) + } resp.Body.Close() if string(receivedBody) != originalBody { @@ -249,7 +255,10 @@ func TestUnknownModelReject(t *testing.T) { defer server.Close() requestBody := `{"model":"unknown-model","messages":[]}` - resp, _ := http.Post(server.URL+"/v1/chat/completions", "application/json", strings.NewReader(requestBody)) + resp, err := http.Post(server.URL+"/v1/chat/completions", "application/json", strings.NewReader(requestBody)) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode != http.StatusBadRequest { @@ -300,7 +309,10 @@ func TestMissingModelField(t *testing.T) { defer server.Close() requestBody := `{"messages":[]}` - resp, _ := http.Post(server.URL+"/v1/chat/completions", "application/json", strings.NewReader(requestBody)) + resp, err := http.Post(server.URL+"/v1/chat/completions", "application/json", strings.NewReader(requestBody)) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode != http.StatusBadRequest { diff --git a/internal/proxy/bodysize_test.go b/internal/proxy/bodysize_test.go index cef32d7..d84c063 100644 --- a/internal/proxy/bodysize_test.go +++ b/internal/proxy/bodysize_test.go @@ -323,7 +323,10 @@ func TestBodySizeCapRejectionLogged(t *testing.T) { // Send an oversized body body := strings.Repeat("a", int(maxBodySize)+1) - resp, _ := http.Post(server.URL+"/test", "text/plain", strings.NewReader(body)) + resp, err := http.Post(server.URL+"/test", "text/plain", strings.NewReader(body)) + if err != nil { + t.Fatalf("request failed: %v", err) + } resp.Body.Close() // Verify rejection status diff --git a/internal/proxy/embeddings_and_rerank_test.go b/internal/proxy/embeddings_and_rerank_test.go index e9978d9..c99a482 100644 --- a/internal/proxy/embeddings_and_rerank_test.go +++ b/internal/proxy/embeddings_and_rerank_test.go @@ -45,11 +45,14 @@ func TestEmbeddingsPassthroughNoRewrite(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/embeddings", "application/json", bytes.NewReader([]byte(`{"model":"nomic-ai/nomic-embed-text-v2-moe","input":"hello"}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if !embeddingsCalled { @@ -91,11 +94,14 @@ func TestEmbeddingsResponsePassthrough(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/embeddings", "application/json", bytes.NewReader([]byte(`{"model":"nomic-ai/nomic-embed-text-v2-moe","input":"test"}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) @@ -135,11 +141,14 @@ func TestRerankPathRewrite(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/rerank", "application/json", bytes.NewReader([]byte(`{"model":"BAAI/bge-reranker-base","query":"test","texts":["a","b"]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() // Verify path IS rewritten to /rerank @@ -177,11 +186,14 @@ func TestRerankResponsePassthrough(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/rerank", "application/json", bytes.NewReader([]byte(`{"model":"BAAI/bge-reranker-base","query":"q","texts":["a"]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) @@ -209,11 +221,14 @@ func TestEmbeddingsUnknownModel(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/embeddings", "application/json", bytes.NewReader([]byte(`{"model":"unknown-embeddings","input":"test"}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode != http.StatusBadRequest { @@ -244,11 +259,14 @@ func TestRerankerUnknownModel(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/rerank", "application/json", bytes.NewReader([]byte(`{"model":"unknown-reranker","query":"q","texts":["a"]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode != http.StatusBadRequest { @@ -288,11 +306,14 @@ func TestEmbeddingsBodyForwarded(t *testing.T) { defer server.Close() originalBody := `{"model":"nomic-ai/nomic-embed-text-v2-moe","input":"test data with special chars: \u0001"}` - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/embeddings", "application/json", bytes.NewReader([]byte(originalBody)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } resp.Body.Close() // The received body should match the original (though may have different formatting) @@ -337,11 +358,14 @@ func TestRerankerBodyForwarded(t *testing.T) { defer server.Close() originalBody := `{"model":"BAAI/bge-reranker-base","query":"test","texts":["a","b","c"]}` - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/rerank", "application/json", bytes.NewReader([]byte(originalBody)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } resp.Body.Close() var orig, received map[string]interface{} @@ -380,11 +404,14 @@ func TestUpstreamErrorStatusSurfaced(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/embeddings", "application/json", bytes.NewReader([]byte(`{"model":"nomic-ai/nomic-embed-text-v2-moe","input":"test"}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode != http.StatusInternalServerError { diff --git a/internal/proxy/headers_test.go b/internal/proxy/headers_test.go index 4baabe1..c5a1b3c 100644 --- a/internal/proxy/headers_test.go +++ b/internal/proxy/headers_test.go @@ -71,7 +71,7 @@ func TestHeaderHygiene(t *testing.T) { t.Errorf("hop-by-hop header %s should be stripped, but found: %s", header, receivedHeaders.Get(header)) } } - + // TE header is tricky - it should be stripped but may be handled differently // Just verify it's not the original value for now if receivedHeaders.Get("TE") == "trailers" { diff --git a/internal/proxy/models_endpoint_test.go b/internal/proxy/models_endpoint_test.go index 3b6c081..487d7b9 100644 --- a/internal/proxy/models_endpoint_test.go +++ b/internal/proxy/models_endpoint_test.go @@ -74,7 +74,10 @@ func TestModelsEndpointContentType(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Get(server.URL + "/v1/models") + resp, err := http.Get(server.URL + "/v1/models") + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() ct := resp.Header.Get("Content-Type") @@ -101,7 +104,10 @@ func TestModelsEndpointResponseShape(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Get(server.URL + "/v1/models") + resp, err := http.Get(server.URL + "/v1/models") + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() var result ModelListResponse @@ -161,7 +167,10 @@ func TestModelsEndpointEnumeratesAllModels(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Get(server.URL + "/v1/models") + resp, err := http.Get(server.URL + "/v1/models") + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() var result ModelListResponse @@ -211,7 +220,10 @@ func TestModelsEndpointHasRequiredFields(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Get(server.URL + "/v1/models") + resp, err := http.Get(server.URL + "/v1/models") + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() var result ModelListResponse @@ -292,7 +304,10 @@ func TestModelsEndpointDerivedFromConfig(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Get(server.URL + "/v1/models") + resp, err := http.Get(server.URL + "/v1/models") + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() var result ModelListResponse @@ -347,18 +362,24 @@ func TestModelsEndpointConsistentWithDispatch(t *testing.T) { defer server.Close() // Get list of models - resp, _ := http.Get(server.URL + "/v1/models") + resp, err := http.Get(server.URL + "/v1/models") + if err != nil { + t.Fatalf("request failed: %v", err) + } var result ModelListResponse json.NewDecoder(resp.Body).Decode(&result) resp.Body.Close() // Try to dispatch to each advertised model for _, model := range result.Data { - dispatchResp, _ := http.Post( + dispatchResp, err := http.Post( server.URL+"/v1/chat/completions", "application/json", strings.NewReader(`{"model":"`+model.ID+`","messages":[]}`), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer dispatchResp.Body.Close() // Should not return 400 (unknown model error) @@ -394,14 +415,20 @@ func TestModelsEndpointResponseIsConsistent(t *testing.T) { defer server.Close() // Call endpoint twice - resp1, _ := http.Get(server.URL + "/v1/models") + resp1, err := http.Get(server.URL + "/v1/models") + if err != nil { + t.Fatalf("request failed: %v", err) + } var result1 ModelListResponse json.NewDecoder(resp1.Body).Decode(&result1) resp1.Body.Close() time.Sleep(10 * time.Millisecond) - resp2, _ := http.Get(server.URL + "/v1/models") + resp2, err := http.Get(server.URL + "/v1/models") + if err != nil { + t.Fatalf("request failed: %v", err) + } var result2 ModelListResponse json.NewDecoder(resp2.Body).Decode(&result2) resp2.Body.Close() diff --git a/internal/proxy/proxy_test.go b/internal/proxy/proxy_test.go index f0faa7e..d7eaa5e 100644 --- a/internal/proxy/proxy_test.go +++ b/internal/proxy/proxy_test.go @@ -105,7 +105,9 @@ func TestProxyPathRewrite(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, err := http.Get(server.URL + "/v1/models") + // Not /v1/models: ServeHTTP serves that endpoint from config (task 2.5) + // and returns before routing, so it never exercises PathRewrite. + resp, err := http.Get(server.URL + "/some/path") if err != nil { t.Fatalf("request failed: %v", err) } @@ -146,7 +148,7 @@ func TestProxyConnectionReuse(t *testing.T) { defer handler.Close() // Verify connection reuse by checking that the same transport is used - + // We can't easily count raw TCP connections in this test setup, // but we can verify that the transport is being reused by checking // that the same transport handles both requests @@ -402,7 +404,10 @@ func TestProxyPreservesBody(t *testing.T) { defer server.Close() testBody := `{"model": "test", "messages": []}` - resp, _ := http.Post(server.URL+"/test", "application/json", strings.NewReader(testBody)) + resp, err := http.Post(server.URL+"/test", "application/json", strings.NewReader(testBody)) + if err != nil { + t.Fatalf("request failed: %v", err) + } resp.Body.Close() if receivedBody != testBody { diff --git a/internal/proxy/timeout_test.go b/internal/proxy/timeout_test.go index 5d0be9f..c3940bd 100644 --- a/internal/proxy/timeout_test.go +++ b/internal/proxy/timeout_test.go @@ -102,7 +102,10 @@ func TestReadTimeout(t *testing.T) { defer server.Close() start := time.Now() - resp, _ := http.Get(server.URL + "/test") + resp, err := http.Get(server.URL + "/test") + if err != nil { + t.Fatalf("request failed: %v", err) + } elapsed := time.Since(start) // Should timeout around the read timeout (with some tolerance) diff --git a/internal/proxy/toolcall_test.go b/internal/proxy/toolcall_test.go index 6425ed4..e487b5c 100644 --- a/internal/proxy/toolcall_test.go +++ b/internal/proxy/toolcall_test.go @@ -16,8 +16,8 @@ import ( // OpenAI-style tool definition type Tool struct { - Type string `json:"type"` - Function ToolFunction `json:"function"` + Type string `json:"type"` + Function ToolFunction `json:"function"` } type ToolFunction struct { @@ -28,21 +28,21 @@ type ToolFunction struct { // OpenAI chat completion with tools request type ChatCompletionRequest struct { - Model string `json:"model"` - Messages []Message `json:"messages"` - Tools []Tool `json:"tools,omitempty"` - Stream bool `json:"stream,omitempty"` + Model string `json:"model"` + Messages []Message `json:"messages"` + Tools []Tool `json:"tools,omitempty"` + Stream bool `json:"stream,omitempty"` } type Message struct { - Role string `json:"role"` - Content interface{} `json:"content"` - ToolCalls []ToolCall `json:"tool_calls,omitempty"` + Role string `json:"role"` + Content interface{} `json:"content"` + ToolCalls []ToolCall `json:"tool_calls,omitempty"` } type ToolCall struct { - ID string `json:"id"` - Type string `json:"type"` + ID string `json:"id"` + Type string `json:"type"` Function FunctionCall `json:"function"` } @@ -64,21 +64,21 @@ func TestToolCallOpenAIStyle(t *testing.T) { w.WriteHeader(http.StatusOK) response := map[string]interface{}{ - "id": "chatcmpl-123", - "object": "chat.completion", - "model": "reasoning", + "id": "chatcmpl-123", + "object": "chat.completion", + "model": "reasoning", "choices": []map[string]interface{}{ { "index": 0, "message": map[string]interface{}{ - "role": "assistant", + "role": "assistant", "content": nil, "tool_calls": []map[string]interface{}{ { - "id": "call_abc123", + "id": "call_abc123", "type": "function", "function": map[string]interface{}{ - "name": "get_weather", + "name": "get_weather", "arguments": `{"location":"San Francisco","unit":"celsius"}`, }, }, @@ -372,10 +372,10 @@ func TestToolCallMultiTurn(t *testing.T) { "content": nil, "tool_calls": []map[string]interface{}{ { - "id": "call_abc123", + "id": "call_abc123", "type": "function", "function": map[string]interface{}{ - "name": "get_weather", + "name": "get_weather", "arguments": `{"location":"San Francisco"}`, }, }, @@ -430,7 +430,10 @@ func TestToolCallMultiTurn(t *testing.T) { } body1, _ := json.Marshal(turn1) - resp1, _ := http.Post(server.URL+"/v1/chat/completions", "application/json", bytes.NewReader(body1)) + resp1, err := http.Post(server.URL+"/v1/chat/completions", "application/json", bytes.NewReader(body1)) + if err != nil { + t.Fatalf("request failed: %v", err) + } var turn1Resp map[string]interface{} json.NewDecoder(resp1.Body).Decode(&turn1Resp) resp1.Body.Close() @@ -472,7 +475,10 @@ func TestToolCallMultiTurn(t *testing.T) { } body2, _ := json.Marshal(turn2) - resp2, _ := http.Post(server.URL+"/v1/chat/completions", "application/json", bytes.NewReader(body2)) + resp2, err := http.Post(server.URL+"/v1/chat/completions", "application/json", bytes.NewReader(body2)) + if err != nil { + t.Fatalf("request failed: %v", err) + } var turn2Resp map[string]interface{} json.NewDecoder(resp2.Body).Decode(&turn2Resp) resp2.Body.Close() @@ -503,7 +509,7 @@ func TestParallelToolCalls(t *testing.T) { "content": nil, "tool_calls": []map[string]interface{}{ { - "id": "call_1", + "id": "call_1", "type": "function", "function": map[string]interface{}{ "name": "get_weather", @@ -511,7 +517,7 @@ func TestParallelToolCalls(t *testing.T) { }, }, { - "id": "call_2", + "id": "call_2", "type": "function", "function": map[string]interface{}{ "name": "get_weather", @@ -519,7 +525,7 @@ func TestParallelToolCalls(t *testing.T) { }, }, { - "id": "call_3", + "id": "call_3", "type": "function", "function": map[string]interface{}{ "name": "get_weather", @@ -576,7 +582,10 @@ func TestParallelToolCalls(t *testing.T) { } body, _ := json.Marshal(request) - resp, _ := http.Post(server.URL+"/v1/chat/completions", "application/json", bytes.NewReader(body)) + resp, err := http.Post(server.URL+"/v1/chat/completions", "application/json", bytes.NewReader(body)) + if err != nil { + t.Fatalf("request failed: %v", err) + } var respData map[string]interface{} json.NewDecoder(resp.Body).Decode(&respData) resp.Body.Close() @@ -627,9 +636,9 @@ func TestAnthropicToolUse(t *testing.T) { // Anthropic response format with tool_use block response := map[string]interface{}{ - "id": "msg_123", - "type": "message", - "role": "assistant", + "id": "msg_123", + "type": "message", + "role": "assistant", "content": []map[string]interface{}{ { "type": "text", @@ -714,7 +723,10 @@ func TestAnthropicToolUse(t *testing.T) { body, _ := json.Marshal(anthropicRequest) // Note: For now we route through a generic path // In Phase 2.9+, this would be integrated with the Anthropic dialect handler - resp, _ := http.Post(server.URL+"/v1/messages", "application/json", bytes.NewReader(body)) + resp, err := http.Post(server.URL+"/v1/messages", "application/json", bytes.NewReader(body)) + if err != nil { + t.Fatalf("request failed: %v", err) + } var respData map[string]interface{} json.NewDecoder(resp.Body).Decode(&respData) resp.Body.Close() @@ -762,7 +774,7 @@ func TestComplexToolArguments(t *testing.T) { "content": nil, "tool_calls": []map[string]interface{}{ { - "id": "call_complex", + "id": "call_complex", "type": "function", "function": map[string]interface{}{ "name": "create_event", @@ -832,7 +844,10 @@ func TestComplexToolArguments(t *testing.T) { } body, _ := json.Marshal(request) - resp, _ := http.Post(server.URL+"/v1/chat/completions", "application/json", bytes.NewReader(body)) + resp, err := http.Post(server.URL+"/v1/chat/completions", "application/json", bytes.NewReader(body)) + if err != nil { + t.Fatalf("request failed: %v", err) + } var respData map[string]interface{} json.NewDecoder(resp.Body).Decode(&respData) resp.Body.Close() diff --git a/internal/proxy/unknown_model_test.go b/internal/proxy/unknown_model_test.go index 32667cf..8684227 100644 --- a/internal/proxy/unknown_model_test.go +++ b/internal/proxy/unknown_model_test.go @@ -134,11 +134,14 @@ func TestUnknownModelEnumeratesValidModels(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/chat/completions", "application/json", bytes.NewReader([]byte(`{"model":"unknown","messages":[]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() var prob map[string]interface{} @@ -188,11 +191,14 @@ func TestMissingModelFieldReturns4xx(t *testing.T) { defer server.Close() // Request with no model field - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/chat/completions", "application/json", bytes.NewReader([]byte(`{"messages":[]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode < 400 || resp.StatusCode >= 500 { @@ -230,11 +236,14 @@ func TestNullModelFieldReturns4xx(t *testing.T) { defer server.Close() // Request with null model - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/chat/completions", "application/json", bytes.NewReader([]byte(`{"model":null,"messages":[]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode < 400 || resp.StatusCode >= 500 { @@ -266,11 +275,14 @@ func TestEmptyModelFieldReturns4xx(t *testing.T) { defer server.Close() // Request with empty model string - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/chat/completions", "application/json", bytes.NewReader([]byte(`{"model":"","messages":[]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode < 400 || resp.StatusCode >= 500 { @@ -297,11 +309,14 @@ func TestInvalidJSONIsDistinguishableError(t *testing.T) { defer server.Close() // Request with invalid JSON - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/chat/completions", "application/json", bytes.NewReader([]byte(`not json`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode < 400 || resp.StatusCode >= 500 { @@ -405,19 +420,25 @@ func TestMissingModelAndUnknownModelBothReturn4xx(t *testing.T) { defer server.Close() // Test missing model - resp1, _ := http.Post( + resp1, err := http.Post( server.URL+"/v1/chat/completions", "application/json", bytes.NewReader([]byte(`{"messages":[]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } resp1.Body.Close() // Test unknown model - resp2, _ := http.Post( + resp2, err := http.Post( server.URL+"/v1/chat/completions", "application/json", bytes.NewReader([]byte(`{"model":"unknown","messages":[]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } resp2.Body.Close() // Both should be in 4xx range @@ -460,11 +481,14 @@ func TestProblemDetailHasRequiredFields(t *testing.T) { server := httptest.NewServer(handler) defer server.Close() - resp, _ := http.Post( + resp, err := http.Post( server.URL+"/v1/chat/completions", "application/json", bytes.NewReader([]byte(`{"model":"unknown","messages":[]}`)), ) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() var prob map[string]interface{}