Initial commit

This commit is contained in:
mats committed 2026-10-19 16:04:17 +02:00
commit afed023152
8152 files changed
+2086461

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { ApiError, apiFetch } from '../src/api/client'
/* fetch is stubbed with Response-shaped objects — the client only needs
status/ok/json, and this keeps the test independent of jsdom's fetch. */
function stubFetch(body: unknown, status = 200) {
const mock = vi.fn().mockResolvedValue({
status,
ok: status >= 200 && status < 300,
statusText: 'status text',
json: () => Promise.resolve(body),
})
vi.stubGlobal('fetch', mock)
return mock
}
afterEach(() => {
vi.unstubAllGlobals()
})
describe('apiFetch', () => {
it('decodes a JSON response', async () => {
stubFetch([{ id: 1, title: 'First' }])
await expect(apiFetch('/api/v1/items')).resolves.toEqual([{ id: 1, title: 'First' }])
})
it('sends method, JSON headers and a JSON body', async () => {
const mock = stubFetch({ id: 1 })
await apiFetch('/api/v1/items', { method: 'POST', body: JSON.stringify({ title: 'x' }) })
const [path, init] = mock.mock.calls[0]! as [string, RequestInit]
expect(path).toBe('/api/v1/items')
expect(init.method).toBe('POST')
expect(init.body).toBe(JSON.stringify({ title: 'x' }))
expect(init.headers).toMatchObject({ 'Content-Type': 'application/json' })
})
it('returns undefined for 204 responses', async () => {
stubFetch(null, 204)
await expect(apiFetch<void>('/api/v1/items/1', { method: 'DELETE' })).resolves.toBeUndefined()
})
it('turns the backend error envelope into an ApiError', async () => {
stubFetch({ error: { code: 'validation_failed', message: 'title: required' } }, 400)
const err = await apiFetch('/api/v1/items', { method: 'POST' }).catch(e => e)
expect(err).toBeInstanceOf(ApiError)
expect(err).toMatchObject({
status: 400,
code: 'validation_failed',
message: 'title: required',
})
})
it('falls back to statusText when the body is not the envelope', async () => {
stubFetch('<html>gateway timeout</html>', 504)
const err = await apiFetch('/api/v1/items').catch(e => e)
expect(err).toMatchObject({ status: 504, code: 'unknown', message: 'status text' })
})
it('wraps network failures (backend down) as a network ApiError', async () => {
vi.stubGlobal(
'fetch',
vi.fn().mockRejectedValue(new TypeError('fetch failed')),
)
const err = await apiFetch('/api/v1/items').catch(e => e)
expect(err).toBeInstanceOf(ApiError)
expect(err.code).toBe('network')
expect(err.message).toContain('Go server')
})
})