import { describe, it, expect, vi, beforeEach } from 'vitest'; import { dismissDuplicateGroup, duplicatesNextOffset, listDuplicates, listResolvedDuplicates, mergeDuplicateGroup, runDuplicateSweep } from './admin'; import type { AdminDuplicatesResponse } from './types'; vi.mock('./client', () => ({ api: { get: vi.fn(), post: vi.fn() } })); import { api } from './client'; describe('admin duplicates API', () => { beforeEach(() => vi.clearAllMocks()); it('listDuplicates GETs one tab of the paged report', async () => { (api.get as unknown as ReturnType).mockResolvedValueOnce({ groups: [] }); await listDuplicates('cross_release', 25); expect(api.get).toHaveBeenCalledWith( '/api/admin/library/duplicates?view=cross_release&limit=25&offset=25' ); }); it('listResolvedDuplicates GETs what the resolver did', async () => { (api.get as unknown as ReturnType).mockResolvedValueOnce({ items: [] }); await listResolvedDuplicates(50); expect(api.get).toHaveBeenCalledWith('/api/admin/library/duplicates/resolved?limit=25&offset=50'); }); // The next offset counts groups, and stops at the total or on an empty page. it('duplicatesNextOffset pages by groups', () => { const page = (offset: number, n: number, total: number) => ({ offset, total, groups: Array.from({ length: n }, () => ({})) }) as unknown as AdminDuplicatesResponse; expect(duplicatesNextOffset(page(0, 25, 60))).toBe(25); expect(duplicatesNextOffset(page(50, 10, 60))).toBeUndefined(); expect(duplicatesNextOffset(page(25, 0, 60))).toBeUndefined(); }); it('runDuplicateSweep POSTs the trigger', async () => { (api.post as unknown as ReturnType).mockResolvedValueOnce({ started: true }); await runDuplicateSweep(); expect(api.post).toHaveBeenCalledWith('/api/admin/library/duplicates/sweep', {}); }); it('dismissDuplicateGroup POSTs to the group', async () => { (api.post as unknown as ReturnType).mockResolvedValueOnce(undefined); await dismissDuplicateGroup('g/1'); expect(api.post).toHaveBeenCalledWith('/api/admin/library/duplicates/g%2F1/dismiss', {}); }); it('mergeDuplicateGroup POSTs the chosen survivor', async () => { (api.post as unknown as ReturnType).mockResolvedValueOnce({ removed_paths: [] }); await mergeDuplicateGroup('g-1', { survivor_track_id: 't-1', unmonitor: true }); expect(api.post).toHaveBeenCalledWith('/api/admin/library/duplicates/g-1/merge', { survivor_track_id: 't-1', unmonitor: true }); }); });