test(12-02): add pagination + GET-only tests for listAllInvoices/listAllInvoiceItems

- listAllInvoices() concatenates pages in order, size=200 on each request
- listAllInvoiceItems(invoiceId) requests the nested /invoices/{id}/items
  path and concatenates its pages
- Extends the existing GET-only / Authorization-header assertion to both
  new methods (PAX8-08)
This commit is contained in:
lorentz 2026-07-10 22:46:12 -04:00
parent 0920ef8e43
commit 5cfbd13bcb

View file

@ -238,4 +238,101 @@ describe('Pax8Client', () => {
expect(authHeader).toMatch(/^Bearer /);
}
});
it('listAllInvoices() concatenates content across all pages in order', async () => {
const pages = [
{ content: [{ id: 'inv-1' }, { id: 'inv-2' }], totalPages: 2 },
{ content: [{ id: 'inv-3' }], totalPages: 2 },
];
const { fetchMock, calls } = makeMultiPageFetchMock({ resource: 'invoices', pages });
vi.stubGlobal('fetch', fetchMock);
const client = new Pax8Client({ clientId: 'id1', clientSecret: SECRET });
const result = await client.listAllInvoices();
expect(result).toEqual([{ id: 'inv-1' }, { id: 'inv-2' }, { id: 'inv-3' }]);
const invoiceCalls = calls.filter(c => c.url.includes('/invoices') && !c.url.includes('/items'));
expect(invoiceCalls).toHaveLength(2);
for (const c of invoiceCalls) {
expect(c.url).toContain('size=200');
}
});
it('listAllInvoiceItems(invoiceId) requests the nested per-invoice path and concatenates its pages', async () => {
const invoiceId = 'inv-123';
const pages = [
{ content: [{ id: 'item-1' }, { id: 'item-2' }], totalPages: 2 },
{ content: [{ id: 'item-3' }], totalPages: 2 },
];
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: true,
status: 200,
json: async () => ({ access_token: 'tok', token_type: 'Bearer', expires_in: 86400 }),
text: async () => '',
} as unknown as Response;
}
if (url.includes(`/invoices/${invoiceId}/items`)) {
const match = url.match(/page=(\d+)/);
const pageNum = match ? parseInt(match[1], 10) : 0;
const p = pages[pageNum];
return {
ok: true,
status: 200,
json: async () => ({
content: p.content,
page: { size: 200, totalElements: pages.reduce((n, pg) => n + pg.content.length, 0), totalPages: p.totalPages, number: pageNum },
}),
text: async () => '',
} as unknown as Response;
}
return {
ok: true,
status: 200,
json: async () => ({ content: [], page: { size: 200, totalElements: 0, totalPages: 1, number: 0 } }),
text: async () => '',
} as unknown as Response;
});
vi.stubGlobal('fetch', fetchMock);
const client = new Pax8Client({ clientId: 'id1', clientSecret: SECRET });
const result = await client.listAllInvoiceItems(invoiceId);
expect(result).toEqual([{ id: 'item-1' }, { id: 'item-2' }, { id: 'item-3' }]);
const itemCalls = calls.filter(c => c.url.includes(`/invoices/${invoiceId}/items`));
expect(itemCalls).toHaveLength(2);
expect(itemCalls.some(c => c.url.includes('/invoices/inv-123/items'))).toBe(true);
for (const c of itemCalls) {
expect(c.url).toContain('size=200');
}
});
it('listAllInvoices() and listAllInvoiceItems() only issue GET requests with an Authorization: Bearer header', async () => {
const invoicePages = [{ content: [{ id: 'inv-1' }], totalPages: 1 }];
const { fetchMock: invoiceFetchMock, calls: invoiceCalls } = makeMultiPageFetchMock({
resource: 'invoices',
pages: invoicePages,
});
vi.stubGlobal('fetch', invoiceFetchMock);
const client = new Pax8Client({ clientId: 'id1', clientSecret: SECRET });
await client.listAllInvoices();
await client.listAllInvoiceItems('inv-123');
const dataCalls = invoiceCalls.filter(c => !c.url.includes('/token'));
expect(dataCalls.length).toBeGreaterThan(0);
for (const c of dataCalls) {
expect(c.init?.method === undefined || c.init?.method === 'GET').toBe(true);
const authHeader = (c.init!.headers as Record<string, string>)['Authorization'];
expect(authHeader).toMatch(/^Bearer /);
}
});
});