feat(typescript): add OAuth wire test support
Description
Linear ticket: N/A Closes: Implements OAuth endpoint mocking for TypeScript SDK wire tests
This PR enables wire test generation for APIs that use OAuth client credentials authentication. Previously, wire tests were skipped entirely when OAuth was configured. Now, the generator can mock OAuth token endpoints and generate tests that properly handle OAuth token refresh flows.
Link to Devin run: https://app.devin.ai/sessions/96317fef2758485da42067f1ccfcc982 Requested by: Niels Swimberghe ([email protected]) (@Swimburger)
Changes Made
Core Implementation
- Added
canMockOAuth()helper to check if OAuth can be mocked (requires examples for token endpoint) - Extended
buildMockAuthFile()to generate OAuth token endpoint mocks in addition to inferred auth - Updated
getAuthClientOptions()to extract OAuth credentials from IR examples instead of hardcoded "test" values - Modified
shouldBuildTest()to allow OAuth-authenticated endpoints (changed fromreturn truetoreturn false)
OAuth Token Refresh Handling
- Implemented "warm-up" approach: performs a preflight token call before registering per-test body-asserting mocks
- Added URL/method matching to detect when an endpoint is the OAuth token endpoint (handles both
getTokenWithClientCredentialsandrefreshTokenwhich both POST to/token) - Override
expires_into 3600 seconds in mock responses to prevent token expiration during test execution
Generated Test Files
- Updated 320 seed snapshot files with new wire tests for OAuth and inferred auth fixtures
- All OAuth fixtures now have wire tests generated (oauth-client-credentials, oauth-client-credentials-environment-variables, etc.)
- Verified inferred auth fixtures continue to work correctly
Testing
- [x] All 91 CI checks passed (0 failures)
- [x] Local seed tests completed: 164 passed, 10 expected failures
- [x] Verified OAuth fixtures: oauth-client-credentials (253 tests), oauth-client-credentials-environment-variables (253 tests)
- [x] Verified inferred auth fixtures: inferred-auth-implicit (253 tests), inferred-auth-explicit (253 tests), inferred-auth-implicit-no-expiry (253 tests)
Review Checklist
Critical areas to review:
-
Warm-up approach (TestGenerator.ts lines ~1128-1131, ~1182): The implementation performs a preflight OAuth token call before registering the per-test mock. This prevents mock ordering conflicts where the preflight hits the body-asserting mock. Verify this pattern makes sense and won't cause issues with test isolation.
-
Token expiration override (TestGenerator.ts line ~547): The mock returns
expires_in: 3600instead of the example value (typically 1). This prevents token expiration during test execution. Confirm this is a reasonable assumption for test suites and won't mask real token expiration issues. -
URL/method matching (TestGenerator.ts lines ~1094-1114): Uses endpoint comparison to detect OAuth token endpoints. This works but could be fragile if IR structure changes. Consider if there's a more robust comparison method.
-
Backward compatibility: Verify that existing inferred auth tests still work correctly (the code now handles both inferred and OAuth in the same methods like
buildMockAuthFile()andshouldBuildMockAuthFile()). -
Example extraction (TestGenerator.ts lines ~581-650): OAuth credentials are extracted from IR examples. If examples are missing or malformed, tests won't be generated (by design via
canMockOAuth()). Confirm this is the desired behavior.
Lower priority:
- Generated test file structure and naming conventions
- Error handling when OAuth config is incomplete
- Code organization and readability
🤖 Devin AI Engineer
I'll be helping with this pull request! Here's what you should know:
✅ I will automatically:
- Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
- Look at CI failures and help fix them
Note: I can only respond to comments from users who have write access to this repository.
⚙️ Control Options:
- [ ] Disable automatic comment and CI monitoring
Fixed! I've removed the expires_in: 3600 override. The mock now uses the example value as-is from the IR definition.
The override was originally added to prevent token expiration during test execution, but you're right that it's suspicious - it could mask real token expiration issues and doesn't match the example values in the API definition.
Done! I've refactored the code to generate separate mock functions per auth scheme:
Changes made:
- Separate mock functions: Now generates
mock{AuthSchemeKeyInPascalCase}functions (e.g.,mockOAuth,mockInferredAuth) instead of a singlemockAuthfunction - PascalCase conversion: Uses
upperFirst(camelCase(scheme.key))to convert auth scheme keys to PascalCase function names - Auth priority in wire tests: Wire tests now import and call the correct mock function based on auth priority (oauth > inferred), mirroring the logic in
GeneratedSdkClientClassImpl.ts
Implementation details:
buildMockAuthFile()now generates multiple exported functions in a singlemockAuth.tsfilebuildExampleTest()determines which mock function to call based on which auth scheme will be used for Authorization (OAuth takes priority over inferred auth)- Only generates mock functions for schemes that require server mocking (OAuth client credentials and Inferred Auth)
This makes the generated code more explicit about which auth scheme is being mocked and aligns with the actual auth selection logic in the SDK client.
Unfortunately, the most oauth's fail:
┌────────────────────────────────────────────────┬────────────────────┬─────────┬─────────────────┬──────────────┬──────────────┬─────────────────┐ │ Name │ Output Folder │ Result │ Generation Time │ Compile Time │ Failure Type │ Failure Message │ ├────────────────────────────────────────────────┼────────────────────┼─────────┼─────────────────┼──────────────┼──────────────┼─────────────────┤ │ oauth-client-credentials │ no-custom-config │ failure │ 6s │ 6.8s │ compile │ │ │ oauth-client-credentials │ serde │ failure │ 6.9s │ 5.7s │ compile │ │ │ oauth-client-credentials-custom │ -- │ failure │ 6s │ 3.9s │ compile │ │ │ oauth-client-credentials-default │ -- │ success │ 5.3s │ 5s │ │ │ │ oauth-client-credentials-environment-variables │ -- │ failure │ 6.3s │ 5.3s │ compile │ │ │ oauth-client-credentials-nested-root │ no-custom-config │ failure │ 6.4s │ 6.9s │ compile │ │ │ oauth-client-credentials-nested-root │ never-throw-errors │ failure │ 6.3s │ 7.2s │ compile │ │ │ oauth-client-credentials-with-variables │ -- │ failure │ 5.4s │ 5s │ compile │ │ └────────────────────────────────────────────────┴────────────────────┴─────────┴─────────────────┴──────────────┴──────────────┴─────────────────┘
This is likely because of how the root client is initialized which influences the auth requests before making other requests.