import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest'; vi.mock('./client', () => ({ api: { get: vi.fn(), post: vi.fn(), put: vi.fn(), del: vi.fn() }, apiFetch: vi.fn() })); import { createRequest, listMyRequests, getRequest, cancelRequest, hasInFlightRequest } from './requests'; import { api, apiFetch } from './client'; import { qk } from './queries'; import type { LidarrRequest } from './types'; const mockRow: LidarrRequest = { id: 'r1', user_id: 'u1', status: 'pending', kind: 'album', lidarr_artist_mbid: 'art-mbid', lidarr_album_mbid: 'alb-mbid', lidarr_track_mbid: null, artist_name: 'Aphex Twin', album_title: 'Selected Ambient Works', track_title: null, quality_profile_id: null, root_folder_path: null, decided_at: null, decided_by: null, notes: null, completed_at: null, matched_track_id: null, matched_album_id: null, matched_artist_id: null, requested_at: '2026-01-01T00:00:00Z', updated_at: '2026-01-01T00:00:00Z', imported_album_count: 0, imported_track_count: 0 }; beforeEach(() => { vi.clearAllMocks(); }); afterEach(() => { vi.clearAllMocks(); }); describe('createRequest', () => { test('POSTs /api/requests with full body for an album request', async () => { (api.post as ReturnType).mockResolvedValueOnce(mockRow); const out = await createRequest({ kind: 'album', lidarr_artist_mbid: 'art-mbid', lidarr_album_mbid: 'alb-mbid', artist_name: 'Aphex Twin', album_title: 'Selected Ambient Works' }); expect(api.post).toHaveBeenCalledWith('/api/requests', { kind: 'album', lidarr_artist_mbid: 'art-mbid', artist_name: 'Aphex Twin', lidarr_album_mbid: 'alb-mbid', album_title: 'Selected Ambient Works' }); expect(out).toBe(mockRow); }); test('omits empty / undefined optional MBID + title fields from wire body', async () => { (api.post as ReturnType).mockResolvedValueOnce(mockRow); await createRequest({ kind: 'artist', lidarr_artist_mbid: 'art-mbid', artist_name: 'Aphex Twin', lidarr_album_mbid: '', lidarr_track_mbid: '' }); const body = (api.post as ReturnType).mock.calls[0][1] as Record< string, unknown >; expect(body).toEqual({ kind: 'artist', lidarr_artist_mbid: 'art-mbid', artist_name: 'Aphex Twin' }); expect(body).not.toHaveProperty('lidarr_album_mbid'); expect(body).not.toHaveProperty('lidarr_track_mbid'); expect(body).not.toHaveProperty('album_title'); expect(body).not.toHaveProperty('track_title'); }); test('includes track fields for a track-kind request', async () => { (api.post as ReturnType).mockResolvedValueOnce(mockRow); await createRequest({ kind: 'track', lidarr_artist_mbid: 'art-mbid', lidarr_album_mbid: 'alb-mbid', lidarr_track_mbid: 'trk-mbid', artist_name: 'Aphex Twin', album_title: 'Drukqs', track_title: 'Avril 14th' }); expect(api.post).toHaveBeenCalledWith('/api/requests', { kind: 'track', lidarr_artist_mbid: 'art-mbid', artist_name: 'Aphex Twin', lidarr_album_mbid: 'alb-mbid', lidarr_track_mbid: 'trk-mbid', album_title: 'Drukqs', track_title: 'Avril 14th' }); }); }); describe('listMyRequests', () => { test('GETs /api/requests', async () => { (api.get as ReturnType).mockResolvedValueOnce([mockRow]); const out = await listMyRequests(); expect(api.get).toHaveBeenCalledWith('/api/requests'); expect(out).toEqual([mockRow]); }); }); describe('getRequest', () => { test('GETs /api/requests/:id', async () => { (api.get as ReturnType).mockResolvedValueOnce(mockRow); const out = await getRequest('r1'); expect(api.get).toHaveBeenCalledWith('/api/requests/r1'); expect(out).toBe(mockRow); }); }); describe('cancelRequest', () => { test('DELETEs /api/requests/:id and returns the cancelled row', async () => { const cancelled = { ...mockRow, status: 'rejected' as const }; (apiFetch as ReturnType).mockResolvedValueOnce(cancelled); const out = await cancelRequest('r1'); expect(apiFetch).toHaveBeenCalledWith('/api/requests/r1', { method: 'DELETE' }); expect(out).toBe(cancelled); expect(out.status).toBe('rejected'); }); }); describe('qk.myRequests', () => { test('returns the expected key tuple', () => { expect(qk.myRequests()).toEqual(['myRequests']); }); }); describe('hasInFlightRequest', () => { test('false for undefined / empty', () => { expect(hasInFlightRequest(undefined)).toBe(false); expect(hasInFlightRequest([])).toBe(false); }); test('false when no row is approved', () => { expect( hasInFlightRequest([ { ...mockRow, status: 'pending' }, { ...mockRow, status: 'completed' }, { ...mockRow, status: 'rejected' } ]) ).toBe(false); }); test('true when at least one row is approved (mid-ingest)', () => { expect( hasInFlightRequest([ { ...mockRow, status: 'pending' }, { ...mockRow, status: 'approved' }, { ...mockRow, status: 'completed' } ]) ).toBe(true); }); });