Skip to content

Commit ed547e9

Browse files
authored
[test] Add tests for mcp.parseHTTPResult (#5268)
# Test Coverage Improvement: `parseHTTPResult` ## Function Analyzed - **Package**: `internal/mcp` - **Function**: `parseHTTPResult` - **File**: `internal/mcp/http_transport.go` - **Previous Coverage**: 0% (not directly tested) - **Complexity**: Medium — 3 reachable branches + delegation to `parseJSONRPCResponseWithSSE` ## Why This Function? `parseHTTPResult` is the core result-parsing step in the HTTP transport's request pipeline — every call via `sendHTTPRequest` routes through it. Despite being called in production, the function had **zero direct test coverage**. It contains meaningful branches around HTTP error handling and JSON-RPC error synthesis that benefit from explicit tests. ## Tests Added - ✅ Happy path: 200 OK with valid JSON-RPC result - ✅ Error path: 200 OK with invalid/empty JSON body (Go error returned) - ✅ Non-200 status (400, 401, 500): synthetic JSON-RPC error synthesised with code `-32603` and HTTP status text - ✅ SSE-formatted 200 response parsed correctly - ✅ JSON-RPC error field in 200 response passes through unchanged ## Coverage Report ``` Before: parseHTTPResult — 0% direct coverage (not referenced in any test) After: parseHTTPResult — all reachable branches covered by 7 sub-tests ``` ## Test Execution > Note: The repository requires Go 1.25.0 (`go.mod`) but only Go 1.24.13 is available in CI; local test execution is blocked by toolchain version constraints. Tests are syntactically valid, follow all existing conventions in `http_transport_test.go`, and are logically correct based on code analysis of `parseHTTPResult` and its dependency `parseJSONRPCResponseWithSSE`. --- *Generated by Test Coverage Improver* > [!WARNING] > <details> > <summary>Firewall blocked 1 domain</summary> > > The following domain was blocked by the firewall during workflow execution: > > - `proxy.golang.org` >> To allow these domains, add them to the `network.allowed` list in your workflow frontmatter: > > ```yaml > network: > allowed: > - defaults > - "proxy.golang.org" > ``` > > See [Network Configuration](https://github.github.com/gh-aw/reference/network/) for more information. > > </details> > Generated by [Test Coverage Improver](https://github.com/github/gh-aw-mcpg/actions/runs/25510294189/agentic_workflow) · ● 17.2M · [◷](https://github.com/search?q=repo%3Agithub%2Fgh-aw-mcpg+%22gh-aw-workflow-id%3A+test-coverage-improver%22&type=pullrequests) <!-- gh-aw-agentic-workflow: Test Coverage Improver, engine: copilot, version: 1.0.40, model: claude-sonnet-4.6, id: 25510294189, workflow_id: test-coverage-improver, run: https://github.com/github/gh-aw-mcpg/actions/runs/25510294189 --> <!-- gh-aw-workflow-id: test-coverage-improver -->
2 parents 5ed5d5f + 73efe70 commit ed547e9

1 file changed

Lines changed: 102 additions & 0 deletions

File tree

internal/mcp/http_transport_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,3 +1320,105 @@ func TestResponseHeaderTimeout_NotCappedByConnectTimeout(t *testing.T) {
13201320
defer resp.Body.Close()
13211321
assert.Equal(t, http.StatusOK, resp.StatusCode)
13221322
}
1323+
1324+
// =============================================================================
1325+
// parseHTTPResult tests
1326+
// =============================================================================
1327+
1328+
func TestParseHTTPResult(t *testing.T) {
1329+
t.Run("status 200 with valid JSON-RPC result", func(t *testing.T) {
1330+
result := &httpRequestResult{
1331+
StatusCode: http.StatusOK,
1332+
ResponseBody: []byte(`{"jsonrpc":"2.0","id":1,"result":{"value":"hello"}}`),
1333+
}
1334+
resp, err := parseHTTPResult(result)
1335+
require.NoError(t, err)
1336+
require.NotNil(t, resp)
1337+
assert.Nil(t, resp.Error, "response should have no error for 200 OK")
1338+
})
1339+
1340+
t.Run("status 200 with invalid JSON returns error", func(t *testing.T) {
1341+
result := &httpRequestResult{
1342+
StatusCode: http.StatusOK,
1343+
ResponseBody: []byte(`not valid json at all`),
1344+
}
1345+
resp, err := parseHTTPResult(result)
1346+
assert.Error(t, err, "should return error for unparseable 200 response")
1347+
assert.Nil(t, resp)
1348+
})
1349+
1350+
t.Run("status 200 with empty body returns error", func(t *testing.T) {
1351+
result := &httpRequestResult{
1352+
StatusCode: http.StatusOK,
1353+
ResponseBody: []byte{},
1354+
}
1355+
resp, err := parseHTTPResult(result)
1356+
assert.Error(t, err, "should return error for empty 200 response body")
1357+
assert.Nil(t, resp)
1358+
})
1359+
1360+
t.Run("status 400 synthesises JSON-RPC error", func(t *testing.T) {
1361+
result := &httpRequestResult{
1362+
StatusCode: http.StatusBadRequest,
1363+
ResponseBody: []byte(`bad request body`),
1364+
}
1365+
resp, err := parseHTTPResult(result)
1366+
require.NoError(t, err, "non-200 status should not return a Go error")
1367+
require.NotNil(t, resp)
1368+
require.NotNil(t, resp.Error, "response should carry a JSON-RPC error for non-200 status")
1369+
assert.Equal(t, -32603, resp.Error.Code)
1370+
assert.Contains(t, resp.Error.Message, "400")
1371+
})
1372+
1373+
t.Run("status 401 synthesises JSON-RPC error with status text", func(t *testing.T) {
1374+
result := &httpRequestResult{
1375+
StatusCode: http.StatusUnauthorized,
1376+
ResponseBody: []byte(`Unauthorized`),
1377+
}
1378+
resp, err := parseHTTPResult(result)
1379+
require.NoError(t, err)
1380+
require.NotNil(t, resp)
1381+
require.NotNil(t, resp.Error)
1382+
assert.Equal(t, -32603, resp.Error.Code)
1383+
assert.Contains(t, resp.Error.Message, "401")
1384+
assert.Contains(t, resp.Error.Message, "Unauthorized")
1385+
})
1386+
1387+
t.Run("status 500 synthesises JSON-RPC error", func(t *testing.T) {
1388+
result := &httpRequestResult{
1389+
StatusCode: http.StatusInternalServerError,
1390+
ResponseBody: []byte(`Internal Server Error`),
1391+
}
1392+
resp, err := parseHTTPResult(result)
1393+
require.NoError(t, err)
1394+
require.NotNil(t, resp)
1395+
require.NotNil(t, resp.Error)
1396+
assert.Equal(t, -32603, resp.Error.Code)
1397+
assert.Contains(t, resp.Error.Message, "500")
1398+
})
1399+
1400+
t.Run("status 200 with SSE-formatted valid response", func(t *testing.T) {
1401+
sseBody := []byte("event: message\ndata: {\"jsonrpc\":\"2.0\",\"id\":2,\"result\":{\"tools\":[]}}" + "\n\n")
1402+
result := &httpRequestResult{
1403+
StatusCode: http.StatusOK,
1404+
ResponseBody: sseBody,
1405+
}
1406+
resp, err := parseHTTPResult(result)
1407+
require.NoError(t, err)
1408+
require.NotNil(t, resp)
1409+
assert.Nil(t, resp.Error)
1410+
})
1411+
1412+
t.Run("status 200 with JSON-RPC error field passes through", func(t *testing.T) {
1413+
result := &httpRequestResult{
1414+
StatusCode: http.StatusOK,
1415+
ResponseBody: []byte(`{"jsonrpc":"2.0","id":3,"error":{"code":-32601,"message":"Method not found"}}`),
1416+
}
1417+
resp, err := parseHTTPResult(result)
1418+
require.NoError(t, err)
1419+
require.NotNil(t, resp)
1420+
require.NotNil(t, resp.Error, "JSON-RPC error in 200 response should be passed through")
1421+
assert.Equal(t, -32601, resp.Error.Code)
1422+
assert.Equal(t, "Method not found", resp.Error.Message)
1423+
})
1424+
}

0 commit comments

Comments
 (0)