start adding metadata tests
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
const { pathsToModuleNameMapper } = require('ts-jest');
|
||||
const { compilerOptions } = require('./tsconfig.json');
|
||||
const {pathsToModuleNameMapper} = require('ts-jest');
|
||||
const {compilerOptions} = require('./tsconfig.json');
|
||||
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/src/' }),
|
||||
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {prefix: '<rootDir>/src/'}),
|
||||
modulePaths: [
|
||||
'<rootDir>'
|
||||
],
|
||||
|
||||
@@ -7,4 +7,5 @@ export interface ICacheService {
|
||||
cacheWrapMetadata: (id: string, method: CacheMethod) => Promise<any>;
|
||||
cacheTrackers: (method: CacheMethod) => Promise<any>;
|
||||
}
|
||||
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
@@ -1,8 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export interface ILoggingService {
|
||||
info(message: string, ...args: any[]): void;
|
||||
|
||||
error(message: string, ...args: any[]): void;
|
||||
|
||||
debug(message: string, ...args: any[]): void;
|
||||
|
||||
warn(message: string, ...args: any[]): void;
|
||||
}
|
||||
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
@@ -29,4 +29,5 @@ export class LoggingService implements ILoggingService {
|
||||
this.logger.warn(message, args);
|
||||
};
|
||||
}
|
||||
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
@@ -70,8 +70,15 @@ export class MetadataService implements IMetadataService {
|
||||
|
||||
const key = Number.isInteger(query.id) || query.id.toString().match(/^\d+$/) ? `kitsu:${query.id}` : query.id;
|
||||
const metaType = query.type === TorrentType.Movie ? TorrentType.Movie : TorrentType.Series;
|
||||
return this.cacheService.cacheWrapMetadata(key.toString(), () => this.requestKitsuMetadata(`${KITSU_URL}/meta/${metaType}/${key}.json`)
|
||||
.catch(() => this.requestCinemetaMetadata(`${CINEMETA_URL}/meta/${metaType}/${key}.json`))
|
||||
const isImdbId = Boolean(key.toString().match(/^tt\d+$/));
|
||||
|
||||
return this.cacheService.cacheWrapMetadata(key.toString(), () => {
|
||||
switch (isImdbId) {
|
||||
case true:
|
||||
return this.requestCinemetaMetadata(`${CINEMETA_URL}/meta/imdb/${key}.json`);
|
||||
default:
|
||||
return this.requestKitsuMetadata(`${KITSU_URL}/meta/${metaType}/${key}.json`)
|
||||
}})
|
||||
.catch(() => {
|
||||
// try different type in case there was a mismatch
|
||||
const otherType = metaType === TorrentType.Movie ? TorrentType.Series : TorrentType.Movie;
|
||||
@@ -79,7 +86,7 @@ export class MetadataService implements IMetadataService {
|
||||
})
|
||||
.catch((error) => {
|
||||
throw new Error(`failed metadata query ${key} due: ${error.message}`);
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
public isEpisodeImdbId = async (imdbId: string | undefined): Promise<boolean> => {
|
||||
@@ -174,7 +181,7 @@ export class MetadataService implements IMetadataService {
|
||||
.filter(entry => entry.season !== null && entry.season !== 0 && entry.episode !== 0)
|
||||
.sort((a, b) => (a.season || 0) - (b.season || 0))
|
||||
.reduce((map: Record<number, number>, next) => {
|
||||
if(next.season || next.season === 0) {
|
||||
if (next.season || next.season === 0) {
|
||||
map[next.season] = (map[next.season] || 0) + 1;
|
||||
}
|
||||
return map;
|
||||
|
||||
@@ -62,7 +62,8 @@ export class TorrentDownloadService implements ITorrentDownloadService {
|
||||
this.logger.debug(`Adding torrent with infoHash ${torrent.infoHash} to torrent engine...`);
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
engine.destroy(() => {});
|
||||
engine.destroy(() => {
|
||||
});
|
||||
reject(new Error('No available connections for torrent!'));
|
||||
}, timeout);
|
||||
|
||||
@@ -78,7 +79,8 @@ export class TorrentDownloadService implements ITorrentDownloadService {
|
||||
|
||||
resolve(files);
|
||||
clearTimeout(timeoutId);
|
||||
engine.destroy(() => {});
|
||||
engine.destroy(() => {
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -94,13 +96,13 @@ export class TorrentDownloadService implements ITorrentDownloadService {
|
||||
const minRedundantRatio = videos.length <= 3 ? 30 : Number.MAX_VALUE;
|
||||
|
||||
const isSample = (video: ITorrentFile): boolean => video.path?.toString()?.match(/sample|bonus|promo/i) && maxSize / video.length > minSampleRatio || false;
|
||||
const isRedundant = (video: ITorrentFile):boolean => maxSize / video.length > minRedundantRatio;
|
||||
const isRedundant = (video: ITorrentFile): boolean => maxSize / video.length > minRedundantRatio;
|
||||
const isExtra = (video: ITorrentFile): boolean => /extras?\//i.test(video.path?.toString() || "");
|
||||
const isAnimeExtra = (video: ITorrentFile): boolean => {
|
||||
if (!video.path || !video.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return video.path.toString()?.match(/(?:\b|_)(?:NC)?(?:ED|OP|PV)(?:v?\d\d?)?(?:\b|_)/i)
|
||||
&& maxSize / parseInt(video.length.toString()) > minAnimeExtraRatio || false;
|
||||
};
|
||||
@@ -108,7 +110,7 @@ export class TorrentDownloadService implements ITorrentDownloadService {
|
||||
if (!video.path || !video.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return video.path.toString()?.match(/^[A-Z-]+(?:\.[A-Z]+)?\.\w{3,4}$/)
|
||||
&& maxSize / parseInt(video.length.toString()) > minAnimeExtraRatio || false;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export class TorrentEntriesService implements ITorrentEntriesService {
|
||||
this.logger.warn(`No title found for ${torrent.provider} [${torrent.infoHash}]`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const titleInfo = parse(torrent.title);
|
||||
|
||||
if (!torrent.imdbId && torrent.type !== TorrentType.Anime) {
|
||||
@@ -98,7 +98,7 @@ export class TorrentEntriesService implements ITorrentEntriesService {
|
||||
contents: fileCollection.contents,
|
||||
subtitles: fileCollection.subtitles
|
||||
});
|
||||
|
||||
|
||||
return this.repository.createTorrent(newTorrent)
|
||||
.then(() => PromiseHelpers.sequence(fileCollection.videos!.map(video => () => {
|
||||
const newVideo: IFileCreationAttributes = {...video, infoHash: video.infoHash, title: video.title};
|
||||
@@ -110,22 +110,7 @@ export class TorrentEntriesService implements ITorrentEntriesService {
|
||||
.then(() => this.logger.info(`Created ${torrent.provider} entry for [${torrent.infoHash}] ${torrent.title}`));
|
||||
};
|
||||
|
||||
private assignKitsuId = async (kitsuQuery: IMetaDataQuery, torrent: IParsedTorrent): Promise<void> => {
|
||||
await this.metadataService.getKitsuId(kitsuQuery)
|
||||
.then((result: number | Error) => {
|
||||
if (typeof result === 'number') {
|
||||
torrent.kitsuId = result;
|
||||
} else {
|
||||
torrent.kitsuId = 0;
|
||||
}
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
this.logger.debug(`Failed getting kitsuId for ${torrent.title}`, error.message);
|
||||
torrent.kitsuId = 0;
|
||||
});
|
||||
};
|
||||
|
||||
public createSkipTorrentEntry: (torrent: Torrent) => Promise<[SkipTorrent, boolean | null]> = async (torrent: Torrent)=> this.repository.createSkipTorrent(torrent.dataValues);
|
||||
public createSkipTorrentEntry: (torrent: Torrent) => Promise<[SkipTorrent, boolean | null]> = async (torrent: Torrent) => this.repository.createSkipTorrent(torrent.dataValues);
|
||||
|
||||
public getStoredTorrentEntry = async (torrent: Torrent): Promise<Torrent | SkipTorrent | null | undefined> => this.repository.getSkipTorrent(torrent.infoHash)
|
||||
.catch(() => this.repository.getTorrent(torrent.dataValues))
|
||||
@@ -156,7 +141,7 @@ export class TorrentEntriesService implements ITorrentEntriesService {
|
||||
await existingTorrent.save();
|
||||
this.logger.debug(`Updated [${existingTorrent.infoHash}] ${existingTorrent.title} language to ${torrent.languages}`);
|
||||
}
|
||||
|
||||
|
||||
return this.createTorrentContents(existingTorrent)
|
||||
.then(() => this.updateTorrentSeeders(existingTorrent.dataValues))
|
||||
.then(() => Promise.resolve(true))
|
||||
@@ -177,7 +162,10 @@ export class TorrentEntriesService implements ITorrentEntriesService {
|
||||
const kitsuId: number = PromiseHelpers.mostCommonValue(storedVideos.map(stored => stored.kitsuId || 0));
|
||||
|
||||
const fileCollection: ITorrentFileCollection = await this.fileService.parseTorrentFiles(torrent)
|
||||
.then(torrentContents => notOpenedVideo ? torrentContents : {...torrentContents, videos: storedVideos.map(video => video.dataValues)})
|
||||
.then(torrentContents => notOpenedVideo ? torrentContents : {
|
||||
...torrentContents,
|
||||
videos: storedVideos.map(video => video.dataValues)
|
||||
})
|
||||
.then(torrentContents => this.subtitleService.assignSubtitles(torrentContents))
|
||||
.then(torrentContents => this.assignMetaIds(torrentContents, imdbId, kitsuId))
|
||||
.catch(error => {
|
||||
@@ -230,13 +218,13 @@ export class TorrentEntriesService implements ITorrentEntriesService {
|
||||
if (!(torrent.infoHash || (torrent.provider && torrent.torrentId)) || !Number.isInteger(torrent.seeders)) {
|
||||
return [0];
|
||||
}
|
||||
|
||||
|
||||
if (torrent.seeders === undefined) {
|
||||
this.logger.warn(`Seeders not found for ${torrent.provider} [${torrent.infoHash}] ${torrent.title}`);
|
||||
return [0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return this.repository.setTorrentSeeders(torrent, torrent.seeders)
|
||||
.catch(error => {
|
||||
this.logger.warn('Failed updating seeders:', error);
|
||||
@@ -244,6 +232,21 @@ export class TorrentEntriesService implements ITorrentEntriesService {
|
||||
});
|
||||
};
|
||||
|
||||
private assignKitsuId = async (kitsuQuery: IMetaDataQuery, torrent: IParsedTorrent): Promise<void> => {
|
||||
await this.metadataService.getKitsuId(kitsuQuery)
|
||||
.then((result: number | Error) => {
|
||||
if (typeof result === 'number') {
|
||||
torrent.kitsuId = result;
|
||||
} else {
|
||||
torrent.kitsuId = 0;
|
||||
}
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
this.logger.debug(`Failed getting kitsuId for ${torrent.title}`, error.message);
|
||||
torrent.kitsuId = 0;
|
||||
});
|
||||
};
|
||||
|
||||
private assignMetaIds = (fileCollection: ITorrentFileCollection, imdbId: string | undefined, kitsuId: number): ITorrentFileCollection => {
|
||||
if (fileCollection.videos && fileCollection.videos.length) {
|
||||
fileCollection.videos.forEach(video => {
|
||||
|
||||
@@ -45,7 +45,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
if (!torrent.title) {
|
||||
return Promise.reject(new Error('Torrent title is missing'));
|
||||
}
|
||||
|
||||
|
||||
const parsedTorrentName = parse(torrent.title);
|
||||
const query: IMetaDataQuery = {
|
||||
id: torrent.kitsuId || torrent.imdbId,
|
||||
@@ -54,7 +54,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
const metadata = await this.metadataService.getMetadata(query)
|
||||
.then(meta => Object.assign({}, meta))
|
||||
.catch(() => undefined);
|
||||
|
||||
|
||||
if (metadata === undefined || metadata instanceof Error) {
|
||||
return Promise.reject(new Error('Failed to retrieve metadata'));
|
||||
}
|
||||
@@ -83,14 +83,14 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
if (torrent.type === TorrentType.Movie) {
|
||||
return parsedInfo.complete || typeof parsedInfo.year === 'string' || /movies/i.test(torrent.title);
|
||||
}
|
||||
|
||||
|
||||
const hasMultipleEpisodes = Boolean(parsedInfo.complete || torrent.size || 0 > MULTIPLE_FILES_SIZE ||
|
||||
(parsedInfo.seasons && parsedInfo.seasons.length > 1) ||
|
||||
(parsedInfo.episodes && parsedInfo.episodes.length > 1) ||
|
||||
(parsedInfo.seasons && !parsedInfo.episodes));
|
||||
|
||||
const hasSingleEpisode: boolean = Boolean(Number.isInteger(parsedInfo.episode) || (!parsedInfo.episodes && parsedInfo.date));
|
||||
|
||||
|
||||
return hasMultipleEpisodes && !hasSingleEpisode;
|
||||
};
|
||||
|
||||
@@ -107,7 +107,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
if (fileCollection.videos === undefined || fileCollection.videos.length === 0) {
|
||||
return {...fileCollection, videos: this.getDefaultFileEntries(torrent)};
|
||||
}
|
||||
|
||||
|
||||
const filteredVideos = fileCollection.videos
|
||||
.filter(video => video.size! > MIN_SIZE)
|
||||
.filter(video => !this.isFeaturette(video));
|
||||
@@ -118,7 +118,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
title: video.path || video.title || video.fileName || '',
|
||||
size: video.size || torrent.size,
|
||||
imdbId: torrent.imdbId?.toString() || metadata && metadata.imdbId?.toString(),
|
||||
kitsuId: parseInt(torrent.kitsuId?.toString() || metadata && metadata.kitsuId?.toString() || '0')
|
||||
kitsuId: parseInt(torrent.kitsuId?.toString() || metadata && metadata.kitsuId?.toString() || '0')
|
||||
}));
|
||||
return {...fileCollection, videos: parsedVideos};
|
||||
}
|
||||
@@ -141,7 +141,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
if (fileCollection.videos === undefined || fileCollection.videos.length === 0) {
|
||||
return {...fileCollection, videos: this.getDefaultFileEntries(torrent)};
|
||||
}
|
||||
|
||||
|
||||
const parsedVideos: IFileAttributes[] = await Promise.resolve(fileCollection.videos)
|
||||
.then(videos => videos.filter(video => videos?.length === 1 || video.size! > MIN_SIZE))
|
||||
.then(videos => this.parseSeriesVideos(torrent, videos))
|
||||
@@ -169,7 +169,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
if (files.contents && files.contents.length && !files.videos?.length && this.isDiskTorrent(files.contents)) {
|
||||
files.videos = this.getDefaultFileEntries(torrent);
|
||||
}
|
||||
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
@@ -269,7 +269,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
}];
|
||||
};
|
||||
|
||||
private decomposeEpisodes = async (torrent: IParsedTorrent, files: IFileAttributes[], metadata: IMetadataResponse = {episodeCount: []}):Promise<IFileAttributes[]> => {
|
||||
private decomposeEpisodes = async (torrent: IParsedTorrent, files: IFileAttributes[], metadata: IMetadataResponse = {episodeCount: []}): Promise<IFileAttributes[]> => {
|
||||
if (files.every(file => !file.episodes && !file.date)) {
|
||||
return files;
|
||||
}
|
||||
@@ -370,7 +370,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
|
||||
private isNewEpisodeNotInMetadata = (torrent: IParsedTorrent, video: IFileAttributes, metadata: IMetadataResponse): boolean => {
|
||||
const isAnime = torrent.type === TorrentType.Anime && torrent.kitsuId;
|
||||
return !!( !isAnime && !video.isMovie && video.episodes && video.season !== 1
|
||||
return !!(!isAnime && !video.isMovie && video.episodes && video.season !== 1
|
||||
&& metadata.status && /continuing|current/i.test(metadata.status)
|
||||
&& metadata.episodeCount && video.season && video.season >= metadata.episodeCount.length
|
||||
&& video.episodes.every(ep => metadata.episodeCount && video.season && ep > (metadata.episodeCount[video.season - 1] || 0)));
|
||||
@@ -410,9 +410,9 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
return !file.season || (metadata.episodeCount[file.season - 1] || 0) < file.episodes[0];
|
||||
})
|
||||
.forEach(file => {
|
||||
if(!file.episodes || !metadata.episodeCount) return;
|
||||
if (!file.episodes || !metadata.episodeCount) return;
|
||||
|
||||
let seasonIdx = metadata.episodeCount
|
||||
let seasonIdx = metadata.episodeCount
|
||||
.map((_, i) => i)
|
||||
.find(i => metadata.episodeCount && file.episodes && metadata.episodeCount.slice(0, i + 1).reduce((a, b) => a + b) >= file.episodes[0]);
|
||||
|
||||
@@ -609,7 +609,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
})
|
||||
};
|
||||
|
||||
private findMovieImdbId = (title: IFileAttributes | string):Promise<string | undefined> => {
|
||||
private findMovieImdbId = (title: IFileAttributes | string): Promise<string | undefined> => {
|
||||
const parsedTitle = typeof title === 'string' ? parse(title) : title;
|
||||
this.logger.debug(`Finding movie imdbId for ${title}`);
|
||||
return this.imdb_limiter.schedule(async () => {
|
||||
@@ -626,7 +626,7 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
});
|
||||
};
|
||||
|
||||
private findMovieKitsuId = async (title: IFileAttributes | string):Promise<number | Error | undefined> => {
|
||||
private findMovieKitsuId = async (title: IFileAttributes | string): Promise<number | Error | undefined> => {
|
||||
const parsedTitle = typeof title === 'string' ? parse(title) : title;
|
||||
const kitsuQuery = {
|
||||
title: parsedTitle.title,
|
||||
@@ -645,10 +645,10 @@ export class TorrentFileService implements ITorrentFileService {
|
||||
|
||||
private isSingleMovie = (videos: IFileAttributes[]): boolean => videos.length === 1 ||
|
||||
(videos.length === 2 &&
|
||||
videos.find(v => /\b(?:part|disc|cd)[ ._-]?0?1\b|^0?1\.\w{2,4}$/i.test(v.path!)) &&
|
||||
videos.find(v => /\b(?:part|disc|cd)[ ._-]?0?1\b|^0?1\.\w{2,4}$/i.test(v.path!)) &&
|
||||
videos.find(v => /\b(?:part|disc|cd)[ ._-]?0?2\b|^0?2\.\w{2,4}$/i.test(v.path!))) !== undefined;
|
||||
|
||||
private isFeaturette = (video: IFileAttributes):boolean => /featurettes?\/|extras-grym/i.test(video.path!);
|
||||
private isFeaturette = (video: IFileAttributes): boolean => /featurettes?\/|extras-grym/i.test(video.path!);
|
||||
|
||||
private parseSeriesVideo = (video: IFileAttributes): IFileAttributes => {
|
||||
const videoInfo = parse(video.title);
|
||||
|
||||
@@ -40,7 +40,7 @@ export class TorrentSubtitleService implements ITorrentSubtitleService {
|
||||
};
|
||||
}
|
||||
|
||||
private mostProbableSubtitleVideos = (subtitle: ISubtitleAttributes, parsedVideos: IFileAttributes[]) : IFileAttributes[] => {
|
||||
private mostProbableSubtitleVideos = (subtitle: ISubtitleAttributes, parsedVideos: IFileAttributes[]): IFileAttributes[] => {
|
||||
const subTitle = (subtitle.title || subtitle.path)?.split('/')?.pop()?.replace(/\.(\w{2,4})$/, '') || '';
|
||||
const parsedSub = this.parsePath(subtitle.title || subtitle.path);
|
||||
const byFileName = parsedVideos.filter(video => subTitle.includes(video.title!));
|
||||
@@ -79,7 +79,7 @@ export class TorrentSubtitleService implements ITorrentSubtitleService {
|
||||
return parsedWithEpisode || pathParts[pathParts.length - 1];
|
||||
}
|
||||
|
||||
private parseFilename = (filename: string) : IFileAttributes => {
|
||||
private parseFilename = (filename: string): IFileAttributes => {
|
||||
const parsedInfo = parse(filename)
|
||||
const titleEpisode = parsedInfo.title.match(/(\d+)$/);
|
||||
if (!parsedInfo.episodes && titleEpisode) {
|
||||
|
||||
21006
src/node/consumer/test/assets/cinemeta-query-response.json
Normal file
21006
src/node/consumer/test/assets/cinemeta-query-response.json
Normal file
File diff suppressed because it is too large
Load Diff
232
src/node/consumer/test/assets/flash-episode-list.json
Normal file
232
src/node/consumer/test/assets/flash-episode-list.json
Normal file
@@ -0,0 +1,232 @@
|
||||
{
|
||||
"meta": {
|
||||
"status": "Ended",
|
||||
"videos": [
|
||||
{
|
||||
"name": "Pilot",
|
||||
"season": 1,
|
||||
"number": 0,
|
||||
"firstAired": "1990-09-20T00:00:00.000Z",
|
||||
"rating": "6.6",
|
||||
"id": "tt0098798:1:0",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0099580"
|
||||
},
|
||||
{
|
||||
"name": "Out of Control",
|
||||
"season": 1,
|
||||
"number": 1,
|
||||
"firstAired": "1990-09-26T00:00:00.000Z",
|
||||
"rating": "6.8",
|
||||
"id": "tt0098798:1:1",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579962"
|
||||
},
|
||||
{
|
||||
"name": "Watching the Detectives",
|
||||
"season": 1,
|
||||
"number": 2,
|
||||
"firstAired": "1990-10-17T00:00:00.000Z",
|
||||
"rating": "6.9",
|
||||
"id": "tt0098798:1:2",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579971"
|
||||
},
|
||||
{
|
||||
"name": "Honor Among Thieves",
|
||||
"season": 1,
|
||||
"number": 3,
|
||||
"firstAired": "1990-10-25T00:00:00.000Z",
|
||||
"rating": "6.8",
|
||||
"id": "tt0098798:1:3",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579961"
|
||||
},
|
||||
{
|
||||
"name": "Double Vision",
|
||||
"season": 1,
|
||||
"number": 4,
|
||||
"firstAired": "1990-11-01T00:00:00.000Z",
|
||||
"rating": "6.6",
|
||||
"id": "tt0098798:1:4",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579957"
|
||||
},
|
||||
{
|
||||
"name": "Sins of the Father",
|
||||
"season": 1,
|
||||
"number": 5,
|
||||
"firstAired": "1990-11-08T00:00:00.000Z",
|
||||
"rating": "6.9",
|
||||
"id": "tt0098798:1:5",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579965"
|
||||
},
|
||||
{
|
||||
"name": "Child's Play",
|
||||
"season": 1,
|
||||
"number": 6,
|
||||
"firstAired": "1990-11-15T00:00:00.000Z",
|
||||
"rating": "6.8",
|
||||
"id": "tt0098798:1:6",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579955"
|
||||
},
|
||||
{
|
||||
"name": "Shroud of Death",
|
||||
"season": 1,
|
||||
"number": 7,
|
||||
"firstAired": "1990-11-29T00:00:00.000Z",
|
||||
"rating": "7.1",
|
||||
"id": "tt0098798:1:7",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579963"
|
||||
},
|
||||
{
|
||||
"name": "Ghost in the Machine",
|
||||
"season": 1,
|
||||
"number": 8,
|
||||
"firstAired": "1990-12-13T00:00:00.000Z",
|
||||
"rating": "7.7",
|
||||
"id": "tt0098798:1:8",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579959"
|
||||
},
|
||||
{
|
||||
"name": "Sight Unseen",
|
||||
"season": 1,
|
||||
"number": 9,
|
||||
"firstAired": "1991-01-10T00:00:00.000Z",
|
||||
"rating": "7",
|
||||
"id": "tt0098798:1:9",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579964"
|
||||
},
|
||||
{
|
||||
"name": "Beat the Clock",
|
||||
"season": 1,
|
||||
"number": 10,
|
||||
"firstAired": "1991-01-31T00:00:00.000Z",
|
||||
"rating": "7",
|
||||
"id": "tt0098798:1:10",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579953"
|
||||
},
|
||||
{
|
||||
"name": "The Trickster",
|
||||
"season": 1,
|
||||
"number": 11,
|
||||
"firstAired": "1991-02-07T00:00:00.000Z",
|
||||
"rating": "7.7",
|
||||
"id": "tt0098798:1:11",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579969"
|
||||
},
|
||||
{
|
||||
"name": "Tina, Is That You?",
|
||||
"season": 1,
|
||||
"number": 12,
|
||||
"firstAired": "1991-02-14T00:00:00.000Z",
|
||||
"rating": "6.8",
|
||||
"id": "tt0098798:1:12",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579967"
|
||||
},
|
||||
{
|
||||
"name": "Be My Baby",
|
||||
"season": 1,
|
||||
"number": 13,
|
||||
"firstAired": "1991-02-20T00:00:00.000Z",
|
||||
"rating": "6.5",
|
||||
"id": "tt0098798:1:13",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579952"
|
||||
},
|
||||
{
|
||||
"name": "Fast Forward",
|
||||
"season": 1,
|
||||
"number": 14,
|
||||
"firstAired": "1991-02-27T00:00:00.000Z",
|
||||
"rating": "7.9",
|
||||
"id": "tt0098798:1:14",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579958"
|
||||
},
|
||||
{
|
||||
"name": "Deadly Nightshade",
|
||||
"season": 1,
|
||||
"number": 15,
|
||||
"firstAired": "1991-03-28T00:00:00.000Z",
|
||||
"rating": "7.7",
|
||||
"id": "tt0098798:1:15",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579966"
|
||||
},
|
||||
{
|
||||
"name": "Captain Cold",
|
||||
"season": 1,
|
||||
"number": 16,
|
||||
"firstAired": "1991-04-05T00:00:00.000Z",
|
||||
"rating": "7.7",
|
||||
"id": "tt0098798:1:16",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579954"
|
||||
},
|
||||
{
|
||||
"name": "Twin Streaks",
|
||||
"season": 1,
|
||||
"number": 17,
|
||||
"firstAired": "1991-04-12T00:00:00.000Z",
|
||||
"rating": "7.1",
|
||||
"id": "tt0098798:1:17",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579970"
|
||||
},
|
||||
{
|
||||
"name": "Done with Mirrors",
|
||||
"season": 1,
|
||||
"number": 18,
|
||||
"firstAired": "1991-04-27T00:00:00.000Z",
|
||||
"rating": "7.3",
|
||||
"id": "tt0098798:1:18",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579956"
|
||||
},
|
||||
{
|
||||
"name": "Good Night, Central City",
|
||||
"season": 1,
|
||||
"number": 19,
|
||||
"firstAired": "1991-05-04T00:00:00.000Z",
|
||||
"rating": "7.1",
|
||||
"id": "tt0098798:1:19",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579960"
|
||||
},
|
||||
{
|
||||
"name": "Alpha",
|
||||
"season": 1,
|
||||
"number": 20,
|
||||
"firstAired": "1991-05-11T00:00:00.000Z",
|
||||
"rating": "7.5",
|
||||
"id": "tt0098798:1:20",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579951"
|
||||
},
|
||||
{
|
||||
"name": "Trial of the Trickster",
|
||||
"season": 1,
|
||||
"number": 21,
|
||||
"firstAired": "1991-05-18T00:00:00.000Z",
|
||||
"rating": "7.9",
|
||||
"id": "tt0098798:1:21",
|
||||
"overview": "",
|
||||
"imdb_id": "tt0579968"
|
||||
}
|
||||
],
|
||||
"id": "tt0098798",
|
||||
"behaviorHints": {
|
||||
"defaultVideoId": null,
|
||||
"hasScheduledVideos": false
|
||||
}
|
||||
}
|
||||
}
|
||||
2491
src/node/consumer/test/assets/kitsu-naruto-full.json
Normal file
2491
src/node/consumer/test/assets/kitsu-naruto-full.json
Normal file
File diff suppressed because it is too large
Load Diff
111
src/node/consumer/test/assets/name-to-imdb-flash.json
Normal file
111
src/node/consumer/test/assets/name-to-imdb-flash.json
Normal file
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"d": [
|
||||
{
|
||||
"i": [
|
||||
"https://m.media-amazon.com/images/M/MV5BMTQ2MTc0MTAtN2VlYi00N2ZkLTlhNmUtMjcyZDg0YzNiYjEyXkEyXkFqcGdeQXVyMzU3MTc5OTE@._V1_.jpg",
|
||||
680,
|
||||
1000
|
||||
],
|
||||
"id": "tt0098798",
|
||||
"l": "The Flash",
|
||||
"q": "TV series",
|
||||
"qid": "tvSeries",
|
||||
"s": "John Wesley Shipp, Amanda Pays",
|
||||
"y": 1990,
|
||||
"yr": "1990-1991"
|
||||
},
|
||||
{
|
||||
"i": [
|
||||
"https://m.media-amazon.com/images/M/MV5BMGU2OGMyNzUtM2Q5NS00ZTdhLThmNjItODRiZDQ5MGMzZGNmXkEyXkFqcGdeQXVyMjQzMzQzODY@._V1_.jpg",
|
||||
766,
|
||||
1023
|
||||
],
|
||||
"id": "tt0100678",
|
||||
"l": "Stan the Flasher",
|
||||
"q": "feature",
|
||||
"qid": "movie",
|
||||
"s": "Claude Berri, Aurore Clément",
|
||||
"y": 1990
|
||||
},
|
||||
{
|
||||
"i": [
|
||||
"https://m.media-amazon.com/images/M/MV5BMDlmNTQ3NzctMGUzYi00MjEyLWIzNmUtNjhjYzZlMDQ5OTE3XkEyXkFqcGdeQXVyMzY4ODk0Nw@@._V1_.jpg",
|
||||
800,
|
||||
600
|
||||
],
|
||||
"id": "tt27750546",
|
||||
"l": "The Flash: Video News Release",
|
||||
"q": "TV special",
|
||||
"qid": "tvSpecial",
|
||||
"s": "John Wesley Shipp, Amanda Pays",
|
||||
"y": 1990
|
||||
},
|
||||
{
|
||||
"i": [
|
||||
"https://m.media-amazon.com/images/M/MV5BMjViZmVlMmUtNzljNS00NjhjLTk5ODMtNTZiZWRlOWRkMWU5XkEyXkFqcGdeQXVyMDA1NzM3OA@@._V1_.jpg",
|
||||
1500,
|
||||
2000
|
||||
],
|
||||
"id": "tt8408320",
|
||||
"l": "Shenzhen Flash",
|
||||
"q": "short",
|
||||
"qid": "short",
|
||||
"s": "",
|
||||
"y": 1990
|
||||
},
|
||||
{
|
||||
"i": [
|
||||
"https://m.media-amazon.com/images/M/MV5BODU4YmIxYzItYTdjYy00NmQ3LWJkYmQtM2UwZGZjNjZhMjYwXkEyXkFqcGdeQXVyMTQxNzMzNDI@._V1_.jpg",
|
||||
1991,
|
||||
2931
|
||||
],
|
||||
"id": "tt0097365",
|
||||
"l": "Flesh Gordon Meets the Cosmic Cheerleaders",
|
||||
"q": "feature",
|
||||
"qid": "movie",
|
||||
"s": "Vince Murdocco, Robyn Kelly",
|
||||
"y": 1990
|
||||
},
|
||||
{
|
||||
"i": [
|
||||
"https://m.media-amazon.com/images/M/MV5BYjMwMjgwNjAtZjllYi00Y2QyLWI2MzUtOGIwMDJjNWQ3YjQxL2ltYWdlL2ltYWdlXkEyXkFqcGdeQXVyNjg3MTAzODM@._V1_.jpg",
|
||||
3183,
|
||||
2015
|
||||
],
|
||||
"id": "tt2356508",
|
||||
"l": "Arrow Flash",
|
||||
"q": "video game",
|
||||
"qid": "videoGame",
|
||||
"s": "Action, Sci-Fi",
|
||||
"y": 1990
|
||||
},
|
||||
{
|
||||
"i": [
|
||||
"https://m.media-amazon.com/images/M/MV5BNTZkNzU2MzEtNzUwMS00NTFmLTgxY2EtZTk1ZTI3NDUzN2M1XkEyXkFqcGdeQXVyMTgwMDI4MTc@._V1_.jpg",
|
||||
299,
|
||||
230
|
||||
],
|
||||
"id": "tt0444569",
|
||||
"l": "Clash!",
|
||||
"q": "TV series",
|
||||
"qid": "tvSeries",
|
||||
"s": "Billy Kimball, Dave Levin",
|
||||
"y": 1990
|
||||
},
|
||||
{
|
||||
"i": [
|
||||
"https://m.media-amazon.com/images/M/MV5BNGNkZTYwNTYtNTc2Mi00ODFjLTg3MjctNTRiMGQwMDc4MjBkXkEyXkFqcGdeQXVyMzU0NzkwMDg@._V1_.jpg",
|
||||
1152,
|
||||
1548
|
||||
],
|
||||
"id": "tt0099584",
|
||||
"l": "Of Flesh and Blood",
|
||||
"q": "feature",
|
||||
"qid": "movie",
|
||||
"s": "Breon, Dick Bangham",
|
||||
"y": 1990
|
||||
}
|
||||
],
|
||||
"q": "the_flash%201990",
|
||||
"v": 1
|
||||
}
|
||||
373
src/node/consumer/test/assets/test-cinemata-theflash.json
Normal file
373
src/node/consumer/test/assets/test-cinemata-theflash.json
Normal file
@@ -0,0 +1,373 @@
|
||||
{
|
||||
"meta": {
|
||||
"awards": "Nominated for 2 Primetime Emmys. 4 nominations total",
|
||||
"cast": [
|
||||
"John Wesley Shipp",
|
||||
"Amanda Pays",
|
||||
"Alex Désert"
|
||||
],
|
||||
"country": "United States",
|
||||
"description": "A police forensic scientist, Barry Allen, battles crimes as the super-fast superhero \"The Flash.\"",
|
||||
"director": null,
|
||||
"dvdRelease": "2011-01-25T00:00:00.000Z",
|
||||
"genre": [
|
||||
"Action",
|
||||
"Crime",
|
||||
"Fantasy"
|
||||
],
|
||||
"imdbRating": "7.1",
|
||||
"imdb_id": "tt0098798",
|
||||
"name": "The Flash",
|
||||
"popularity": 0.987,
|
||||
"poster": "https://images.metahub.space/poster/small/tt0098798/img",
|
||||
"released": "1990-09-20T00:00:00.000Z",
|
||||
"runtime": "2 min",
|
||||
"status": "Ended",
|
||||
"tvdb_id": "78650",
|
||||
"type": "series",
|
||||
"writer": [
|
||||
"Danny Bilson",
|
||||
"Paul De Meo"
|
||||
],
|
||||
"year": "1990–1991",
|
||||
"popularities": {
|
||||
"moviedb": 57.842,
|
||||
"trakt": 2,
|
||||
"stremio": 0.987,
|
||||
"stremio_lib": 0
|
||||
},
|
||||
"background": "https://images.metahub.space/background/medium/tt0098798/img",
|
||||
"logo": "https://images.metahub.space/logo/medium/tt0098798/img",
|
||||
"moviedb_id": 236,
|
||||
"slug": "series/the-flash-0098798",
|
||||
"id": "tt0098798",
|
||||
"genres": [
|
||||
"Action",
|
||||
"Crime",
|
||||
"Fantasy"
|
||||
],
|
||||
"releaseInfo": "1990–1991",
|
||||
"videos": [
|
||||
{
|
||||
"name": "Pilot",
|
||||
"season": 1,
|
||||
"number": 1,
|
||||
"firstAired": "1990-09-20T00:00:00.000Z",
|
||||
"tvdb_id": 335168,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/1/w780.jpg",
|
||||
"id": "tt0098798:1:1",
|
||||
"released": "1990-09-20T00:00:00.000Z",
|
||||
"episode": 1
|
||||
},
|
||||
{
|
||||
"name": "Out of Control",
|
||||
"season": 1,
|
||||
"number": 2,
|
||||
"firstAired": "1990-09-27T00:00:00.000Z",
|
||||
"tvdb_id": 291041,
|
||||
"rating": "7.5",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/2/w780.jpg",
|
||||
"id": "tt0098798:1:2",
|
||||
"released": "1990-09-27T00:00:00.000Z",
|
||||
"episode": 2
|
||||
},
|
||||
{
|
||||
"name": "Watching the Detectives",
|
||||
"season": 1,
|
||||
"number": 3,
|
||||
"firstAired": "1990-10-18T00:00:00.000Z",
|
||||
"tvdb_id": 291042,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/3/w780.jpg",
|
||||
"id": "tt0098798:1:3",
|
||||
"released": "1990-10-18T00:00:00.000Z",
|
||||
"episode": 3
|
||||
},
|
||||
{
|
||||
"name": "Honor Among Thieves",
|
||||
"season": 1,
|
||||
"number": 4,
|
||||
"firstAired": "1990-10-25T00:00:00.000Z",
|
||||
"tvdb_id": 291043,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/4/w780.jpg",
|
||||
"id": "tt0098798:1:4",
|
||||
"released": "1990-10-25T00:00:00.000Z",
|
||||
"episode": 4
|
||||
},
|
||||
{
|
||||
"name": "Double Vision",
|
||||
"season": 1,
|
||||
"number": 5,
|
||||
"firstAired": "1990-11-01T00:00:00.000Z",
|
||||
"tvdb_id": 291044,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/5/w780.jpg",
|
||||
"id": "tt0098798:1:5",
|
||||
"released": "1990-11-01T00:00:00.000Z",
|
||||
"episode": 5
|
||||
},
|
||||
{
|
||||
"name": "Sins of the Father",
|
||||
"season": 1,
|
||||
"number": 6,
|
||||
"firstAired": "1990-11-08T00:00:00.000Z",
|
||||
"tvdb_id": 291045,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/6/w780.jpg",
|
||||
"id": "tt0098798:1:6",
|
||||
"released": "1990-11-08T00:00:00.000Z",
|
||||
"episode": 6
|
||||
},
|
||||
{
|
||||
"name": "Child's Play",
|
||||
"season": 1,
|
||||
"number": 7,
|
||||
"firstAired": "1990-11-15T00:00:00.000Z",
|
||||
"tvdb_id": 291046,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/7/w780.jpg",
|
||||
"id": "tt0098798:1:7",
|
||||
"released": "1990-11-15T00:00:00.000Z",
|
||||
"episode": 7
|
||||
},
|
||||
{
|
||||
"name": "Shroud of Death",
|
||||
"season": 1,
|
||||
"number": 8,
|
||||
"firstAired": "1990-11-29T00:00:00.000Z",
|
||||
"tvdb_id": 291047,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/8/w780.jpg",
|
||||
"id": "tt0098798:1:8",
|
||||
"released": "1990-11-29T00:00:00.000Z",
|
||||
"episode": 8
|
||||
},
|
||||
{
|
||||
"name": "Ghost in the Machine",
|
||||
"season": 1,
|
||||
"number": 9,
|
||||
"firstAired": "1990-12-13T00:00:00.000Z",
|
||||
"tvdb_id": 291048,
|
||||
"rating": "7.5",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/9/w780.jpg",
|
||||
"id": "tt0098798:1:9",
|
||||
"released": "1990-12-13T00:00:00.000Z",
|
||||
"episode": 9
|
||||
},
|
||||
{
|
||||
"name": "Sight Unseen",
|
||||
"season": 1,
|
||||
"number": 10,
|
||||
"firstAired": "1991-01-10T00:00:00.000Z",
|
||||
"tvdb_id": 291049,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/10/w780.jpg",
|
||||
"id": "tt0098798:1:10",
|
||||
"released": "1991-01-10T00:00:00.000Z",
|
||||
"episode": 10
|
||||
},
|
||||
{
|
||||
"name": "Beat the Clock",
|
||||
"season": 1,
|
||||
"number": 11,
|
||||
"firstAired": "1991-01-31T00:00:00.000Z",
|
||||
"tvdb_id": 291050,
|
||||
"rating": "6.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/11/w780.jpg",
|
||||
"id": "tt0098798:1:11",
|
||||
"released": "1991-01-31T00:00:00.000Z",
|
||||
"episode": 11
|
||||
},
|
||||
{
|
||||
"name": "The Trickster",
|
||||
"season": 1,
|
||||
"number": 12,
|
||||
"firstAired": "1991-02-07T00:00:00.000Z",
|
||||
"tvdb_id": 291051,
|
||||
"rating": "7.5",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/12/w780.jpg",
|
||||
"id": "tt0098798:1:12",
|
||||
"released": "1991-02-07T00:00:00.000Z",
|
||||
"episode": 12
|
||||
},
|
||||
{
|
||||
"name": "Tina, Is That You?",
|
||||
"season": 1,
|
||||
"number": 13,
|
||||
"firstAired": "1991-02-14T00:00:00.000Z",
|
||||
"tvdb_id": 291052,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/13/w780.jpg",
|
||||
"id": "tt0098798:1:13",
|
||||
"released": "1991-02-14T00:00:00.000Z",
|
||||
"episode": 13
|
||||
},
|
||||
{
|
||||
"name": "Be My Baby",
|
||||
"season": 1,
|
||||
"number": 14,
|
||||
"firstAired": "1991-02-21T00:00:00.000Z",
|
||||
"tvdb_id": 291053,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/14/w780.jpg",
|
||||
"id": "tt0098798:1:14",
|
||||
"released": "1991-02-21T00:00:00.000Z",
|
||||
"episode": 14
|
||||
},
|
||||
{
|
||||
"name": "Fast Forward",
|
||||
"season": 1,
|
||||
"number": 15,
|
||||
"firstAired": "1991-02-27T00:00:00.000Z",
|
||||
"tvdb_id": 291054,
|
||||
"rating": "6.5",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/15/w780.jpg",
|
||||
"id": "tt0098798:1:15",
|
||||
"released": "1991-02-27T00:00:00.000Z",
|
||||
"episode": 15
|
||||
},
|
||||
{
|
||||
"name": "Deadly Nightshade",
|
||||
"season": 1,
|
||||
"number": 16,
|
||||
"firstAired": "1991-03-30T00:00:00.000Z",
|
||||
"tvdb_id": 291055,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/16/w780.jpg",
|
||||
"id": "tt0098798:1:16",
|
||||
"released": "1991-03-30T00:00:00.000Z",
|
||||
"episode": 16
|
||||
},
|
||||
{
|
||||
"name": "Captain Cold",
|
||||
"season": 1,
|
||||
"number": 17,
|
||||
"firstAired": "1991-04-06T00:00:00.000Z",
|
||||
"tvdb_id": 291056,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/17/w780.jpg",
|
||||
"id": "tt0098798:1:17",
|
||||
"released": "1991-04-06T00:00:00.000Z",
|
||||
"episode": 17
|
||||
},
|
||||
{
|
||||
"name": "Twin Streaks",
|
||||
"season": 1,
|
||||
"number": 18,
|
||||
"firstAired": "1991-04-13T00:00:00.000Z",
|
||||
"tvdb_id": 291057,
|
||||
"rating": "6.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/18/w780.jpg",
|
||||
"id": "tt0098798:1:18",
|
||||
"released": "1991-04-13T00:00:00.000Z",
|
||||
"episode": 18
|
||||
},
|
||||
{
|
||||
"name": "Done with Mirrors",
|
||||
"season": 1,
|
||||
"number": 19,
|
||||
"firstAired": "1991-04-27T00:00:00.000Z",
|
||||
"tvdb_id": 291058,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/19/w780.jpg",
|
||||
"id": "tt0098798:1:19",
|
||||
"released": "1991-04-27T00:00:00.000Z",
|
||||
"episode": 19
|
||||
},
|
||||
{
|
||||
"name": "Good Night, Central City",
|
||||
"season": 1,
|
||||
"number": 20,
|
||||
"firstAired": "1991-05-04T00:00:00.000Z",
|
||||
"tvdb_id": 291059,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/20/w780.jpg",
|
||||
"id": "tt0098798:1:20",
|
||||
"released": "1991-05-04T00:00:00.000Z",
|
||||
"episode": 20
|
||||
},
|
||||
{
|
||||
"name": "Alpha",
|
||||
"season": 1,
|
||||
"number": 21,
|
||||
"firstAired": "1991-05-11T00:00:00.000Z",
|
||||
"tvdb_id": 291060,
|
||||
"rating": "7.0",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/21/w780.jpg",
|
||||
"id": "tt0098798:1:21",
|
||||
"released": "1991-05-11T00:00:00.000Z",
|
||||
"episode": 21
|
||||
},
|
||||
{
|
||||
"name": "The Trial of the Trickster",
|
||||
"season": 1,
|
||||
"number": 22,
|
||||
"firstAired": "1991-05-18T00:00:00.000Z",
|
||||
"tvdb_id": 291061,
|
||||
"rating": "7.5",
|
||||
"thumbnail": "https://episodes.metahub.space/tt0098798/1/22/w780.jpg",
|
||||
"id": "tt0098798:1:22",
|
||||
"released": "1991-05-18T00:00:00.000Z",
|
||||
"episode": 22
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"name": "7.1",
|
||||
"category": "imdb",
|
||||
"url": "https://imdb.com/title/tt0098798"
|
||||
},
|
||||
{
|
||||
"name": "The Flash",
|
||||
"category": "share",
|
||||
"url": "https://www.strem.io/s/series/the-flash-0098798"
|
||||
},
|
||||
{
|
||||
"name": "Action",
|
||||
"category": "Genres",
|
||||
"url": "stremio:///discover/https%3A%2F%2Fv3-cinemeta.strem.io%2Fmanifest.json/series/top?genre=Action"
|
||||
},
|
||||
{
|
||||
"name": "Crime",
|
||||
"category": "Genres",
|
||||
"url": "stremio:///discover/https%3A%2F%2Fv3-cinemeta.strem.io%2Fmanifest.json/series/top?genre=Crime"
|
||||
},
|
||||
{
|
||||
"name": "Fantasy",
|
||||
"category": "Genres",
|
||||
"url": "stremio:///discover/https%3A%2F%2Fv3-cinemeta.strem.io%2Fmanifest.json/series/top?genre=Fantasy"
|
||||
},
|
||||
{
|
||||
"name": "John Wesley Shipp",
|
||||
"category": "Cast",
|
||||
"url": "stremio:///search?search=John%20Wesley%20Shipp"
|
||||
},
|
||||
{
|
||||
"name": "Amanda Pays",
|
||||
"category": "Cast",
|
||||
"url": "stremio:///search?search=Amanda%20Pays"
|
||||
},
|
||||
{
|
||||
"name": "Alex Désert",
|
||||
"category": "Cast",
|
||||
"url": "stremio:///search?search=Alex%20D%C3%A9sert"
|
||||
},
|
||||
{
|
||||
"name": "Danny Bilson",
|
||||
"category": "Writers",
|
||||
"url": "stremio:///search?search=Danny%20Bilson"
|
||||
},
|
||||
{
|
||||
"name": "Paul De Meo",
|
||||
"category": "Writers",
|
||||
"url": "stremio:///search?search=Paul%20De%20Meo"
|
||||
}
|
||||
],
|
||||
"behaviorHints": {
|
||||
"defaultVideoId": null,
|
||||
"hasScheduledVideos": true
|
||||
}
|
||||
}
|
||||
}
|
||||
1060
src/node/consumer/test/assets/test-kitsu-search-id-naruto.json
Normal file
1060
src/node/consumer/test/assets/test-kitsu-search-id-naruto.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
import "reflect-metadata"; // required
|
||||
import { ILoggingService } from '@interfaces/logging_service';
|
||||
import { CacheService, CacheMethod } from '@services/cache_service';
|
||||
import {ILoggingService} from '@interfaces/logging_service';
|
||||
import {CacheMethod, CacheService} from '@services/cache_service';
|
||||
|
||||
jest.mock('@services/configuration_service', () => {
|
||||
return {
|
||||
@@ -12,7 +12,7 @@ jest.mock('@services/configuration_service', () => {
|
||||
MONGO_INITDB_ROOT_USERNAME: 'mongo',
|
||||
MONGO_INITDB_ROOT_PASSWORD: 'mongo',
|
||||
NO_CACHE: false,
|
||||
COLLECTION_NAME: 'knightcrawler_consumer_collection',
|
||||
COLLECTION_NAME: 'knightcrawler_consumer_collection',
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,8 @@ jest.mock('@tirke/node-cache-manager-mongodb', () => {
|
||||
|
||||
describe('CacheService Tests', () => {
|
||||
let cacheService: CacheService,
|
||||
loggingService: ILoggingService,
|
||||
cacheMethod: CacheMethod;
|
||||
loggingService: ILoggingService,
|
||||
cacheMethod: CacheMethod;
|
||||
|
||||
beforeEach(() => {
|
||||
process.env.LOG_LEVEL = 'debug';
|
||||
@@ -100,7 +100,7 @@ describe('CacheService Tests', () => {
|
||||
cacheMethod = jest.fn().mockRejectedValue(new Error('Test error'));
|
||||
await expect(cacheService.cacheTrackers(cacheMethod)).rejects.toThrow('Test error');
|
||||
});
|
||||
|
||||
|
||||
it('should handle when cache is disabled', async () => {
|
||||
jest.mock('@services/configuration_service', () => {
|
||||
return {
|
||||
|
||||
@@ -26,13 +26,13 @@ describe('Configuration Tests', () => {
|
||||
process.env.IMDB_INTERVAL_MS = '1000';
|
||||
process.env.JOB_CONCURRENCY = '1';
|
||||
process.env.JOBS_ENABLED = 'true';
|
||||
|
||||
|
||||
// shitty hack cause jest caches modules and resetModules isnt working
|
||||
({ configurationService } = await import("@services/configuration_service"));
|
||||
({configurationService} = await import("@services/configuration_service"));
|
||||
});
|
||||
|
||||
|
||||
it('should populate cacheConfig correctly', () => {
|
||||
const { cacheConfig } = configurationService;
|
||||
const {cacheConfig} = configurationService;
|
||||
expect(cacheConfig.MONGODB_HOST).toBe('test_mongodb');
|
||||
expect(cacheConfig.MONGODB_PORT).toBe('27017');
|
||||
expect(cacheConfig.MONGODB_DB).toBe('knightcrawler');
|
||||
@@ -44,7 +44,7 @@ describe('Configuration Tests', () => {
|
||||
});
|
||||
|
||||
it('should populate databaseConfig correctly', () => {
|
||||
const { databaseConfig } = configurationService;
|
||||
const {databaseConfig} = configurationService;
|
||||
expect(databaseConfig.POSTGRES_HOST).toBe('postgres');
|
||||
expect(databaseConfig.POSTGRES_PORT).toBe(5432);
|
||||
expect(databaseConfig.POSTGRES_DB).toBe('knightcrawler');
|
||||
@@ -55,33 +55,33 @@ describe('Configuration Tests', () => {
|
||||
});
|
||||
|
||||
it('should populate jobConfig correctly', () => {
|
||||
const { jobConfig } = configurationService;
|
||||
const {jobConfig} = configurationService;
|
||||
expect(jobConfig.JOB_CONCURRENCY).toBe(1);
|
||||
expect(jobConfig.JOBS_ENABLED).toBe(true);
|
||||
});
|
||||
|
||||
it('should populate metadataConfig correctly', () => {
|
||||
const { metadataConfig } = configurationService;
|
||||
const {metadataConfig} = configurationService;
|
||||
expect(metadataConfig.IMDB_CONCURRENT).toBe(1);
|
||||
expect(metadataConfig.IMDB_INTERVAL_MS).toBe(1000);
|
||||
});
|
||||
|
||||
it('should populate rabbitConfig correctly', () => {
|
||||
const { rabbitConfig } = configurationService;
|
||||
const {rabbitConfig} = configurationService;
|
||||
expect(rabbitConfig.RABBIT_URI).toBe('amqp://localhost');
|
||||
expect(rabbitConfig.QUEUE_NAME).toBe('test-queue');
|
||||
});
|
||||
|
||||
it('should populate torrentConfig correctly', () => {
|
||||
const { torrentConfig } = configurationService;
|
||||
const {torrentConfig} = configurationService;
|
||||
expect(torrentConfig.MAX_CONNECTIONS_PER_TORRENT).toBe(20);
|
||||
expect(torrentConfig.TIMEOUT).toBe(30000);
|
||||
});
|
||||
|
||||
it('should populate trackerConfig correctly', () => {
|
||||
const { trackerConfig } = configurationService;
|
||||
const {trackerConfig} = configurationService;
|
||||
expect(trackerConfig.TRACKERS_URL).toBe('https://ngosang.github.io/trackerslist/trackers_all.txt');
|
||||
expect(trackerConfig.UDP_ENABLED).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import "reflect-metadata"; // required
|
||||
import { LoggingService } from '@services/logging_service';
|
||||
import {LoggingService} from '@services/logging_service';
|
||||
|
||||
jest.mock('pino', () => {
|
||||
const actualPino = jest.requireActual('pino');
|
||||
@@ -14,7 +14,7 @@ jest.mock('pino', () => {
|
||||
};
|
||||
});
|
||||
|
||||
describe('LoggingService', () => {
|
||||
describe('LoggingService Tests', () => {
|
||||
let service: LoggingService,
|
||||
mockLogger: any;
|
||||
|
||||
@@ -24,22 +24,22 @@ describe('LoggingService', () => {
|
||||
});
|
||||
|
||||
it('should log info', () => {
|
||||
service.info('test message', { key: 'value' });
|
||||
expect(mockLogger.info).toHaveBeenCalledWith('test message', [{ key: 'value' }]);
|
||||
service.info('test message', {key: 'value'});
|
||||
expect(mockLogger.info).toHaveBeenCalledWith('test message', [{key: 'value'}]);
|
||||
});
|
||||
|
||||
it('should log error', () => {
|
||||
service.error('test message', { key: 'value' });
|
||||
expect(mockLogger.error).toHaveBeenCalledWith('test message', [{ key: 'value' }]);
|
||||
service.error('test message', {key: 'value'});
|
||||
expect(mockLogger.error).toHaveBeenCalledWith('test message', [{key: 'value'}]);
|
||||
});
|
||||
|
||||
it('should log debug', () => {
|
||||
service.debug('test message', { key: 'value' });
|
||||
expect(mockLogger.debug).toHaveBeenCalledWith('test message', [{ key: 'value' }]);
|
||||
service.debug('test message', {key: 'value'});
|
||||
expect(mockLogger.debug).toHaveBeenCalledWith('test message', [{key: 'value'}]);
|
||||
});
|
||||
|
||||
it('should log warn', () => {
|
||||
service.warn('test message', { key: 'value' });
|
||||
expect(mockLogger.warn).toHaveBeenCalledWith('test message', [{ key: 'value' }]);
|
||||
service.warn('test message', {key: 'value'});
|
||||
expect(mockLogger.warn).toHaveBeenCalledWith('test message', [{key: 'value'}]);
|
||||
})
|
||||
});
|
||||
91
src/node/consumer/test/metadata_service.test.ts
Normal file
91
src/node/consumer/test/metadata_service.test.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import "reflect-metadata"; // required
|
||||
import {ICacheService} from "@interfaces/cache_service";
|
||||
import {IMetadataResponse} from "@interfaces/metadata_response";
|
||||
import {MetadataService} from "@services/metadata_service";
|
||||
import {setupServer} from "msw/node";
|
||||
import * as responses from "./mock-responses/metadata_mock_responses";
|
||||
|
||||
jest.mock('@services/cache_service', () => {
|
||||
return {
|
||||
cacheWrapImdbId: jest.fn().mockImplementation(async (key, fn) => await fn()),
|
||||
cacheWrapKitsuId: jest.fn().mockImplementation(async (key, fn) => await fn()),
|
||||
cacheWrapMetadata: jest.fn().mockImplementation(async (key, fn) => await fn()),
|
||||
}
|
||||
})
|
||||
|
||||
const server = setupServer(
|
||||
responses.cinemetaQueryResponse,
|
||||
responses.cinemetaFlashMetadataSearchTestResponse,
|
||||
responses.kitsuNarutoIdSearchTestResponse,
|
||||
responses.kitsuNarutoMetaDataSearchTestResponse,
|
||||
responses.nameToImdbTheFlash,
|
||||
responses.checkIfImdbEpisode);
|
||||
|
||||
beforeAll(() => server.listen())
|
||||
beforeEach(() => {
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => 1234567890);
|
||||
})
|
||||
afterEach(() => () => {
|
||||
server.resetHandlers()
|
||||
jest.spyOn(Date, 'now').mockRestore();
|
||||
})
|
||||
afterAll(() => server.close())
|
||||
|
||||
describe('MetadataService Tests', () => {
|
||||
let metadataService: MetadataService,
|
||||
mockCacheService: ICacheService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockCacheService = jest.requireMock<ICacheService>('@services/cache_service');
|
||||
metadataService = new MetadataService(mockCacheService);
|
||||
});
|
||||
|
||||
it("should get kitsu id", async () => {
|
||||
const result = await metadataService.getKitsuId({
|
||||
title: 'Naruto',
|
||||
year: 2002,
|
||||
season: 1
|
||||
});
|
||||
expect(mockCacheService.cacheWrapKitsuId).toHaveBeenCalledWith('naruto 2002 S1', expect.any(Function));
|
||||
expect(result).not.toBeNull();
|
||||
expect(result).toEqual('11');
|
||||
});
|
||||
|
||||
it("should get kitsu metadata", async () => {
|
||||
const result = await metadataService.getMetadata({
|
||||
id: 'kitsu:11',
|
||||
type: 'series'
|
||||
});
|
||||
|
||||
expect(mockCacheService.cacheWrapMetadata).toHaveBeenCalledWith('kitsu:11', expect.any(Function));
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
const body = result as IMetadataResponse;
|
||||
expect(body.videos).not.toBeNull();
|
||||
expect(body.videos.length).toBe(220);
|
||||
});
|
||||
|
||||
it("should get imdb metadata", async () => {
|
||||
const result = await metadataService.getMetadata({
|
||||
id: 'tt0098798',
|
||||
type: 'series'
|
||||
});
|
||||
|
||||
expect(mockCacheService.cacheWrapMetadata).toHaveBeenCalledWith('tt0098798', expect.any(Function));
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
const body = result as IMetadataResponse;
|
||||
expect(body.videos).not.toBeNull();
|
||||
expect(body.videos.length).toBe(22);
|
||||
});
|
||||
|
||||
it("should check if imdb id is an episode", async () => {
|
||||
const result = await metadataService.isEpisodeImdbId('tt0579968');
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it("should escape title", () => {
|
||||
const result = metadataService.escapeTitle('Naruto: Shippuden');
|
||||
expect(result).toEqual('naruto shippuden');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import {http, HttpResponse} from "msw";
|
||||
import cinemetaQuery from "../assets/cinemeta-query-response.json";
|
||||
import cinemetaFlashFull from "../assets/flash-episode-list.json";
|
||||
import kitsuNarutoFull from "../assets/kitsu-naruto-full.json";
|
||||
import imdbTheFlash from "../assets/name-to-imdb-flash.json";
|
||||
import kitsuNarutoSearchId from "../assets/test-kitsu-search-id-naruto.json";
|
||||
|
||||
const kitsuNarutoIdSearchTestResponse = http.get('https://anime-kitsu.strem.fun/catalog/series/kitsu-anime-list/search=naruto%202002%20S1.json', () => {
|
||||
return HttpResponse.json(kitsuNarutoSearchId);
|
||||
});
|
||||
|
||||
const kitsuNarutoMetaDataSearchTestResponse = http.get('https://anime-kitsu.strem.fun/meta/Series/kitsu:11.json', () => {
|
||||
return HttpResponse.json(kitsuNarutoFull);
|
||||
});
|
||||
|
||||
const nameToImdbTheFlash = http.get('https://sg.media-imdb.com/suggests/t/the%20flash%201990.json', () => {
|
||||
const jsonpResponse = `/**/imdb$the_flash%201990(${JSON.stringify(imdbTheFlash)});`;
|
||||
return HttpResponse.json(jsonpResponse);
|
||||
});
|
||||
|
||||
const cinemetaQueryResponse = http.get('https://cinemeta.strem.io/stremioget/stremio/v1/q.json', () => {
|
||||
return HttpResponse.json(cinemetaQuery);
|
||||
});
|
||||
|
||||
const cinemetaFlashMetadataSearchTestResponse = http.get('https://v3-cinemeta.strem.io/meta/imdb/tt0098798.json', () => {
|
||||
return HttpResponse.json(cinemetaFlashFull);
|
||||
});
|
||||
|
||||
const checkIfImdbEpisode = http.get('https://www.imdb.com/title/tt0579968/', () => {
|
||||
return HttpResponse.text('<meta property="og:type" content="video.episode">');
|
||||
});
|
||||
|
||||
export {
|
||||
kitsuNarutoIdSearchTestResponse,
|
||||
kitsuNarutoMetaDataSearchTestResponse,
|
||||
nameToImdbTheFlash,
|
||||
cinemetaQueryResponse,
|
||||
cinemetaFlashMetadataSearchTestResponse,
|
||||
checkIfImdbEpisode
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import {http, HttpResponse} from "msw";
|
||||
|
||||
export const trackerTestResponse = http.get('https://ngosang.github.io/trackerslist/trackers_all.txt', () => {
|
||||
return HttpResponse.text('http://tracker1.com\nhttp://tracker2.com')
|
||||
});
|
||||
@@ -1,8 +1,8 @@
|
||||
import "reflect-metadata"; // required
|
||||
import { ILoggingService } from '@interfaces/logging_service';
|
||||
import { ITorrentProcessingService } from '@interfaces/torrent_processing_service';
|
||||
import { ProcessTorrentsJob } from '@jobs/process_torrents_job';
|
||||
import { configurationService } from '@services/configuration_service';
|
||||
import {ILoggingService} from '@interfaces/logging_service';
|
||||
import {ITorrentProcessingService} from '@interfaces/torrent_processing_service';
|
||||
import {ProcessTorrentsJob} from '@jobs/process_torrents_job';
|
||||
import {configurationService} from '@services/configuration_service';
|
||||
import client, {ConsumeMessage} from 'amqplib';
|
||||
|
||||
jest.mock('@services/configuration_service', () => {
|
||||
@@ -48,7 +48,7 @@ jest.mock('@services/torrent_processing_service', () => {
|
||||
})
|
||||
|
||||
describe('ProcessTorrentsJob Tests', () => {
|
||||
let processTorrentsJob: ProcessTorrentsJob,
|
||||
let processTorrentsJob: ProcessTorrentsJob,
|
||||
loggingService: ILoggingService,
|
||||
torrentProcessingService: ITorrentProcessingService;
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('ProcessTorrentsJob Tests', () => {
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
})
|
||||
|
||||
describe('listenToQueue', () => {
|
||||
test('should connect to the rabbitmq server and create a channel', async () => {
|
||||
@@ -79,7 +79,7 @@ describe('ProcessTorrentsJob Tests', () => {
|
||||
|
||||
test('should process messages from the queue', async () => {
|
||||
const mockMessage = {
|
||||
content: Buffer.from(JSON.stringify({
|
||||
content: Buffer.from(JSON.stringify({
|
||||
message: {
|
||||
name: 'test_name',
|
||||
source: 'test_source',
|
||||
@@ -90,7 +90,7 @@ describe('ProcessTorrentsJob Tests', () => {
|
||||
leechers: 0,
|
||||
imdb: 'test_imdb',
|
||||
processed: false,
|
||||
}
|
||||
}
|
||||
})),
|
||||
} as ConsumeMessage;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "reflect-metadata"; // required
|
||||
import { ILoggingService } from '@interfaces/logging_service';
|
||||
import {ILoggingService} from '@interfaces/logging_service';
|
||||
import {IParsedTorrent} from "@interfaces/parsed_torrent";
|
||||
import { TorrentDownloadService } from '@services/torrent_download_service';
|
||||
import {TorrentDownloadService} from '@services/torrent_download_service';
|
||||
import torrentStream from 'torrent-stream';
|
||||
|
||||
jest.mock('@services/logging_service', () => {
|
||||
@@ -22,7 +22,7 @@ jest.mock('torrent-stream', () => {
|
||||
|
||||
describe('TorrentDownloadService', () => {
|
||||
let torrentDownloadService: TorrentDownloadService,
|
||||
mockLoggingService: ILoggingService;
|
||||
mockLoggingService: ILoggingService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockLoggingService = jest.requireMock<ILoggingService>('@services/logging_service');
|
||||
@@ -42,7 +42,7 @@ describe('TorrentDownloadService', () => {
|
||||
uploadDate: new Date(),
|
||||
seeders: 100,
|
||||
torrentId: 'torrent1',
|
||||
fileCollection: { },
|
||||
fileCollection: {},
|
||||
title: 'Test Movie',
|
||||
year: 2020,
|
||||
season: 1,
|
||||
@@ -58,7 +58,7 @@ describe('TorrentDownloadService', () => {
|
||||
container: 'mp4',
|
||||
unrated: false,
|
||||
};
|
||||
|
||||
|
||||
const mockFiles = [
|
||||
{
|
||||
name: 'file1.mp4',
|
||||
@@ -134,7 +134,7 @@ describe('TorrentDownloadService', () => {
|
||||
fileId: file.fileIndex,
|
||||
})),
|
||||
});
|
||||
|
||||
|
||||
expect(torrentStream).toHaveBeenCalledWith(expect.any(String), expect.any(Object));
|
||||
expect(mockLoggingService.debug).toHaveBeenCalledWith(`Adding torrent with infoHash ${mockTorrent.infoHash} to torrent engine...`);
|
||||
});
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import "reflect-metadata"; // required
|
||||
import { ICacheService } from '@interfaces/cache_service';
|
||||
import { ILoggingService } from '@interfaces/logging_service';
|
||||
import { TrackerService } from '@services/tracker_service';
|
||||
import { http, HttpResponse } from 'msw'
|
||||
import { setupServer } from 'msw/node';
|
||||
import {ICacheService} from '@interfaces/cache_service';
|
||||
import {ILoggingService} from '@interfaces/logging_service';
|
||||
import {TrackerService} from '@services/tracker_service';
|
||||
import {setupServer} from 'msw/node';
|
||||
import * as responses from "./mock-responses/trackers_mock_responses";
|
||||
|
||||
const server = setupServer(
|
||||
http.get('https://ngosang.github.io/trackerslist/trackers_all.txt', ({ request, params, cookies }) => {
|
||||
return HttpResponse.text('http://tracker1.com\nhttp://tracker2.com')
|
||||
}),
|
||||
)
|
||||
const server = setupServer(responses.trackerTestResponse);
|
||||
|
||||
jest.mock('@services/logging_service', () => {
|
||||
return {
|
||||
@@ -38,8 +34,8 @@ afterAll(() => server.close())
|
||||
|
||||
describe('TrackerService', () => {
|
||||
let trackerService: TrackerService,
|
||||
mockCacheService: ICacheService,
|
||||
mockLoggingService: ILoggingService;
|
||||
mockCacheService: ICacheService,
|
||||
mockLoggingService: ILoggingService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockCacheService = jest.requireMock<ICacheService>('@services/cache_service');
|
||||
@@ -54,6 +50,6 @@ describe('TrackerService', () => {
|
||||
|
||||
expect(result).toEqual(mockTrackers);
|
||||
expect(mockLoggingService.info).toHaveBeenCalledWith(`Trackers updated at 1234567890: ${mockTrackers.length} trackers`);
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
@@ -2,8 +2,12 @@
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["ESNext"],
|
||||
"typeRoots": ["node_modules/@types"],
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
@@ -21,16 +25,37 @@
|
||||
"importHelpers": true,
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@/*": ["*"],
|
||||
"@enums/*": ["lib/enums/*"],
|
||||
"@repository/*": ["lib/repository/*"],
|
||||
"@interfaces/*": ["lib/interfaces/*"],
|
||||
"@models/*": ["lib/models/*"],
|
||||
"@services/*": ["lib/services/*"],
|
||||
"@helpers/*": ["lib/helpers/*"],
|
||||
"@jobs/*": ["lib/jobs/*"]
|
||||
"@/*": [
|
||||
"*"
|
||||
],
|
||||
"@enums/*": [
|
||||
"lib/enums/*"
|
||||
],
|
||||
"@repository/*": [
|
||||
"lib/repository/*"
|
||||
],
|
||||
"@interfaces/*": [
|
||||
"lib/interfaces/*"
|
||||
],
|
||||
"@models/*": [
|
||||
"lib/models/*"
|
||||
],
|
||||
"@services/*": [
|
||||
"lib/services/*"
|
||||
],
|
||||
"@helpers/*": [
|
||||
"lib/helpers/*"
|
||||
],
|
||||
"@jobs/*": [
|
||||
"lib/jobs/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": ["src", "test"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": [
|
||||
"src",
|
||||
"test"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user