describe('BangDream song meta rank summary', () => { it('matches full meta ranking entries for a single song', async () => { jest.resetModules(); jest.doMock( '@/modules/qqbot/plugins/bangdream/src/application/catalog/bangdream-catalog-cache', () => ({ __esModule: true, default: createCatalogFixture(), }), ); const { Server } = await import( '@/modules/qqbot/plugins/bangdream/src/domain/catalog/server.model' ); const songModule = await import( '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model' ); const { Song, getMetaRanking } = songModule; const getSongMetaRankSummary = ( songModule as typeof songModule & { getSongMetaRankSummary?: ( song: InstanceType, withFever: boolean, server: typeof Server.cn, ) => { entries: Array<{ difficulty: number; meta: number; rank: number }>; maxMeta: number; }; } ).getSongMetaRankSummary; const song = new Song(243); const fullRanking = getMetaRanking(true, Server.cn); expect(typeof getSongMetaRankSummary).toBe('function'); const summary = getSongMetaRankSummary(song, true, Server.cn); expect(summary.maxMeta).toBe(fullRanking[0].meta); expect(summary.entries).toEqual( fullRanking .filter((entry) => entry.songId === 243) .map(({ difficulty, meta, rank }) => ({ difficulty, meta, rank })), ); }); it('caches jacket image decoding within a song instance', async () => { const getJacketImageBuffer = jest .fn, [unknown]>() .mockResolvedValue(Buffer.from('fake-png')); const loadImage = jest.fn(async () => ({ height: 1, width: 1, })); jest.resetModules(); jest.doMock( '@/modules/qqbot/plugins/bangdream/src/application/catalog/bangdream-catalog-cache', () => ({ __esModule: true, default: createCatalogFixture(), }), ); jest.doMock( '@/modules/qqbot/plugins/bangdream/src/domain/song/song-resource.repository', () => ({ songResourceRepository: { getChart: jest.fn(), getDetail: jest.fn(), getJacketImageBuffer, getJacketImagePath: jest.fn(), getSongRip: jest.fn(), resolveJacketImageUrl: jest.fn(), }, }), ); jest.doMock('skia-canvas', () => ({ loadImage, })); const { Song } = await import( '@/modules/qqbot/plugins/bangdream/src/domain/song/song.model' ); const song = new Song(243); const first = await song.getSongJacketImage(); const second = await song.getSongJacketImage(); expect(first).toBe(second); expect(getJacketImageBuffer).toHaveBeenCalledTimes(1); expect(loadImage).toHaveBeenCalledTimes(1); }); }); function createCatalogFixture() { return { meta: { 100: createMeta({ 1: 90, 2: 60 }), 243: createMeta({ 1: 50, 2: 70 }), 300: createMeta({ 1: 100, 2: 100 }), 400: createMeta({ 1: 70, 2: 70 }), }, songs: { 100: createSong({ id: 100, publishedCn: true }), 243: createSong({ id: 243, publishedCn: true }), 300: createSong({ id: 300, publishedCn: false }), 400: createSong({ id: 400, publishedCn: true }), }, }; } function createSong({ id, publishedCn, }: { id: number; publishedCn: boolean; }) { return { bandId: 1, bpm: { 1: [{ bpm: 120, end: 1, start: 0 }], 2: [{ bpm: 140, end: 1, start: 0 }], }, closedAt: [], difficulty: { 1: { playLevel: 20 }, 2: { playLevel: 25 }, }, jacketImage: [`song-${id}`], length: 120, musicTitle: [`Song ${id}`, null, null, `歌曲 ${id}`, null], nickname: null, notes: { 1: 500, 2: 700, }, publishedAt: [null, null, null, publishedCn ? 1 : null, null], tag: 'normal', }; } function createMeta(values: Record) { return Object.fromEntries( Object.entries(values).map(([difficulty, value]) => [ difficulty, { 7: [0, value, 0, value], }, ]), ); }