import { describe, it, expect } from 'vitest' import { DiscogsClient, DiscogsAuthError, DiscogsRateLimitError, mapSearchResult, mapRelease, } from '../src/discogs.js' import { discogsSearchFixture, discogsReleaseFixture } from './fixtures.js' function jsonResponse(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' }, }) } function stubFetch(routes: (url: string, init?: any) => Response): typeof fetch { return (async (input: any, init?: any) => routes(String(input), init)) as typeof fetch } describe('DiscogsClient', () => { it('searches by barcode with token and maps results', async () => { const seen: string[] = [] const seenAuth: string[] = [] const c = new DiscogsClient( 'testtoken', stubFetch((_url, init) => { seen.push(_url) seenAuth.push(String(new Headers(init?.headers).get('Authorization'))) return jsonResponse(discogsSearchFixture) }) ) const results = await c.searchByBarcode('5021592210629') expect(seen[0]).toContain('/database/search') expect(seen[0]).toContain('barcode=5021592210629') expect(seen[0]).toContain('type=release') expect(seenAuth[0]).toBe('Discogs token=testtoken') expect(results).toHaveLength(2) expect(results[0]).toEqual({ id: 1001, artist: 'The Cinematic Orchestra', title: 'Motion', year: 1999, formats: ['CD', 'Album'], labels: ['Ninja Tune'], country: 'UK', catno: 'ZENCD012', thumbUrl: 'https://img.discogs.com/small1.jpg', }) }) it('searches by text with optional format filter', async () => { const seen: string[] = [] const c = new DiscogsClient( 't', stubFetch((url) => { seen.push(url) return jsonResponse(discogsSearchFixture) }) ) await c.searchByText('motion', 'Vinyl') expect(seen[0]).toContain('q=motion') expect(seen[0]).toContain('format=Vinyl') }) it('throws DiscogsAuthError on 401', async () => { const c = new DiscogsClient('bad', stubFetch(() => jsonResponse({ message: 'bad' }, 401))) await expect(c.searchByBarcode('123')).rejects.toBeInstanceOf(DiscogsAuthError) }) it('throws DiscogsRateLimitError on 429', async () => { const c = new DiscogsClient('t', stubFetch(() => jsonResponse({}, 429))) await expect(c.searchByBarcode('123')).rejects.toBeInstanceOf(DiscogsRateLimitError) }) it('fetches full release with barcode identifiers', async () => { const c = new DiscogsClient( 't', stubFetch((url) => { expect(url).toContain('/releases/1001') return jsonResponse(discogsReleaseFixture) }) ) const release = await c.getRelease(1001) expect(release.title).toBe('Motion') expect(release.barcodes).toEqual(['5021592210629']) expect(release.coverUrl).toBe('https://img.discogs.com/full1.jpg') }) }) describe('mappers', () => { it('splits search title into artist/title and parses year', () => { const first = discogsSearchFixture.results[0]! const mapped = mapSearchResult(first) expect(mapped.artist).toBe('The Cinematic Orchestra') expect(mapped.title).toBe('Motion') expect(mapped.year).toBe(1999) }) it('maps full release', () => { const mapped = mapRelease(discogsReleaseFixture) expect(mapped.formats).toEqual(['CD']) expect(mapped.labels).toEqual(['Ninja Tune']) expect(mapped.tracklist).toEqual([ { position: '1', title: 'Overture' }, { position: '2', title: 'Theme de Yoyo' }, ]) }) it('does not throw on malformed payloads', () => { expect(() => mapSearchResult(null)).not.toThrow() expect(() => mapRelease(undefined)).not.toThrow() expect(mapRelease({ tracklist: [null, { title: 'X' }] }).tracklist).toEqual([{ position: '', title: 'X' }]) expect(mapSearchResult({ format: [null, 'CD'] }).formats).toEqual(['CD']) expect(new DiscogsAuthError().name).toBe('DiscogsAuthError') expect(new DiscogsRateLimitError().name).toBe('DiscogsRateLimitError') }) })