import { http, HttpResponse } from 'msw' import { describe, expect, it } from 'vitest' import { server } from '../test/server' import { ApiError, api } from './api' describe('api.postJson', () => { it('sends application/json and returns the parsed body', async () => { let sentContentType: string | null = null server.use( http.post('http://localhost/api/login', async ({ request }) => { sentContentType = request.headers.get('Content-Type') return HttpResponse.json({ id: 'u1', email: 'a@b.com' }) }), ) const result = await api.postJson<{ id: string; email: string }>('/login', { username: 'a@b.com', password: 'secret', }) expect(sentContentType).toBe('application/json') expect(result).toEqual({ id: 'u1', email: 'a@b.com' }) }) it('throws ApiError on a non-2xx response', async () => { server.use( http.post('http://localhost/api/login', () => HttpResponse.json({ message: 'bad creds' }, { status: 401 }), ), ) const error = await api.postJson('/login', {}).catch((e: unknown) => e) expect(error).toBeInstanceOf(ApiError) expect((error as ApiError).status).toBe(401) }) }) describe('api path guard', () => { it('throws if a path already includes the /api prefix', async () => { // Callers pass `/login`, not `/api/login` — the client prepends BASE. await expect(api.get('/api/login')).rejects.toThrow(/must not include/i) }) }) describe('api.get', () => { it('sends Accept: application/ld+json and returns the parsed body', async () => { let accept: string | null = null server.use( http.get('http://localhost/api/scenarios', ({ request }) => { accept = request.headers.get('Accept') return HttpResponse.json({ member: [] }) }), ) const result = await api.get<{ member: unknown[] }>('/scenarios') expect(accept).toBe('application/ld+json') expect(result).toEqual({ member: [] }) }) }) describe('api.post', () => { it('sends Content-Type: application/ld+json (API Platform write format)', async () => { let contentType: string | null = null server.use( http.post('http://localhost/api/buckets', async ({ request }) => { contentType = request.headers.get('Content-Type') return HttpResponse.json({ '@id': '/api/buckets/1' }, { status: 201 }) }), ) await api.post('/buckets', { name: 'Rent' }) expect(contentType).toBe('application/ld+json') }) }) describe('api.patch', () => { it('sends Content-Type: application/merge-patch+json', async () => { let contentType: string | null = null server.use( http.patch('http://localhost/api/buckets/1', async ({ request }) => { contentType = request.headers.get('Content-Type') return HttpResponse.json({ '@id': '/api/buckets/1' }) }), ) await api.patch('/buckets/1', { name: 'Rent' }) expect(contentType).toBe('application/merge-patch+json') }) }) describe('api.delete', () => { it('resolves to undefined on a 204 No Content', async () => { server.use( http.delete( 'http://localhost/api/buckets/1', () => new HttpResponse(null, { status: 204 }), ), ) const result = await api.delete('/buckets/1') expect(result).toBeUndefined() }) })