Advertisement

← vibesdk

worker/services/oauth/github.test.ts

3.0 KB Download file
import { describe, it, expect, vi, afterEach } from 'vitest';
import { GitHubOAuthProvider } from './github';

type GitHubEmail = { email: string; verified: boolean; primary: boolean };

function mockGitHubFetch(options: {
	user: { id: number; login: string; email?: string; name?: string };
	emails?: GitHubEmail[];
	emailsStatus?: number;
}) {
	return vi.fn(async (input: RequestInfo | URL) => {
		const url = typeof input === 'string' ? input : input.toString();
		if (url === 'https://api.github.com/user') {
			return new Response(JSON.stringify(options.user), { status: 200 });
		}
		if (url === 'https://api.github.com/user/emails') {
			return new Response(JSON.stringify(options.emails ?? []), {
				status: options.emailsStatus ?? 200,
			});
		}
		throw new Error(`Unexpected fetch to ${url}`);
	});
}

function createProvider() {
	return new GitHubOAuthProvider('client-id', 'client-secret', 'https://app.local/api/auth/callback/github');
}

describe('GitHubOAuthProvider.getUserInfo', () => {
	afterEach(() => {
		vi.restoreAllMocks();
	});

	it('selects the verified primary email and reports it verified', async () => {
		vi.stubGlobal(
			'fetch',
			mockGitHubFetch({
				user: { id: 1, login: 'octocat', name: 'Octo Cat' },
				emails: [
					{ email: 'secondary@example.com', verified: true, primary: false },
					{ email: 'primary@example.com', verified: true, primary: true },
				],
			}),
		);

		const info = await createProvider().getUserInfo('token');

		expect(info.email).toBe('primary@example.com');
		expect(info.emailVerified).toBe(true);
		expect(info.id).toBe('1');
	});

	it('falls back to a verified non-primary email', async () => {
		vi.stubGlobal(
			'fetch',
			mockGitHubFetch({
				user: { id: 2, login: 'octocat' },
				emails: [
					{ email: 'primary@example.com', verified: false, primary: true },
					{ email: 'verified@example.com', verified: true, primary: false },
				],
			}),
		);

		const info = await createProvider().getUserInfo('token');

		expect(info.email).toBe('verified@example.com');
		expect(info.emailVerified).toBe(true);
	});

	it('reports emailVerified=false when no verified email exists', async () => {
		vi.stubGlobal(
			'fetch',
			mockGitHubFetch({
				user: { id: 3, login: 'octocat', email: 'unverified@example.com' },
				emails: [
					{ email: 'unverified@example.com', verified: false, primary: true },
				],
			}),
		);

		const info = await createProvider().getUserInfo('token');

		// Never hardcodes verification; an unverified address must surface as unverified
		// so the auth layer can fail closed.
		expect(info.email).toBe('unverified@example.com');
		expect(info.emailVerified).toBe(false);
	});

	it('throws when no email can be resolved', async () => {
		vi.stubGlobal(
			'fetch',
			mockGitHubFetch({
				user: { id: 4, login: 'octocat' },
				emails: [],
			}),
		);

		await expect(createProvider().getUserInfo('token')).rejects.toThrow(
			/Could not retrieve user email/,
		);
	});
});