mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
95 lines
2.8 KiB
TypeScript
95 lines
2.8 KiB
TypeScript
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
import { fetchRemoteData, RemoteFetchError } from './remote-data';
|
|
|
|
/** A minimal Response stand-in for the bits the adapter reads. */
|
|
function fakeResponse(opts: {
|
|
ok: boolean;
|
|
status?: number;
|
|
statusText?: string;
|
|
body?: string;
|
|
contentType?: string | null;
|
|
}): Response {
|
|
return {
|
|
ok: opts.ok,
|
|
status: opts.status ?? (opts.ok ? 200 : 500),
|
|
statusText: opts.statusText ?? '',
|
|
text: () => Promise.resolve(opts.body ?? ''),
|
|
headers: {
|
|
get: (k: string) => (k.toLowerCase() === 'content-type' ? (opts.contentType ?? null) : null),
|
|
},
|
|
} as unknown as Response;
|
|
}
|
|
|
|
afterEach(() => vi.unstubAllGlobals());
|
|
|
|
describe('fetchRemoteData', () => {
|
|
test('returns the body and content-type on success', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(() =>
|
|
Promise.resolve(fakeResponse({ ok: true, body: 'a,b\n1,2', contentType: 'text/csv' })),
|
|
),
|
|
);
|
|
const result = await fetchRemoteData('https://example.com/data.csv');
|
|
expect(result.text).toBe('a,b\n1,2');
|
|
expect(result.contentType).toBe('text/csv');
|
|
});
|
|
|
|
test('classifies a non-2xx response as http with its status', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(() =>
|
|
Promise.resolve(fakeResponse({ ok: false, status: 404, statusText: 'Not Found' })),
|
|
),
|
|
);
|
|
await expect(fetchRemoteData('https://example.com/missing.csv')).rejects.toMatchObject({
|
|
reason: 'http',
|
|
status: 404,
|
|
});
|
|
});
|
|
|
|
test('classifies a rejected fetch (CORS / offline) as network', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(() => Promise.reject(new TypeError('Failed to fetch'))),
|
|
);
|
|
await expect(fetchRemoteData('https://blocked.example/data.csv')).rejects.toMatchObject({
|
|
reason: 'network',
|
|
});
|
|
});
|
|
|
|
test('classifies an empty body as empty', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(() => Promise.resolve(fakeResponse({ ok: true, body: ' \n' }))),
|
|
);
|
|
await expect(fetchRemoteData('https://example.com/blank.csv')).rejects.toMatchObject({
|
|
reason: 'empty',
|
|
});
|
|
});
|
|
|
|
test('classifies an aborted fetch as timeout', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(() => {
|
|
const e = new Error('aborted');
|
|
e.name = 'AbortError';
|
|
return Promise.reject(e);
|
|
}),
|
|
);
|
|
await expect(
|
|
fetchRemoteData('https://slow.example/data.csv', { timeoutMs: 1 }),
|
|
).rejects.toMatchObject({
|
|
reason: 'timeout',
|
|
});
|
|
});
|
|
|
|
test('the thrown error is a RemoteFetchError', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(() => Promise.reject(new TypeError('Failed to fetch'))),
|
|
);
|
|
await expect(fetchRemoteData('https://x.example')).rejects.toBeInstanceOf(RemoteFetchError);
|
|
});
|
|
});
|