fern icon indicating copy to clipboard operation
fern copied to clipboard

feat(typescript): add OAuth wire test support

Open devin-ai-integration[bot] opened this issue 1 month ago • 1 comments

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 from return true to return 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 getTokenWithClientCredentials and refreshToken which both POST to /token)
  • Override expires_in to 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:

  1. 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.

  2. Token expiration override (TestGenerator.ts line ~547): The mock returns expires_in: 3600 instead 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.

  3. 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.

  4. 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() and shouldBuildMockAuthFile()).

  5. 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:

  1. Separate mock functions: Now generates mock{AuthSchemeKeyInPascalCase} functions (e.g., mockOAuth, mockInferredAuth) instead of a single mockAuth function
  2. PascalCase conversion: Uses upperFirst(camelCase(scheme.key)) to convert auth scheme keys to PascalCase function names
  3. 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 single mockAuth.ts file
  • buildExampleTest() 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.

Swimburger avatar Nov 19 '25 03:11 Swimburger