import { describe, it, expect, vi, beforeEach } from 'vitest'; vi.mock('../../src/services/igdbClient', () => ({ searchGames: vi.fn(), getGameById: vi.fn(), })); vi.mock('../../src/services/rawgClient', () => ({ searchGames: vi.fn(), getGameById: vi.fn(), })); vi.mock('../../src/services/thegamesdbClient', () => ({ searchGames: vi.fn(), getGameById: vi.fn(), })); import * as igdb from '../../src/services/igdbClient'; import * as rawg from '../../src/services/rawgClient'; import * as tgdb from '../../src/services/thegamesdbClient'; import { enrichGame, searchGames } from '../../src/services/metadataService'; describe('services/metadataService', () => { beforeEach(() => { vi.clearAllMocks(); }); it('prioriza IGDB cuando hay resultados', async () => { (igdb.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 11, name: 'Sonic', slug: 'sonic', releaseDate: '1991-06-23', genres: ['Platform'], coverUrl: 'http://img', source: 'igdb', }, ]); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([]); const res = await enrichGame({ title: 'Sonic' }); expect(res).not.toBeNull(); expect(res?.source).toBe('igdb'); expect(res?.externalIds.igdb).toBe(11); expect(res?.title).toBe('Sonic'); }); it('cae a RAWG cuando IGDB no responde resultados', async () => { (igdb.searchGames as unknown as ReturnType).mockResolvedValue([]); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 22, name: 'Sonic (rawg)', slug: 'sonic-rawg', releaseDate: '1991-06-23', genres: ['Platform'], coverUrl: 'http://img', source: 'rawg', }, ]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([]); const res = await enrichGame({ title: 'Sonic' }); expect(res).not.toBeNull(); expect(res?.source).toBe('rawg'); expect(res?.externalIds.rawg).toBe(22); }); it('retorna null si no hay resultados en ninguna API', async () => { (igdb.searchGames as unknown as ReturnType).mockResolvedValue([]); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([]); const res = await enrichGame({ title: 'Juego inexistente' }); expect(res).toBeNull(); }); describe('searchGames', () => { it('debería buscar en paralelo en IGDB, RAWG y TheGamesDB', async () => { (igdb.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 1, name: 'Super Mario Bros.', slug: 'super-mario-bros', releaseDate: '1985-09-13', genres: ['Platform'], coverUrl: 'http://igdb.com/cover.jpg', source: 'igdb', }, ]); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 2, name: 'Super Mario Bros.', slug: 'super-mario-bros-rawg', releaseDate: '1985-09-13', genres: ['Platform'], coverUrl: 'http://rawg.com/cover.jpg', source: 'rawg', }, ]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 3, name: 'Super Mario Bros.', slug: 'super-mario-bros-tgdb', releaseDate: '1985-09-13', genres: ['Platform'], coverUrl: 'http://tgdb.com/cover.jpg', source: 'thegamesdb', }, ]); const results = await searchGames({ title: 'Super Mario Bros.' }); expect(results.length).toBeGreaterThan(0); expect(igdb.searchGames).toHaveBeenCalledWith('Super Mario Bros.', undefined); expect(rawg.searchGames).toHaveBeenCalledWith('Super Mario Bros.'); expect(tgdb.searchGames).toHaveBeenCalledWith('Super Mario Bros.'); }); it('debería deduplicar resultados por nombre normalizado', async () => { (igdb.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 1, name: 'Super Mario Bros.', slug: 'super-mario-bros', releaseDate: '1985-09-13', genres: ['Platform'], coverUrl: 'http://igdb.com/cover.jpg', source: 'igdb', }, ]); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 2, name: 'Super Mario Bros.', slug: 'super-mario-bros-rawg', releaseDate: '1985-09-13', genres: ['Platform'], coverUrl: 'http://rawg.com/cover.jpg', source: 'rawg', }, ]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([]); const results = await searchGames({ title: 'Super Mario Bros.' }); // Debería haber un solo resultado (prioridad IGDB) expect(results.length).toBe(1); expect(results[0].source).toBe('igdb'); expect(results[0].externalIds.igdb).toBe(1); expect(results[0].externalIds.rawg).toBe(2); }); it('debería priorizar IGDB > RAWG > TheGamesDB en deduplicación', async () => { (igdb.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 1, name: 'Zelda', slug: 'zelda', releaseDate: '1986-02-21', genres: ['Adventure'], coverUrl: 'http://igdb.com/zelda.jpg', source: 'igdb', }, ]); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 2, name: 'Zelda', slug: 'zelda-rawg', releaseDate: '1986-02-21', genres: ['Adventure'], coverUrl: 'http://rawg.com/zelda.jpg', source: 'rawg', }, ]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 3, name: 'Zelda', slug: 'zelda-tgdb', releaseDate: '1986-02-21', genres: ['Adventure'], coverUrl: 'http://tgdb.com/zelda.jpg', source: 'thegamesdb', }, ]); const results = await searchGames({ title: 'Zelda' }); expect(results.length).toBe(1); expect(results[0].source).toBe('igdb'); expect(results[0].externalIds).toEqual({ igdb: 1, rawg: 2, thegamesdb: 3, }); }); it('debería devolver array vacío si no hay resultados', async () => { (igdb.searchGames as unknown as ReturnType).mockResolvedValue([]); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([]); const results = await searchGames({ title: 'Juego inexistente' }); expect(results).toEqual([]); }); it('debería manejar errores de API y continuar con otras fuentes', async () => { (igdb.searchGames as unknown as ReturnType).mockRejectedValue( new Error('IGDB error') ); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 2, name: 'Sonic', slug: 'sonic', releaseDate: '1991-06-23', genres: ['Platform'], coverUrl: 'http://rawg.com/sonic.jpg', source: 'rawg', }, ]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([]); const results = await searchGames({ title: 'Sonic' }); expect(results.length).toBe(1); expect(results[0].source).toBe('rawg'); }); it('debería pasar el parámetro platform a IGDB', async () => { (igdb.searchGames as unknown as ReturnType).mockResolvedValue([]); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([]); await searchGames({ title: 'Mario', platform: 'NES' }); expect(igdb.searchGames).toHaveBeenCalledWith('Mario', 'NES'); expect(rawg.searchGames).toHaveBeenCalledWith('Mario'); expect(tgdb.searchGames).toHaveBeenCalledWith('Mario'); }); it('debería mantener múltiples resultados con nombres diferentes', async () => { (igdb.searchGames as unknown as ReturnType).mockResolvedValue([ { id: 1, name: 'Super Mario Bros.', slug: 'super-mario-bros', releaseDate: '1985-09-13', genres: ['Platform'], coverUrl: 'http://igdb.com/smb.jpg', source: 'igdb', }, { id: 2, name: 'Super Mario Bros. 2', slug: 'super-mario-bros-2', releaseDate: '1988-10-09', genres: ['Platform'], coverUrl: 'http://igdb.com/smb2.jpg', source: 'igdb', }, ]); (rawg.searchGames as unknown as ReturnType).mockResolvedValue([]); (tgdb.searchGames as unknown as ReturnType).mockResolvedValue([]); const results = await searchGames({ title: 'Super Mario' }); expect(results.length).toBe(2); expect(results[0].name).toBe('Super Mario Bros.'); expect(results[1].name).toBe('Super Mario Bros. 2'); }); }); });