mirror of
https://github.com/wahyd4/heygo.git
synced 2026-08-09 05:15:52 +10:00
152 lines
5.3 KiB
TypeScript
152 lines
5.3 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import {
|
|
ApiClientError,
|
|
approveSubmission,
|
|
listAdminSubmissions,
|
|
listMySubmissions,
|
|
needsChangesSubmission,
|
|
promoteLink,
|
|
rejectSubmission,
|
|
} from '../src/lib/api';
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
function mockFetch(responder: (input: RequestInfo | URL, init?: RequestInit) => {
|
|
status?: number;
|
|
body?: unknown;
|
|
statusText?: string;
|
|
}) {
|
|
const calls: { path: string; init?: RequestInit }[] = [];
|
|
const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
const path = typeof input === 'string' ? input : new URL(input.toString()).pathname;
|
|
calls.push({ path, init });
|
|
const { status = 200, body = {}, statusText = 'OK' } = responder(input, init);
|
|
return new Response(JSON.stringify(body), {
|
|
status,
|
|
statusText,
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
});
|
|
globalThis.fetch = fetchMock as unknown as typeof fetch;
|
|
return { calls, fetchMock };
|
|
}
|
|
|
|
afterEach(() => {
|
|
globalThis.fetch = originalFetch;
|
|
});
|
|
|
|
describe('promotion api client', () => {
|
|
it('promoteLink POSTs privateLinkId, proposedAlias, note to /api/promotions', async () => {
|
|
const { calls } = mockFetch((_input, init) => ({
|
|
status: 201,
|
|
body: { submission: { id: 'sub_1', status: 'pending', proposedAlias: 'docs' } },
|
|
}));
|
|
|
|
const result = await promoteLink({
|
|
privateLinkId: 'priv_1',
|
|
proposedAlias: 'docs',
|
|
note: 'please review',
|
|
});
|
|
|
|
expect(calls[0].path).toBe('/api/promotions');
|
|
expect(calls[0].init?.method).toBe('POST');
|
|
expect(calls[0].init?.credentials).toBe('include');
|
|
const body = JSON.parse(calls[0].init?.body as string);
|
|
expect(body).toEqual({ privateLinkId: 'priv_1', proposedAlias: 'docs', note: 'please review' });
|
|
expect(result.submission.id).toBe('sub_1');
|
|
});
|
|
|
|
it('promoteLink omits the note field when it is empty or undefined', async () => {
|
|
const { calls } = mockFetch(() => ({
|
|
status: 201,
|
|
body: { submission: { id: 'sub_2', status: 'pending' } },
|
|
}));
|
|
|
|
await promoteLink({ privateLinkId: 'priv_1', proposedAlias: 'docs', note: '' });
|
|
|
|
const body = JSON.parse(calls[0].init?.body as string);
|
|
expect(body).not.toHaveProperty('note');
|
|
});
|
|
|
|
it('listMySubmissions GETs /api/promotions/mine with credentials', async () => {
|
|
const { calls } = mockFetch(() => ({
|
|
body: { submissions: [{ id: 'sub_1', status: 'pending', proposedAlias: 'docs' }] },
|
|
}));
|
|
|
|
const result = await listMySubmissions();
|
|
|
|
expect(calls[0].path).toBe('/api/promotions/mine');
|
|
expect(calls[0].init?.method).toBeUndefined();
|
|
expect(calls[0].init?.credentials).toBe('include');
|
|
expect(result.submissions).toHaveLength(1);
|
|
});
|
|
|
|
it('listAdminSubmissions encodes the status filter into the query string', async () => {
|
|
const { calls } = mockFetch(() => ({ body: { submissions: [] } }));
|
|
|
|
await listAdminSubmissions('needs_changes');
|
|
|
|
expect(calls[0].path).toBe('/api/admin/promotions?status=needs_changes');
|
|
});
|
|
|
|
it('approveSubmission POSTs to /api/admin/promotions/:id/approve and returns publicLink', async () => {
|
|
const { calls } = mockFetch(() => ({
|
|
body: {
|
|
submission: { id: 'sub_1', status: 'approved' },
|
|
publicLink: { id: 'pub_1', alias: 'docs', scope: 'public' },
|
|
},
|
|
}));
|
|
|
|
const result = await approveSubmission('sub_1', { reason: 'lgtm' });
|
|
|
|
expect(calls[0].path).toBe('/api/admin/promotions/sub_1/approve');
|
|
expect(calls[0].init?.method).toBe('POST');
|
|
const body = JSON.parse(calls[0].init?.body as string);
|
|
expect(body).toEqual({ reason: 'lgtm' });
|
|
expect(result.publicLink.id).toBe('pub_1');
|
|
});
|
|
|
|
it('rejectSubmission posts an empty object when no reason is given', async () => {
|
|
const { calls } = mockFetch(() => ({
|
|
body: { submission: { id: 'sub_1', status: 'rejected', rejectionReason: null } },
|
|
}));
|
|
|
|
await rejectSubmission('sub_1');
|
|
|
|
expect(calls[0].path).toBe('/api/admin/promotions/sub_1/reject');
|
|
const body = JSON.parse(calls[0].init?.body as string);
|
|
expect(body).toEqual({});
|
|
});
|
|
|
|
it('needsChangesSubmission targets the needs-changes endpoint', async () => {
|
|
const { calls } = mockFetch(() => ({
|
|
body: { submission: { id: 'sub_1', status: 'needs_changes' } },
|
|
}));
|
|
|
|
await needsChangesSubmission('sub_1', { reason: 'tighten description' });
|
|
|
|
expect(calls[0].path).toBe('/api/admin/promotions/sub_1/needs-changes');
|
|
const body = JSON.parse(calls[0].init?.body as string);
|
|
expect(body).toEqual({ reason: 'tighten description' });
|
|
});
|
|
|
|
it('encodes submission ids with special characters in review paths', async () => {
|
|
const { calls } = mockFetch(() => ({ body: { submission: { id: 'a/b', status: 'approved' } } }));
|
|
|
|
await approveSubmission('a/b');
|
|
|
|
expect(calls[0].path).toBe('/api/admin/promotions/a%2Fb/approve');
|
|
});
|
|
|
|
it('surfaces server errors as ApiClientError', async () => {
|
|
mockFetch(() => ({ status: 409, body: { error: 'A pending submission for this link and alias already exists' } }));
|
|
|
|
await expect(promoteLink({ privateLinkId: 'priv_1', proposedAlias: 'docs' }))
|
|
.rejects.toMatchObject({
|
|
name: 'ApiClientError',
|
|
status: 409,
|
|
message: 'A pending submission for this link and alias already exists',
|
|
});
|
|
});
|
|
});
|