import { afterEach, describe, expect, test, vi } from 'vitest'; import { listPlaylists, getPlaylist, createPlaylist, reorderPlaylist, removePlaylistTrack } from './playlists'; function stubFetch(status: number, body: unknown, init: Partial = {}) { const res = new Response( body === null ? null : JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' }, ...init } ); const spy = vi.fn().mockResolvedValue(res); vi.stubGlobal('fetch', spy); return spy; } afterEach(() => { vi.unstubAllGlobals(); }); describe('playlists API helper', () => { test('listPlaylists GETs /api/playlists with default kind=user', async () => { const spy = stubFetch(200, { owned: [], public: [] }); const r = await listPlaylists(); expect(r.owned).toEqual([]); expect(r.public).toEqual([]); const call = spy.mock.calls[0]; expect(call[0]).toBe('/api/playlists?kind=user'); expect((call[1] as RequestInit).method).toBe('GET'); }); test('listPlaylists passes kind=system through the query string', async () => { const spy = stubFetch(200, { owned: [], public: [] }); await listPlaylists('system'); const call = spy.mock.calls[0]; expect(call[0]).toBe('/api/playlists?kind=system'); }); test('createPlaylist POSTs JSON body', async () => { const spy = stubFetch(200, { id: 'p1', name: 'Test' }); const r = await createPlaylist({ name: 'Test' }); expect(r.id).toBe('p1'); const call = spy.mock.calls[0]; expect((call[1] as RequestInit).method).toBe('POST'); expect(JSON.parse((call[1] as RequestInit).body as string)).toEqual({ name: 'Test' }); }); test('reorderPlaylist PUTs ordered_positions', async () => { const spy = stubFetch(200, { id: 'p1', tracks: [] }); await reorderPlaylist('p1', [2, 1, 0]); const call = spy.mock.calls[0]; expect(call[0]).toBe('/api/playlists/p1/tracks'); expect((call[1] as RequestInit).method).toBe('PUT'); expect(JSON.parse((call[1] as RequestInit).body as string)).toEqual({ ordered_positions: [2, 1, 0] }); }); test('removePlaylistTrack DELETEs by position', async () => { const spy = stubFetch(200, { id: 'p1', tracks: [] }); await removePlaylistTrack('p1', 3); const call = spy.mock.calls[0]; expect(call[0]).toBe('/api/playlists/p1/tracks/3'); expect((call[1] as RequestInit).method).toBe('DELETE'); }); test('not_found surfaces as ApiError', async () => { stubFetch(404, { error: 'not_found' }); await expect(getPlaylist('missing')).rejects.toMatchObject({ code: 'not_found' }); }); });