diff --git a/src/node/consumer/src/lib/interfaces/torrent_entries_service.ts b/src/node/consumer/src/lib/interfaces/torrent_entries_service.ts index f05268a..032be9b 100644 --- a/src/node/consumer/src/lib/interfaces/torrent_entries_service.ts +++ b/src/node/consumer/src/lib/interfaces/torrent_entries_service.ts @@ -1,12 +1,12 @@ import {IParsedTorrent} from "@interfaces/parsed_torrent"; -import {ITorrentAttributes} from "@repository/interfaces/torrent_attributes"; +import {ITorrentAttributes, ITorrentCreationAttributes} from "@repository/interfaces/torrent_attributes"; import {SkipTorrent} from "@repository/models/skipTorrent"; import {Torrent} from "@repository/models/torrent"; export interface ITorrentEntriesService { createTorrentEntry(torrent: IParsedTorrent, overwrite: boolean): Promise; - createSkipTorrentEntry(torrent: Torrent): Promise<[SkipTorrent, boolean | null]>; + createSkipTorrentEntry(torrent: ITorrentCreationAttributes): Promise<[SkipTorrent, boolean | null]>; getStoredTorrentEntry(torrent: Torrent): Promise; diff --git a/src/node/consumer/src/lib/services/torrent_entries_service.ts b/src/node/consumer/src/lib/services/torrent_entries_service.ts index 17124d5..2bea7f1 100644 --- a/src/node/consumer/src/lib/services/torrent_entries_service.ts +++ b/src/node/consumer/src/lib/services/torrent_entries_service.ts @@ -110,7 +110,7 @@ export class TorrentEntriesService implements ITorrentEntriesService { .then(() => this.logger.info(`Created ${torrent.provider} entry for [${torrent.infoHash}] ${torrent.title}`)); }; - public createSkipTorrentEntry: (torrent: Torrent) => Promise<[SkipTorrent, boolean | null]> = async (torrent: Torrent) => this.repository.createSkipTorrent(torrent.dataValues); + public createSkipTorrentEntry: (torrent: ITorrentCreationAttributes) => Promise<[SkipTorrent, boolean | null]> = async (torrent: ITorrentCreationAttributes) => this.repository.createSkipTorrent(torrent); public getStoredTorrentEntry = async (torrent: Torrent): Promise => this.repository.getSkipTorrent(torrent.infoHash) .catch(() => this.repository.getTorrent(torrent.dataValues)) @@ -173,8 +173,6 @@ export class TorrentEntriesService implements ITorrentEntriesService { return {}; }); - this.assignMetaIds(fileCollection, imdbId, kitsuId); - if (!fileCollection.contents || !fileCollection.contents.length) { return; } @@ -206,10 +204,13 @@ export class TorrentEntriesService implements ITorrentEntriesService { } return Promise.resolve(); }) - .then(() => PromiseHelpers.sequence(fileCollection.videos!.map(video => (): Promise => { - const newVideo: IFileCreationAttributes = {...video, infoHash: video.infoHash, title: video.title}; - return this.repository.createFile(newVideo) - }))) + .then(() => { + const promises = fileCollection.videos!.map(video => { + const newVideo: IFileCreationAttributes = {...video, infoHash: video.infoHash, title: video.title}; + return this.repository.createFile(newVideo); + }); + return Promise.all(promises); + }) .then(() => this.logger.info(`Created contents for ${torrent.provider} [${torrent.infoHash}] ${torrent.title}`)) .catch(error => this.logger.error(`Failed saving contents for [${torrent.infoHash}] ${torrent.title}`, error)); }; @@ -248,7 +249,7 @@ export class TorrentEntriesService implements ITorrentEntriesService { }; private assignMetaIds = (fileCollection: ITorrentFileCollection, imdbId: string | undefined, kitsuId: number): ITorrentFileCollection => { - if (fileCollection.videos && fileCollection.videos.length) { + if (fileCollection && fileCollection.videos && fileCollection.videos.length) { fileCollection.videos.forEach(video => { video.imdbId = imdbId || ''; video.kitsuId = kitsuId || 0; diff --git a/src/node/consumer/test/cache_service.test.ts b/src/node/consumer/test/cache_service.test.ts index 79acdf2..d32bb9a 100644 --- a/src/node/consumer/test/cache_service.test.ts +++ b/src/node/consumer/test/cache_service.test.ts @@ -47,6 +47,7 @@ describe('CacheService Tests', () => { cacheMethod: CacheMethod; beforeEach(() => { + jest.clearAllMocks(); process.env.LOG_LEVEL = 'debug'; loggingService = jest.requireMock('@services/logging_service'); cacheMethod = jest.fn().mockResolvedValue({}); diff --git a/src/node/consumer/test/logging_service.test.ts b/src/node/consumer/test/logging_service.test.ts index 6dff068..975d45e 100644 --- a/src/node/consumer/test/logging_service.test.ts +++ b/src/node/consumer/test/logging_service.test.ts @@ -19,6 +19,7 @@ describe('LoggingService Tests', () => { mockLogger: any; beforeEach(() => { + jest.clearAllMocks(); service = new LoggingService(); mockLogger = (service as any).logger; }); diff --git a/src/node/consumer/test/metadata_service.test.ts b/src/node/consumer/test/metadata_service.test.ts index 64a88d2..3fa868a 100644 --- a/src/node/consumer/test/metadata_service.test.ts +++ b/src/node/consumer/test/metadata_service.test.ts @@ -23,6 +23,7 @@ const server = setupServer( beforeAll(() => server.listen()) beforeEach(() => { + jest.clearAllMocks(); jest.spyOn(Date, 'now').mockImplementation(() => 1234567890); }) afterEach(() => () => { diff --git a/src/node/consumer/test/process_torrent_job.test.ts b/src/node/consumer/test/process_torrent_job.test.ts index ec4c2ea..0eac22b 100644 --- a/src/node/consumer/test/process_torrent_job.test.ts +++ b/src/node/consumer/test/process_torrent_job.test.ts @@ -53,6 +53,7 @@ describe('ProcessTorrentsJob Tests', () => { torrentProcessingService: ITorrentProcessingService; beforeEach(() => { + jest.clearAllMocks(); loggingService = jest.requireMock('@services/logging_service'); torrentProcessingService = jest.requireMock('@services/torrent_processing_service'); processTorrentsJob = new ProcessTorrentsJob(torrentProcessingService, loggingService); diff --git a/src/node/consumer/test/torrent_download_service.test.ts b/src/node/consumer/test/torrent_download_service.test.ts index ef13ca6..f513d88 100644 --- a/src/node/consumer/test/torrent_download_service.test.ts +++ b/src/node/consumer/test/torrent_download_service.test.ts @@ -25,6 +25,7 @@ describe('TorrentDownloadService', () => { mockLoggingService: ILoggingService; beforeEach(() => { + jest.clearAllMocks(); mockLoggingService = jest.requireMock('@services/logging_service'); torrentDownloadService = new TorrentDownloadService(mockLoggingService); }); diff --git a/src/node/consumer/test/torrent_entries_service.test.ts b/src/node/consumer/test/torrent_entries_service.test.ts index 604aa36..50d0188 100644 --- a/src/node/consumer/test/torrent_entries_service.test.ts +++ b/src/node/consumer/test/torrent_entries_service.test.ts @@ -7,10 +7,11 @@ import {ITorrentFileCollection} from "@interfaces/torrent_file_collection"; import {ITorrentFileService} from "@interfaces/torrent_file_service"; import {ITorrentSubtitleService} from "@interfaces/torrent_subtitle_service"; import {IDatabaseRepository} from "@repository/interfaces/database_repository"; +import {IFileAttributes} from "@repository/interfaces/file_attributes"; +import {ITorrentCreationAttributes} from "@repository/interfaces/torrent_attributes"; +import {Torrent} from "@repository/models/torrent"; import {TorrentEntriesService} from "@services/torrent_entries_service"; - - jest.mock('@services/logging_service', () => { return { error: jest.fn(), @@ -22,19 +23,21 @@ jest.mock('@services/logging_service', () => { jest.mock('@services/torrent_file_service', () => { return { - parseTorrentFiles: jest.fn(), + parseTorrentFiles: jest.fn().mockResolvedValue(undefined), + isPackTorrent: jest.fn().mockResolvedValue(undefined), } }) jest.mock('@services/metadata_service', () => { return { - getImdbId: jest.fn(), + getImdbId: jest.fn().mockResolvedValue(undefined), + getKitsuId: jest.fn().mockResolvedValue(undefined), } }) jest.mock('@services/torrent_subtitle_service', () => { return { - assignSubtitles: jest.fn(), + assignSubtitles: jest.fn().mockResolvedValue(undefined), } }) @@ -42,6 +45,11 @@ jest.mock('@repository/database_repository', () => { return { createTorrent: jest.fn().mockResolvedValue(undefined), createFile: jest.fn().mockResolvedValue(undefined), + createSkipTorrent: jest.fn().mockResolvedValue(undefined), + getSkipTorrent: jest.fn().mockResolvedValue(undefined), + getTorrent: jest.fn().mockResolvedValue(undefined), + setTorrentSeeders: jest.fn().mockResolvedValue(undefined), + getFiles: jest.fn().mockResolvedValue(undefined), } }) @@ -54,12 +62,14 @@ describe('TorrentEntriesService Tests', () => { mockDatabaseRepository: IDatabaseRepository; beforeEach(() => { + jest.clearAllMocks(); + mockFileService = jest.requireMock('@services/torrent_file_service'); mockMetadataService = jest.requireMock('@services/metadata_service'); mockSubtitleService = jest.requireMock('@services/torrent_subtitle_service'); mockLoggingService = jest.requireMock('@services/logging_service'); mockDatabaseRepository = jest.requireMock('@repository/database_repository'); - torrentEntriesService = new TorrentEntriesService(mockMetadataService, mockLoggingService, mockFileService , mockSubtitleService, mockDatabaseRepository); + torrentEntriesService = new TorrentEntriesService(mockMetadataService, mockLoggingService, mockFileService , mockSubtitleService, mockDatabaseRepository); }); it('should create a torrent entry', async () => { @@ -106,4 +116,231 @@ describe('TorrentEntriesService Tests', () => { expect(mockSubtitleService.assignSubtitles).toHaveReturnedWith(Promise.resolve(fileCollectionWithSubtitles)); expect(mockDatabaseRepository.createTorrent).toHaveBeenCalledWith(expect.objectContaining(torrent)); }); + + it('should assign imdbId correctly', async () => { + const torrent : IParsedTorrent = { + title: 'Test title', + provider: 'Test provider', + infoHash: 'Test infoHash', + type: TorrentType.Movie, + }; + + const fileCollection : ITorrentFileCollection = { + videos: [{ + fileIndex: 0, + title: 'Test video', + size: 123456, + imdbId: 'tt1234567', + }], + contents: [], + subtitles: [], + }; + + const fileCollectionWithSubtitles : ITorrentFileCollection = { + ...fileCollection, + subtitles: [ { + fileId: 0, + title: 'Test subtitle', + fileIndex: 0, + path: 'Test path', + infoHash: 'Test infoHash', + }], + }; + + (mockMetadataService.getImdbId as jest.Mock).mockResolvedValue('tt1234567'); + (mockFileService.parseTorrentFiles as jest.Mock).mockResolvedValue(fileCollection); + (mockSubtitleService.assignSubtitles as jest.Mock).mockResolvedValue(fileCollectionWithSubtitles); + (mockDatabaseRepository.createTorrent as jest.Mock).mockResolvedValue(torrent); + + await torrentEntriesService.createTorrentEntry(torrent); + + expect(torrent.imdbId).toEqual('tt1234567'); + expect(torrent.kitsuId).toEqual(undefined); + }); + + it('should assign kitsuId correctly', async () => { + const torrent : IParsedTorrent = { + title: 'Test title', + provider: 'Test provider', + infoHash: 'Test infoHash', + type: TorrentType.Anime, + }; + + const fileCollection : ITorrentFileCollection = { + videos: [{ + fileIndex: 0, + title: 'Test video', + size: 123456, + kitsuId: 11 + }], + contents: [], + subtitles: [], + }; + + const fileCollectionWithSubtitles : ITorrentFileCollection = { + ...fileCollection, + subtitles: [ { + fileId: 0, + title: 'Test subtitle', + fileIndex: 0, + path: 'Test path', + infoHash: 'Test infoHash', + }], + }; + + (mockMetadataService.getKitsuId as jest.Mock).mockResolvedValue(11); + (mockFileService.parseTorrentFiles as jest.Mock).mockResolvedValue(fileCollection); + (mockSubtitleService.assignSubtitles as jest.Mock).mockResolvedValue(fileCollectionWithSubtitles); + (mockDatabaseRepository.createTorrent as jest.Mock).mockResolvedValue(torrent); + + await torrentEntriesService.createTorrentEntry(torrent); + + expect(torrent.imdbId).toEqual(undefined); + expect(torrent.kitsuId).toEqual(11); + }); + + it('should create a skip torrent entry', async () => { + const torrent: ITorrentCreationAttributes = { + infoHash: 'Test infoHash', + provider: 'Test provider', + title: 'Test title', + type: TorrentType.Movie, + }; + + (mockDatabaseRepository.createSkipTorrent as jest.Mock).mockResolvedValue([torrent, null]); + + const result = await torrentEntriesService.createSkipTorrentEntry(torrent); + + expect(mockDatabaseRepository.createSkipTorrent).toHaveBeenCalledWith(torrent); + expect(result).toEqual([torrent, null]); + }); + + it('should get stored torrent entry', async () => { + const torrent = { + infoHash: 'Test infoHash', + provider: 'Test provider', + title: 'Test title', + type: TorrentType.Movie, + dataValues: { + infoHash: 'Test infoHash', + provider: 'Test provider', + title: 'Test title', + type: TorrentType.Movie, + } + } as Torrent; + + (mockDatabaseRepository.getSkipTorrent as jest.Mock).mockRejectedValue(undefined); + (mockDatabaseRepository.getTorrent as jest.Mock).mockResolvedValue(torrent); + + const result = await torrentEntriesService.getStoredTorrentEntry(torrent); + + expect(mockDatabaseRepository.getSkipTorrent).toHaveBeenCalledWith(torrent.infoHash); + expect(mockDatabaseRepository.getTorrent).toHaveBeenCalledWith(torrent.dataValues); + expect(result).toEqual(torrent); + }); + + it('should check and update torrent', async () => { + const torrent : IParsedTorrent = { + title: 'Test title', + provider: 'Test provider', + infoHash: 'Test infoHash', + type: TorrentType.Movie, + seeders: 1, + }; + + const files : IFileAttributes[] = [{ + infoHash: 'Test infoHash', + fileIndex: 0, + title: 'Test title', + path: 'Test path', + size: 123456, + },{ + infoHash: 'Test infoHash 2', + fileIndex: 1, + title: 'Test title 2', + path: 'Test path 2', + size: 234567, + }]; + + const torrentInstance = { + ...torrent, + dataValues:{ ...torrent}, + save: jest.fn().mockResolvedValue(torrent), + }; + + const filesInstance = { + ...files, + dataValues:{ ...files}, + save: jest.fn().mockResolvedValue(files), + }; + + const seedersResponse = [1]; + + (mockDatabaseRepository.getTorrent as jest.Mock).mockResolvedValue(torrentInstance); + + (mockDatabaseRepository.setTorrentSeeders as jest.Mock).mockResolvedValue(seedersResponse); + (mockDatabaseRepository.getFiles as jest.Mock).mockResolvedValue(filesInstance) + + const result = await torrentEntriesService.checkAndUpdateTorrent(torrent); + + expect(mockDatabaseRepository.getTorrent).toHaveBeenCalledWith({ + infoHash: torrent.infoHash, + provider: torrent.provider + }); + + expect(mockDatabaseRepository.getFiles).toHaveBeenCalledWith(torrent.infoHash); + expect(mockDatabaseRepository.setTorrentSeeders).toHaveBeenCalledWith(torrentInstance.dataValues, 1); + expect(result).toEqual(true); + }); + + it('should create torrent contents', async () => { + const torrent = { + infoHash: 'Test infoHash', + provider: 'Test provider', + title: 'Test title', + type: TorrentType.Movie, + dataValues: { + infoHash: 'Test infoHash', + provider: 'Test provider', + title: 'Test title', + type: TorrentType.Movie, + } + } as Torrent; + + const fileCollection : ITorrentFileCollection = { + videos: [{ + id: 1, + title: 'Test video', + size: 123456, + imdbId: 'tt1234567', + infoHash: 'Test infoHash', + }], + contents: [], + subtitles: [], + }; + + const fileCollectionWithContents : ITorrentFileCollection = { + ...fileCollection, + contents: [{ + size: 123456, + fileIndex: 0, + path: 'Test path', + infoHash: 'Test infoHash', + }], + }; + + (mockDatabaseRepository.getFiles as jest.Mock).mockResolvedValue(fileCollection.videos); + (mockFileService.parseTorrentFiles as jest.Mock).mockResolvedValue(fileCollectionWithContents); + (mockSubtitleService.assignSubtitles as jest.Mock).mockResolvedValue(fileCollectionWithContents); + (mockDatabaseRepository.createFile as jest.Mock).mockResolvedValue(Promise.resolve()); + (mockDatabaseRepository.createTorrent as jest.Mock).mockResolvedValue(torrent); + + await torrentEntriesService.createTorrentContents(torrent); + + const newTorrentFiles = await (mockDatabaseRepository.createTorrent as jest.Mock).mock.calls[0][0].files; + + newTorrentFiles.forEach(file => { + expect(mockDatabaseRepository.createFile).toHaveBeenCalledWith(file); + }); + }); }); \ No newline at end of file diff --git a/src/node/consumer/test/tracker_service.test.ts b/src/node/consumer/test/tracker_service.test.ts index 3fe9981..71b7353 100644 --- a/src/node/consumer/test/tracker_service.test.ts +++ b/src/node/consumer/test/tracker_service.test.ts @@ -24,6 +24,7 @@ jest.mock('@services/cache_service', () => { beforeAll(() => server.listen()) beforeEach(() => { + jest.clearAllMocks(); jest.spyOn(Date, 'now').mockImplementation(() => 1234567890); }) afterEach(() => () => {