Files
ccs/tests/unit/cliproxy/oauth-handler-paste-callback.test.ts
T
Tam Nhu Tran 834704938d test(cliproxy): cover optional nickname auth flows
- add registry coverage for generated ids, reauth, and ambiguous prefixes

- add CLI and dashboard validation tests for optional nickname handling

- add manual callback coverage for persisted social OAuth nicknames
2026-03-23 11:02:08 -04:00

156 lines
5.6 KiB
TypeScript

import { afterEach, describe, expect, it } from 'bun:test';
import type { ProxyTarget } from '../../../src/cliproxy/proxy-target-resolver';
import { getCapturedFetchRequests, mockFetch, restoreFetch } from '../../mocks';
const remoteTarget: ProxyTarget = {
host: 'proxy.example.com',
port: 8317,
protocol: 'https',
managementKey: 'test-mgmt-key',
isRemote: true,
};
afterEach(() => {
restoreFetch();
});
describe('requestPasteCallbackStart', () => {
it('uses management auth-url route for non-kiro providers', async () => {
mockFetch([
{
url: /\/v0\/management\/anthropic-auth-url\?is_webui=true$/,
response: { auth_url: 'https://auth.example.com/claude' },
},
]);
const { requestPasteCallbackStart } = await import(
`../../../src/cliproxy/auth/oauth-handler?request-claude-start=${Date.now()}`
);
const startData = await requestPasteCallbackStart('claude', remoteTarget);
expect(startData.auth_url).toBe('https://auth.example.com/claude');
const [request] = getCapturedFetchRequests();
expect(request.url).toBe(
'https://proxy.example.com:8317/v0/management/anthropic-auth-url?is_webui=true'
);
expect(request.method).toBe('GET');
expect(request.headers['Authorization']).toBe('Bearer test-mgmt-key');
expect(request.headers['Content-Type']).toBeUndefined();
});
it('keeps kiro on the legacy start route with POST', async () => {
mockFetch([
{
url: /\/oauth\/kiro\/start$/,
method: 'POST',
response: { auth_url: 'https://auth.example.com/kiro' },
},
]);
const { requestPasteCallbackStart } = await import(
`../../../src/cliproxy/auth/oauth-handler?request-kiro-start=${Date.now()}`
);
const startData = await requestPasteCallbackStart('kiro', remoteTarget);
expect(startData.auth_url).toBe('https://auth.example.com/kiro');
const [request] = getCapturedFetchRequests();
expect(request.url).toBe('https://proxy.example.com:8317/oauth/kiro/start');
expect(request.method).toBe('POST');
expect(request.headers['Authorization']).toBe('Bearer test-mgmt-key');
expect(request.headers['Content-Type']).toBe('application/json');
});
});
describe('resolvePasteCallbackAuthUrl', () => {
it('returns the immediate auth URL without polling', async () => {
const { resolvePasteCallbackAuthUrl } = await import(
`../../../src/cliproxy/auth/oauth-handler?resolve-immediate-auth-url=${Date.now()}`
);
const authUrl = await resolvePasteCallbackAuthUrl(
remoteTarget,
{ auth_url: 'https://auth.example.com/direct' },
50,
0
);
expect(authUrl).toBe('https://auth.example.com/direct');
expect(getCapturedFetchRequests()).toHaveLength(0);
});
it('polls management status when the start response only returns state', async () => {
mockFetch([
{
url: /\/v0\/management\/get-auth-status\?state=state-123$/,
response: { status: 'auth_url', auth_url: 'https://auth.example.com/polled' },
},
]);
const { resolvePasteCallbackAuthUrl } = await import(
`../../../src/cliproxy/auth/oauth-handler?resolve-polled-auth-url=${Date.now()}`
);
const authUrl = await resolvePasteCallbackAuthUrl(remoteTarget, { state: 'state-123' }, 50, 0);
expect(authUrl).toBe('https://auth.example.com/polled');
const [request] = getCapturedFetchRequests();
expect(request.url).toBe(
'https://proxy.example.com:8317/v0/management/get-auth-status?state=state-123'
);
expect(request.method).toBe('GET');
expect(request.headers['Authorization']).toBe('Bearer test-mgmt-key');
});
});
describe('getCliAuthNicknameError', () => {
it('allows omitted nicknames for no-email providers', async () => {
const { getCliAuthNicknameError } = await import(
`../../../src/cliproxy/auth/oauth-handler?cli-nickname-empty=${Date.now()}`
);
expect(getCliAuthNicknameError('kiro', undefined, [])).toBeNull();
expect(getCliAuthNicknameError('ghcp', undefined, [])).toBeNull();
});
it('rejects invalid supplied nicknames before OAuth starts', async () => {
const { getCliAuthNicknameError } = await import(
`../../../src/cliproxy/auth/oauth-handler?cli-nickname-invalid=${Date.now()}`
);
expect(getCliAuthNicknameError('kiro', 'bad nickname', [])).toBe(
'Nickname cannot contain whitespace'
);
});
it('rejects supplied nicknames that collide with existing ids or nicknames', async () => {
const { getCliAuthNicknameError } = await import(
`../../../src/cliproxy/auth/oauth-handler?cli-nickname-conflict=${Date.now()}`
);
const existingAccounts = [
{ id: 'github-ABC123', nickname: 'work' },
{ id: 'ghcp-2', nickname: 'personal' },
];
expect(getCliAuthNicknameError('ghcp', 'github-ABC123', existingAccounts)).toBe(
'Nickname "github-ABC123" is already in use. Choose a different one.'
);
expect(getCliAuthNicknameError('ghcp', 'work', existingAccounts)).toBe(
'Nickname "work" is already in use. Choose a different one.'
);
});
it('allows reauth when the supplied nickname already belongs to the same account', async () => {
const { getCliAuthNicknameError } = await import(
`../../../src/cliproxy/auth/oauth-handler?cli-nickname-reauth=${Date.now()}`
);
const existingAccounts = [
{ id: 'github-ABC123', nickname: 'work' },
{ id: 'amazon-XYZ789', nickname: 'personal' },
];
expect(getCliAuthNicknameError('kiro', 'work', existingAccounts, 'github-ABC123')).toBeNull();
expect(getCliAuthNicknameError('kiro', 'github-ABC123', existingAccounts, 'github-ABC123')).toBeNull();
});
});