test(10-01): add failing tests for Pax8Client token exchange + auth-proof call
This commit is contained in:
parent
5c9cee02af
commit
ed485d8bde
1 changed files with 108 additions and 0 deletions
108
lib/services/pax8-client.test.ts
Normal file
108
lib/services/pax8-client.test.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { Pax8Client } from './pax8-client';
|
||||
|
||||
const SECRET = 'super-secret-value-should-never-leak';
|
||||
|
||||
function makeFetchMock(opts: {
|
||||
tokenOk?: boolean;
|
||||
tokenStatus?: number;
|
||||
tokenBody?: unknown;
|
||||
companiesBody?: unknown;
|
||||
}) {
|
||||
const {
|
||||
tokenOk = true,
|
||||
tokenStatus = 200,
|
||||
tokenBody = { access_token: 'tok', token_type: 'Bearer', expires_in: 86400 },
|
||||
companiesBody = { content: [{ id: 'c1', name: 'Acme' }], page: { size: 10, totalElements: 1, totalPages: 1, number: 0 } },
|
||||
} = opts;
|
||||
|
||||
const calls: Array<{ url: string; init?: RequestInit }> = [];
|
||||
|
||||
const fetchMock = vi.fn(async (url: string, init?: RequestInit) => {
|
||||
calls.push({ url, init });
|
||||
|
||||
if (url.includes('/token')) {
|
||||
return {
|
||||
ok: tokenOk,
|
||||
status: tokenStatus,
|
||||
json: async () => tokenBody,
|
||||
text: async () => `token error status ${tokenStatus}`,
|
||||
} as unknown as Response;
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => companiesBody,
|
||||
text: async () => '',
|
||||
} as unknown as Response;
|
||||
});
|
||||
|
||||
return { fetchMock, calls };
|
||||
}
|
||||
|
||||
describe('Pax8Client', () => {
|
||||
beforeEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('getToken() POSTs a JSON body with grant_type, client_id, client_secret, audience', async () => {
|
||||
const { fetchMock, calls } = makeFetchMock({});
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
const client = new Pax8Client({ clientId: 'id1', clientSecret: SECRET });
|
||||
await client.listCompanies();
|
||||
|
||||
const tokenCall = calls.find(c => c.url.includes('/token'));
|
||||
expect(tokenCall).toBeDefined();
|
||||
expect((tokenCall!.init!.headers as Record<string, string>)['Content-Type']).toBe('application/json');
|
||||
|
||||
const parsedBody = JSON.parse(tokenCall!.init!.body as string);
|
||||
expect(parsedBody.grant_type).toBe('client_credentials');
|
||||
expect(parsedBody.client_id).toBe('id1');
|
||||
expect(parsedBody.client_secret).toBe(SECRET);
|
||||
expect(parsedBody.audience).toBe('https://api.pax8.com');
|
||||
});
|
||||
|
||||
it('reuses the cached token without a second fetch within the cache window', async () => {
|
||||
const { fetchMock, calls } = makeFetchMock({});
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
const client = new Pax8Client({ clientId: 'id1', clientSecret: SECRET });
|
||||
await client.listCompanies();
|
||||
await client.listCompanies();
|
||||
|
||||
const tokenCalls = calls.filter(c => c.url.includes('/token'));
|
||||
expect(tokenCalls).toHaveLength(1);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(3); // 1 token + 2 companies
|
||||
});
|
||||
|
||||
it('throws on a not-ok token response, including the status but never the secret', async () => {
|
||||
const { fetchMock } = makeFetchMock({ tokenOk: false, tokenStatus: 401 });
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
const client = new Pax8Client({ clientId: 'id1', clientSecret: SECRET });
|
||||
|
||||
await expect(client.listCompanies()).rejects.toThrow(/401/);
|
||||
await expect(client.listCompanies()).rejects.not.toThrow(new RegExp(SECRET));
|
||||
});
|
||||
|
||||
it('listCompanies() sends Authorization: Bearer <token> and returns the parsed envelope', async () => {
|
||||
const companiesBody = {
|
||||
content: [{ id: 'c1', name: 'Acme Co' }],
|
||||
page: { size: 10, totalElements: 1, totalPages: 1, number: 0 },
|
||||
};
|
||||
const { fetchMock, calls } = makeFetchMock({ companiesBody });
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
const client = new Pax8Client({ clientId: 'id1', clientSecret: SECRET });
|
||||
const result = await client.listCompanies();
|
||||
|
||||
const companiesCall = calls.find(c => c.url.includes('/companies'));
|
||||
expect(companiesCall).toBeDefined();
|
||||
const authHeader = (companiesCall!.init!.headers as Record<string, string>)['Authorization'];
|
||||
expect(authHeader).toMatch(/^Bearer /);
|
||||
|
||||
expect(result).toEqual(companiesBody);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue