import fs from 'fs' import path from 'path' import { describe, it, beforeAll, expect, vitest } from 'vitest' import { createAppWalletClient } from '../../services' import { EntityManagerClient } from '../../services/EntityManager' import { Logger } from '../../services/Logger' import { Storage } from '../../services/Storage' import { StorageNodeSelector } from '../../services/StorageNodeSelector' import { Configuration, Mood, Genre } from '../generated/default' import { PlaylistsApi as GeneratedPlaylistsApi } from '../generated/default/apis/PlaylistsApi' import type { PlaylistResponse } from '../generated/default/models/PlaylistResponse' import { TrackUploadHelper } from '../tracks/TrackUploadHelper' import { PlaylistsApi } from './PlaylistsApi' const wavFile = fs.readFileSync( path.resolve(__dirname, '../../test/wav-file.wav') ) const pngFile = fs.readFileSync( path.resolve(__dirname, '../../test/png-file.png') ) vitest.mock('../../services/EntityManager') vitest.mock('../../services/StorageNodeSelector') vitest.mock('../../services/Storage') vitest.mock('../generated/default/apis/PlaylistsApi') vitest.spyOn(Storage.prototype, 'uploadFile').mockImplementation(() => ({ start: async () => { return { id: 'a', status: 'done', results: { '320': 'a' }, orig_file_cid: 'baeaaaiqsea7fukrfrjrugqts6jqfmqhcb5ruc5pjmdk3anj7amoht4d4gemvq', orig_filename: 'file.wav', probe: { format: { duration: '10' } }, audio_analysis_error_count: 0, audio_analysis_results: {} } }, abort: () => {} })) vitest .spyOn(TrackUploadHelper.prototype, 'generateId') .mockImplementation(async () => { return 1 }) vitest .spyOn(EntityManagerClient.prototype, 'manageEntity') .mockImplementation(async () => { return { blockHash: 'a', blockNumber: 1 } as any }) const mockPlaylistResponse: PlaylistResponse = { latestChainBlock: 0, latestIndexedBlock: 0, latestChainSlotPlays: 0, latestIndexedSlotPlays: 0, signature: '', timestamp: '', version: { service: 'api', version: '1.0' }, data: [ { id: 'x5pJ3Aj', playlistName: 'Test Playlist', permalink: '/test-playlist', isAlbum: false, isImageAutogenerated: false, playlistContents: [ { trackId: 'yyNwXq7', timestamp: 1 } ], repostCount: 0, favoriteCount: 0, totalPlayCount: 0, trackCount: 1, blocknumber: 0, createdAt: '', followeeReposts: [], followeeFavorites: [], hasCurrentUserReposted: false, hasCurrentUserSaved: false, isPrivate: false, updatedAt: '', access: { stream: true }, user: { id: '7eP5n', name: 'Test User', handle: 'testuser', blocknumber: 0, createdAt: '', isAvailable: true, updatedAt: '', followeeCount: 0, followerCount: 0, doesFollowCurrentUser: false, supporterCount: 0, supportingCount: 0, totalAudioBalance: 0, trackCount: 0 } } as any ] } vitest .spyOn(GeneratedPlaylistsApi.prototype, 'getPlaylist') .mockImplementation(async () => mockPlaylistResponse) describe('PlaylistsApi', () => { // TODO: Move this setup out of describe let playlists: PlaylistsApi // eslint-disable-next-line mocha/no-setup-in-describe const audiusWalletClient = createAppWalletClient({ apiKey: '' }) const logger = new Logger() const storageNodeSelector = new StorageNodeSelector({ endpoint: 'https://discoveryprovider.audius.co', logger }) beforeAll(() => { playlists = new PlaylistsApi(new Configuration(), { storage: new Storage({ storageNodeSelector, logger: new Logger() }), entityManager: new EntityManagerClient({ audiusWalletClient, endpoint: 'https://discoveryprovider.audius.co' }) }) vitest.spyOn(console, 'warn').mockImplementation(() => {}) vitest.spyOn(console, 'info').mockImplementation(() => {}) vitest.spyOn(console, 'debug').mockImplementation(() => {}) vitest.spyOn(console, 'error').mockImplementation(() => {}) }) describe('createPlaylist', () => { it('creates a playlist if valid metadata is provided', async () => { const result = await playlists.createPlaylist({ userId: '7eP5n', imageFile: { buffer: pngFile, name: 'coverArt' }, metadata: { playlistName: 'My Playlist', playlistContents: [ { trackId: 'yyNwXq7', timestamp: 1, metadataTimestamp: 1 } ] } }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1, playlistId: '7eP5n' }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.createPlaylist({ userId: '7eP5n', imageFile: { buffer: pngFile, name: 'coverArt' }, metadata: { playlistContents: [ { trackId: 'yyNwXq7', timestamp: 1 } ] } as any }) }).rejects.toThrow() }) }) describe('uploadPlaylist', () => { it('uploads a playlist if valid metadata is provided', async () => { const result = await playlists.uploadPlaylist({ userId: '7eP5n', imageFile: { buffer: pngFile, name: 'coverArt' }, metadata: { playlistName: 'My Playlist', genre: Genre.Acoustic, mood: Mood.Tender }, trackMetadatas: [ { title: 'BachGavotte' } ], audioFiles: [ { buffer: wavFile, name: 'trackArt' } ] }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1, playlistId: '7eP5n' }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.uploadPlaylist({ userId: '7eP5n', imageFile: { buffer: pngFile, name: 'coverArt' }, metadata: {} as any, trackMetadatas: [ { title: 'BachGavotte' } ], audioFiles: [ { buffer: wavFile, name: 'trackArt' } ] }) }).rejects.toThrow() }) }) describe('addTrackToPlaylist', () => { it('adds a track to a playlist if valid metadata is provided', async () => { const result = await playlists.addTrackToPlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj', trackId: 'yyNwXq7' }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1 }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.addTrackToPlaylist({ userId: '7eP5n', trackId: 'yyNwXq7' } as any) }).rejects.toThrow() }) }) describe('removeTrackFromPlaylist', () => { it('removes a track from a playlist if valid metadata is provided', async () => { const result = await playlists.removeTrackFromPlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj', trackIndex: 0 }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1 }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.removeTrackFromPlaylist({ userId: '7eP5n' } as any) }).rejects.toThrow() }) }) describe('publishPlaylist', () => { it('publishes a playlist if valid metadata is provided', async () => { const result = await playlists.publishPlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj' }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1 }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.publishPlaylist({ userId: '7eP5n' } as any) }).rejects.toThrow() }) }) describe('updatePlaylist', () => { it('updates a playlist if valid metadata is provided', async () => { const result = await playlists.updatePlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj', imageFile: { buffer: pngFile, name: 'coverArt' }, metadata: { playlistName: 'My Playlist edited', mood: Mood.Tender, playlistContents: [] } }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1 }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.updatePlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj', imageFile: { buffer: pngFile, name: 'coverArt' }, metadata: { playlistName: 'My Playlist edited', playlistMood: Mood.Tender, mod: Mood.Tender } as any }) }).rejects.toThrow() }) }) describe('deletePlaylist', () => { it('deletes a playlist if valid metadata is provided', async () => { const result = await playlists.deletePlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj' }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1 }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.deletePlaylist({ userId: '7eP5n', playlistId: 1 as any }) }).rejects.toThrow() }) }) describe('favoritePlaylist', () => { it('favorites a playlist if valid metadata is provided', async () => { const result = await playlists.favoritePlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj' }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1 }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.favoritePlaylist({ userId: '7eP5n', playlistId: 1 as any }) }).rejects.toThrow() }) }) describe('unfavoritePlaylist', () => { it('unfavorites a playlist if valid metadata is provided', async () => { const result = await playlists.unfavoritePlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj' }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1 }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.unfavoritePlaylist({ userId: '7eP5n', playlistId: 1 as any }) }).rejects.toThrow() }) }) describe('repostPlaylist', () => { it('reposts a playlist if valid metadata is provided', async () => { const result = await playlists.repostPlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj' }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1 }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.repostPlaylist({ userId: '7eP5n', playlistId: 1 as any }) }).rejects.toThrow() }) }) describe('unrepostPlaylist', () => { it('unreposts a playlist if valid metadata is provided', async () => { const result = await playlists.unrepostPlaylist({ userId: '7eP5n', playlistId: 'x5pJ3Aj' }) expect(result).toStrictEqual({ blockHash: 'a', blockNumber: 1 }) }) it('throws an error if invalid metadata is provided', async () => { await expect(async () => { await playlists.unrepostPlaylist({ userId: '7eP5n', playlistId: 1 as any }) }).rejects.toThrow() }) }) })