refactor: create internal/integrationtest package infrastructure#206
refactor: create internal/integrationtest package infrastructure#206
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
f45d4d2 to
e95970f
Compare
fd1cc42 to
e235208
Compare
dannykopping
left a comment
There was a problem hiding this comment.
Love the more concise tests! Thanks for doing this.
I would've appreciated if you'd stacked these changes. It's a huge PR with not only deduplication but also logical changes, and this makes it very difficult to review effectively. I'm concerned that some things will slip through, but in general it looks good.
| for _, h := range header { | ||
| for k, vals := range h { | ||
| for _, v := range vals { | ||
| req.Header.Set(k, v) |
There was a problem hiding this comment.
This should be Header.Add right? Otherwise the last one always wins?
| require.Len(t, received, 1) | ||
| require.Equal(t, tc.expectedUpstreamPath, received[0].Path) | ||
| require.Contains(t, received[0].Header.Get(provider.AuthHeader()), apiKey) | ||
| require.Contains(t, received[0].Header.Get(tc.authHeader), apiKey) |
There was a problem hiding this comment.
You're hardcoding the auth header now instead of using provider.AuthHeader; why?
There was a problem hiding this comment.
After refactor provider variable is no longer here.
This change is done because of it but in general hardcoding test inputs / expected outputs, in my experience was desired.
There was a problem hiding this comment.
I'd prefer we didn't define this upstream requirement in multiple places.
But, I can see why you did it and change it in the provider will cause the tests to fail, so it's not a big deal.
ssncferreira
left a comment
There was a problem hiding this comment.
Overall LGTM, this makes the tests cleaner 🧹 thanks for working on this!
Agree with Danny that in the future it would be nice to split big PRs like this into smaller, more focused ones to make the review process easier 🙂
| configureFunc func(*testing.T, string, aibridge.Recorder) (*aibridge.RequestBridge, error) | ||
| createRequest func(t *testing.T, baseURL string, body []byte) *http.Request | ||
| header http.Header | ||
| mutateBody func(t *testing.T, body []byte) []byte |
There was a problem hiding this comment.
Nit: IIUC mutateBody is only used once to set metadata.user_id, maybe a simple string field like sessionUserID would be clearer? We can always promote to a func later if needed.
| resp := bridgeServer.makeRequest(t, http.MethodPost, tc.path, reqBody) | ||
| // Drain the body so streaming responses complete without | ||
| // a "connection reset" error in the mock upstream. | ||
| _, _ = io.ReadAll(resp.Body) |
There was a problem hiding this comment.
We should still check for errors here, right? 🤔
| _, _ = io.ReadAll(resp.Body) | |
| _, err := io.ReadAll(resp.Body) | |
| require.NoError(t, err) |
| provider.NewAnthropic(anthropicCfg("http://unused", apiKey), bedrockCfg), | ||
| }, recorderClient, testutil.NewNoopMCPManager(), logger, nil, testTracer) | ||
| require.NoError(t, err) | ||
| bridgeServer := newBridgeTestServer(t, ctx, "http://unused", |
There was a problem hiding this comment.
newBridgeTestServer calls newLogger(t) which does not set IgnoreErrors: true as we had before, isn't this breaking the tests? 🤔
There was a problem hiding this comment.
Test pass, I'm not sure what changed that there are no errors now.
| srv, _ := newTestSrv(t, ctx, prov, nil, testTracer) | ||
| defer srv.Close() | ||
| resp := bridgeServer.makeRequest(t, http.MethodPost, pathOpenAIResponses, []byte(tc.request)) | ||
| _, _ = io.ReadAll(resp.Body) |
There was a problem hiding this comment.
We should still check the error:
| _, _ = io.ReadAll(resp.Body) | |
| _, err = io.ReadAll(resp.Body) | |
| require.NoError(t, err) |
| // withCustomProvider adds a pre-built provider. The upstream URL passed to | ||
| // [newBridgeTestServer] is ignored for this provider. | ||
| // When any provider option is used, the default "all providers" set is not created. | ||
| func withCustomProvider(p aibridge.Provider) bridgeOption { |
There was a problem hiding this comment.
Nit: what happens when we set both withProvider and withCustomProvider? This means we could be appending 2 anthropic providers, right?
There was a problem hiding this comment.
It is possible to append 2 same providers just using withProvider.
With 2 the same providers test will fail due to panic, http mux will panic due to not being able to mount routes with the same path. For testing utility I think it is fine not to check for it.
panic: pattern "/openai/v1/chat/completions" (registered at /home/coder/aibridge/bridge.go:106) conflicts with pattern "/openai/v1/chat/completions" (registered at /home/coder/aibridge/bridge.go:106):
| resp := bridgeServer.makeRequest(t, http.MethodPost, path, reqBody) | ||
| require.Equal(t, http.StatusOK, resp.StatusCode) | ||
|
|
||
| // We must ALWAYS have 2 calls to the bridge for injected tool tests. |
There was a problem hiding this comment.
IIUC, the 2 upstream calls are:
- The initial request, where the provider responds with "call tool X"
- The follow-up request, where aibridge sends the tool result back to the provider
Is this correct? Could we make the comment a bit clearer about this?
| return upstream.Calls.Load() == 2 | ||
| }, time.Second*10, time.Millisecond*50) | ||
|
|
||
| return bridgeServer.Recorder, mockMCP, resp |
There was a problem hiding this comment.
Nit: setupInjectedToolTest creates a bridgeTestServer but only returns the Recorder. Would it make sense to return *bridgeTestServer instead? This way callers can still access .Recorder, but also have access to the test server if needed.
- Move upstream.go and mockmcp.go from internal/testutil to internal/integrationtest - Create bridge.go with NewBridgeTestServer, NewLogger, DefaultActorID, DefaultTracer - Create requests.go with shared request/config helpers (promoted from test files) - internal/testutil retains only lightweight mocks (MockRecorder, MockProvider)
…egrationtest/ - Move metrics_integration_test.go → internal/integrationtest/metrics_test.go - Move trace_integration_test.go → internal/integrationtest/trace_test.go - Change package from aibridge_test to integrationtest_test - Replace all newTestSrv calls with integrationtest.NewBridgeTestServer - Delete the newTestSrv helper function entirely - Update all imports: testutil symbols → integrationtest where moved - Replace local helpers (apiKey, userID, openaiCfg, etc.) with exported versions - All former newTestSrv sites use WithWrappedRecorder() to preserve behavior - Remove duplicate testBedrockCfg (already in bridge_test.go)
… tests to internal/integrationtest/ Move the following files from the repo root: - responses_integration_test.go → internal/integrationtest/responses_test.go - circuit_breaker_integration_test.go → internal/integrationtest/circuit_breaker_test.go - apidump_integration_test.go → internal/integrationtest/apidump_test.go Changes in all files: - Package: aibridge_test → integrationtest_test - Replace newTestSrv/NewRequestBridge with integrationtest.NewBridgeTestServer - Replace testutil.* moved symbols with integrationtest.* - Replace local helper refs (openaiCfg, apiKey, userID, etc.) with integrationtest.OpenAICfg, integrationtest.APIKey, integrationtest.DefaultActorID - Delete local createOpenAIResponsesReq (now in requests.go) - Keep local helpers specific to each file
…lerplate with NewBridgeTestServer - Convert all 18 remaining aibridge.NewRequestBridge call sites to use NewBridgeTestServer with functional options - Replace all configureFunc closures (4 different signatures, ~12 inline closures) with providerFn field + NewBridgeTestServer in test body - Convert setupInjectedToolTest to use NewBridgeTestServer internally - Fix stale recorderClient references to ts.Recorder - Remove unused imports (slog, aibcontext, testutil from responses_test) - Delete bridge_integration_test.go from root Net result: -424 lines of boilerplate, root directory down to 6 files. All tests pass (go test ./... -count=1).
Change package from integrationtest_test to integrationtest, remove the self-import, and strip all integrationtest. prefixes from identifiers.
Switch all _test.go files from package integrationtest_test to package integrationtest. Since nothing outside this directory imports the package, all exported symbols are unexported: - Types: BridgeTestServer → bridgeTestServer, MockMCP → mockMCP, MockUpstream → mockUpstream, UpstreamResponse → upstreamResponse, etc. - Functions: NewBridgeTestServer → newBridgeTestServer, CreateAnthropicMessagesReq → createAnthropicMessagesReq, etc. - Options: WithMetrics → withMetrics, WithTracer → withTracer, etc. - Constants: APIKey → apiKey, DefaultActorID → defaultActorID, etc. - Methods: GetCallsByTool → getCallsByTool, ReceivedRequests → receivedRequests, etc. This eliminates the integrationtest. prefix from all call sites and removes the artificial export surface. All tests pass (go test ./... -count=1).
…p.Response return type makeRequest now performs the HTTP request internally and returns *http.Response directly. Remove the intermediate http.Client creation, client.Do(req), and require.NoError(t, err) calls at all 5 call sites.
makeRequest now returns *http.Response directly instead of *http.Request. Updated all 6 call sites: - Removed intermediate http.DefaultClient.Do / client.Do calls - Removed require.NoError for the HTTP request error - Collapsed header mutation site to pass http.Header as extra arg
makeRequest now returns *http.Response directly (performs HTTP request
internally). Updated all 14 call sites:
- Changed req := to resp :=
- Removed http.DefaultClient.Do(req) and require.NoError(t, err) lines
- Removed client := &http.Client{} and client.Do(req) patterns
- Converted header mutation case to pass http.Header as extra arg
…etions to use newBridgeTestServer + test some names cleanup
…derName from TestFallthrough
b24c6fc to
0f25518
Compare

Refactor integration tests into internal/integrationtest package
This PR consolidates all integration tests into a dedicated
internal/integrationtestpackage.Move test infrastructure to internal/integrationtest
upstream.goandmockmcp.gofrominternal/testutiltointernal/integrationtestsetupbridge.gowithnewBridgeTestServerand functional options patternhelpers.gowith shared configuration builders and utilitiesMigrate all integration tests
Move integration test files from root to
internal/integrationtest/:bridge_integration_test.go→bridge_test.gometrics_integration_test.go→metrics_test.gotrace_integration_test.go→trace_test.goresponses_integration_test.go→responses_test.gocircuit_breaker_integration_test.go→circuit_breaker_test.goapidump_integration_test.go→apidump_test.goReplace boilerplate with functional options
Convert all
aibridge.NewRequestBridgecall sites to usenewBridgeTestServerwith options:withProvider(),withMetrics(),withMCP(), etc.configureFuncclosures and repetitive server setup codewithActor()Consolidate test utilities
bridgeTestServer.makeRequest()apiKey,defaultActorID, path constants)