Improve e2e test coverage for middleware + streamable-http proxy mode
Overview
While investigating #2509, we discovered that our e2e tests didn't catch a bug where audit and authz middleware broke streamable-http proxy mode due to missing http.Flusher interface implementation. This issue tracks improvements to prevent similar bugs in the future.
Root Cause of Missed Detection
The existing e2e tests use the mark3labs/mcp-go client library, which abstracts away HTTP-level details. This means:
- Tests don't directly interact with streaming HTTP behavior
- The Flusher interface requirement isn't exercised
- Middleware response writer wrapping issues aren't exposed
Current Test Coverage
What We Have
-
Audit middleware test (
test/e2e/audit_middleware_e2e_test.go:23)- ✅ Tests streamable-http transport
- ✅ Uses
--audit-configflag - ⚠️ Uses MCP client library (doesn't test raw HTTP streaming)
-
Streamable-HTTP test (
test/e2e/osv_streamable_http_mcp_server_test.go)- ✅ Tests streamable-http transport
- ✅ Makes GET request with
Accept: text/event-streamheader (line 94) - ⚠️ Uses MCP client library for protocol operations
-
Authz middleware test (
test/e2e/osv_authz_test.go:20)- ❌ Only tests SSE transport (line 66)
- ❌ No streamable-http + authz test coverage
What's Missing
- No direct HTTP streaming tests (raw HTTP client, not MCP library)
- No combination tests: authz + streamable-http
- No tests that verify ResponseWriter wrappers implement
http.Flusher - No tests with large responses that would stress streaming behavior
Recommendations
1. Add Raw HTTP Streaming Tests
Create tests that directly use net/http to make requests with streaming headers, bypassing the MCP client library:
// Test that streamable-http endpoints support streaming with middleware enabled
It("should support HTTP streaming with audit middleware", func() {
client := &http.Client{}
req, _ := http.NewRequest("POST", serverURL+"/mcp", bytes.NewReader(jsonPayload))
req.Header.Set("Accept", "text/event-stream")
resp, err := client.Do(req)
Expect(err).ToNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(200), "Should not return 'Streaming not supported' error")
// Verify we can read streaming response
flusher, ok := resp.Body.(http.Flusher)
// Test actual streaming behavior...
})
2. Add Middleware Combination Tests
Test each middleware with streamable-http transport:
- Audit + streamable-http ✅ (exists but needs improvement)
- Authz + streamable-http ❌ (missing)
- Telemetry + streamable-http (needs verification)
- Multiple middlewares + streamable-http
3. Add Flusher Interface Tests
Add unit/integration tests that verify middleware ResponseWriter wrappers properly implement http.Flusher:
It("should implement http.Flusher interface", func() {
w := httptest.NewRecorder()
wrapper := &responseWriter{ResponseWriter: w}
_, ok := wrapper.(http.Flusher)
Expect(ok).To(BeTrue(), "ResponseWriter wrapper must implement http.Flusher")
})
4. Add Large Response Tests
Test with servers that return large responses (like GitHub MCP) to ensure streaming works correctly under load.
Acceptance Criteria
- [ ] Add at least one raw HTTP streaming test for streamable-http + audit middleware
- [ ] Add e2e test for authz + streamable-http combination
- [ ] Add unit tests verifying all middleware ResponseWriter wrappers implement
http.Flusher - [ ] Document why we need both MCP client library tests AND raw HTTP tests
- [ ] Consider adding CI check that verifies all middleware wrappers implement standard interfaces
Related
- Original bug: #2509
- Fix PR: #2512
Additional Context
The bug was fixed by adding Flush() methods to:
-
pkg/audit/auditor.go:94-99 -
pkg/authz/response_filter.go:92-98
Both now properly delegate to the underlying ResponseWriter's Flusher if it supports it, matching the pattern in telemetry and tool filter middlewares.