Add auth retry tests

This commit is contained in:
Peter Steinberger 2025-11-07 00:51:10 +00:00
parent 41c36f2926
commit f36be7254e
2 changed files with 48 additions and 1 deletions

View File

@ -791,7 +791,7 @@ export async function handleAuth(runtime: Awaited<ReturnType<typeof createRuntim
logInfo(`Authorization complete. ${tools.length} tool${tools.length === 1 ? '' : 's'} available.`);
return;
} catch (error) {
if (attempt === 0 && error instanceof Error && /Unauthorized/i.test(error.message)) {
if (attempt === 0 && shouldRetryAuthError(error)) {
logWarn('Server signaled OAuth after the initial attempt. Retrying with browser flow...');
continue;
}
@ -801,6 +801,14 @@ export async function handleAuth(runtime: Awaited<ReturnType<typeof createRuntim
}
}
function shouldRetryAuthError(error: unknown): boolean {
const message = error instanceof Error ? error.message : typeof error === 'string' ? error : '';
if (!message) {
return false;
}
return /unauthorized|invalid[_-]?token|\b(401|403)\b/i.test(message);
}
function looksLikeHttpUrl(value: string): boolean {
return /^https?:\/\//i.test(value);
}

View File

@ -0,0 +1,39 @@
import { describe, expect, it, vi } from 'vitest';
process.env.MCPORTER_DISABLE_AUTORUN = '1';
const cliModulePromise = import('../src/cli.js');
const baseDefinition = {
name: 'adhoc-server',
command: { kind: 'http' as const, url: new URL('https://example.com/mcp') },
source: { kind: 'local' as const, path: '<adhoc>' },
};
describe('handleAuth retry logic', () => {
it('retries once when the first attempt is unauthorized', async () => {
const { handleAuth } = await cliModulePromise;
const runtime = {
registerDefinition: vi.fn(),
getDefinition: vi.fn().mockReturnValue(baseDefinition),
listTools: vi
.fn()
.mockRejectedValueOnce(new Error('SSE error: Non-200 status code (401)'))
.mockResolvedValueOnce([{ name: 'ok' }]),
} as unknown as Awaited<ReturnType<typeof import('../src/runtime.js')['createRuntime']>>;
await expect(handleAuth(runtime, ['adhoc-server'])).resolves.toBeUndefined();
expect(runtime.listTools).toHaveBeenCalledTimes(2);
});
it('throws after the second unauthorized attempt', async () => {
const { handleAuth } = await cliModulePromise;
const runtime = {
registerDefinition: vi.fn(),
getDefinition: vi.fn().mockReturnValue(baseDefinition),
listTools: vi.fn().mockRejectedValue(new Error('SSE error: Non-200 status code (401)')),
} as unknown as Awaited<ReturnType<typeof import('../src/runtime.js')['createRuntime']>>;
await expect(handleAuth(runtime, ['adhoc-server'])).rejects.toThrow(/Failed to authorize/);
expect(runtime.listTools).toHaveBeenCalledTimes(2);
});
});