import { describe, expect, test, vi, beforeEach } from 'vitest'; vi.mock('./client', () => ({ api: { get: vi.fn(), post: vi.fn(), put: vi.fn(), del: vi.fn() } })); import { searchLidarr } from './lidarr'; import { api } from './client'; import { qk } from './queries'; import type { LidarrSearchResult } from './types'; beforeEach(() => { vi.clearAllMocks(); }); describe('searchLidarr', () => { test('builds URL with q and kind query params', async () => { (api.get as ReturnType).mockResolvedValueOnce([]); await searchLidarr('aphex twin', 'artist'); expect(api.get).toHaveBeenCalledWith('/api/lidarr/search?q=aphex+twin&kind=artist'); }); test('encodes special characters in q (quotes, ampersands, plus)', async () => { (api.get as ReturnType).mockResolvedValueOnce([]); await searchLidarr('boards of canada & friends', 'album'); const calledWith = (api.get as ReturnType).mock.calls[0][0] as string; // URLSearchParams encodes space as '+' and '&' as '%26'. expect(calledWith).toBe( '/api/lidarr/search?q=boards+of+canada+%26+friends&kind=album' ); }); test('passes through results unchanged', async () => { const fixture: LidarrSearchResult[] = [ { mbid: 'mbid-1', name: 'Aphex Twin', secondary_text: 'Electronic', image_url: 'https://example.test/img.jpg', artist_mbid: '', album_mbid: '', in_library: false, requested: false } ]; (api.get as ReturnType).mockResolvedValueOnce(fixture); const out = await searchLidarr('aphex', 'artist'); expect(out).toBe(fixture); }); test('propagates errors from api.get', async () => { const err = { code: 'lidarr_unavailable', message: 'down', status: 503 }; (api.get as ReturnType).mockRejectedValueOnce(err); await expect(searchLidarr('x', 'track')).rejects.toMatchObject({ code: 'lidarr_unavailable', status: 503 }); }); }); describe('qk.lidarrSearch', () => { test('keys include q + kind', () => { expect(qk.lidarrSearch('foo', 'artist')).toEqual([ 'lidarrSearch', { q: 'foo', kind: 'artist' } ]); }); });