mirror of
https://github.com/immich-app/immich.git
synced 2024-11-24 08:52:28 +02:00
chore(server): eslint await-thenable (#7545)
* await-thenable * fix library watchers * moar eslint * fix test * fix typo * try to remove check void return * fix checksVoidReturn * move to domain utils * remove eslint ignores * chore: cleanup types * chore: use logger * fix: e2e --------- Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
parent
972d5a3411
commit
5d377e5b0f
@ -25,6 +25,12 @@ module.exports = {
|
||||
'unicorn/prefer-top-level-await': 'off',
|
||||
'unicorn/prefer-event-target': 'off',
|
||||
'unicorn/no-thenable': 'off',
|
||||
'@typescript-eslint/await-thenable': 'error',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-misused-promises': 'error',
|
||||
// Note: you must disable the base rule as it can report incorrect errors
|
||||
'require-await': 'off',
|
||||
'@typescript-eslint/require-await': 'error',
|
||||
curly: 2,
|
||||
'prettier/prettier': 0,
|
||||
},
|
||||
|
@ -208,7 +208,7 @@ describe(`Library watcher (e2e)`, () => {
|
||||
await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir3`, { recursive: true });
|
||||
});
|
||||
|
||||
it('should use an updated import paths', async () => {
|
||||
it('should use an updated import path', async () => {
|
||||
await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir4`, { recursive: true });
|
||||
|
||||
await api.libraryApi.setImportPaths(server, admin.accessToken, library.id, [
|
||||
|
@ -11,7 +11,7 @@ describe(ActivityService.name, () => {
|
||||
let accessMock: IAccessRepositoryMock;
|
||||
let activityMock: jest.Mocked<IActivityRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
activityMock = newActivityRepositoryMock();
|
||||
|
||||
|
@ -23,7 +23,7 @@ describe(AlbumService.name, () => {
|
||||
let jobMock: jest.Mocked<IJobRepository>;
|
||||
let userMock: jest.Mocked<IUserRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
albumMock = newAlbumRepositoryMock();
|
||||
assetMock = newAssetRepositoryMock();
|
||||
|
@ -8,7 +8,7 @@ describe(APIKeyService.name, () => {
|
||||
let keyMock: jest.Mocked<IKeyRepository>;
|
||||
let cryptoMock: jest.Mocked<ICryptoRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
cryptoMock = newCryptoRepositoryMock();
|
||||
keyMock = newKeyRepositoryMock();
|
||||
sut = new APIKeyService(cryptoMock, keyMock);
|
||||
|
@ -169,7 +169,7 @@ describe(AssetService.name, () => {
|
||||
expect(sut).toBeDefined();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
assetMock = newAssetRepositoryMock();
|
||||
communicationMock = newCommunicationRepositoryMock();
|
||||
|
@ -31,7 +31,7 @@ describe(AuditService.name, () => {
|
||||
let storageMock: jest.Mocked<IStorageRepository>;
|
||||
let userMock: jest.Mocked<IUserRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
assetMock = newAssetRepositoryMock();
|
||||
cryptoMock = newCryptoRepositoryMock();
|
||||
|
@ -74,7 +74,7 @@ describe('AuthService', () => {
|
||||
let callbackMock: jest.Mock;
|
||||
let userinfoMock: jest.Mock;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
callbackMock = jest.fn().mockReturnValue({ access_token: 'access-token' });
|
||||
userinfoMock = jest.fn().mockResolvedValue({ sub, email });
|
||||
|
||||
|
@ -13,7 +13,7 @@ describe(DatabaseService.name, () => {
|
||||
let sut: DatabaseService;
|
||||
let databaseMock: jest.Mocked<IDatabaseRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
databaseMock = newDatabaseRepositoryMock();
|
||||
|
||||
sut = new DatabaseService(databaseMock);
|
||||
@ -31,7 +31,7 @@ describe(DatabaseService.name, () => {
|
||||
let errorLog: jest.SpyInstance;
|
||||
let warnLog: jest.SpyInstance;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
fatalLog = jest.spyOn(ImmichLogger.prototype, 'fatal');
|
||||
errorLog = jest.spyOn(ImmichLogger.prototype, 'error');
|
||||
warnLog = jest.spyOn(ImmichLogger.prototype, 'warn');
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { ImmichLogger } from '@app/infra/logger';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { QueryFailedError } from 'typeorm';
|
||||
import { Version, VersionType } from '../domain.constant';
|
||||
import {
|
||||
DatabaseExtension,
|
||||
@ -61,7 +60,9 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
private async createVectorExtension() {
|
||||
await this.databaseRepository.createExtension(this.vectorExt).catch(async (error: QueryFailedError) => {
|
||||
try {
|
||||
await this.databaseRepository.createExtension(this.vectorExt);
|
||||
} catch (error) {
|
||||
const otherExt =
|
||||
this.vectorExt === DatabaseExtension.VECTORS ? DatabaseExtension.VECTOR : DatabaseExtension.VECTORS;
|
||||
this.logger.fatal(`
|
||||
@ -78,7 +79,7 @@ export class DatabaseService {
|
||||
In this case, you may set either extension now, but you will not be able to switch to the other extension following a successful startup.
|
||||
`);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async updateVectorExtension() {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ImmichLogger } from '@app/infra/logger';
|
||||
import { applyDecorators } from '@nestjs/common';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
@ -157,7 +158,7 @@ export type Paginated<T> = Promise<PaginationResult<T>>;
|
||||
|
||||
export async function* usePagination<T>(
|
||||
pageSize: number,
|
||||
getNextPage: (pagination: PaginationOptions) => Paginated<T>,
|
||||
getNextPage: (pagination: PaginationOptions) => PaginationResult<T> | Paginated<T>,
|
||||
) {
|
||||
let hasNextPage = true;
|
||||
|
||||
@ -252,3 +253,7 @@ export const setIsSuperset = <T>(set: Set<T>, subset: Set<T>): boolean => {
|
||||
export const setIsEqual = <T>(setA: Set<T>, setB: Set<T>): boolean => {
|
||||
return setA.size === setB.size && setIsSuperset(setA, setB);
|
||||
};
|
||||
|
||||
export const handlePromiseError = <T>(promise: Promise<T>, logger: ImmichLogger): void => {
|
||||
promise.catch((error: Error | any) => logger.error(`Promise error: ${error}`, error?.stack));
|
||||
};
|
||||
|
@ -34,7 +34,7 @@ describe(DownloadService.name, () => {
|
||||
expect(sut).toBeDefined();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
assetMock = newAssetRepositoryMock();
|
||||
storageMock = newStorageRepositoryMock();
|
||||
|
@ -114,9 +114,7 @@ export class DownloadService {
|
||||
const assetIds = dto.assetIds;
|
||||
await this.access.requirePermission(auth, Permission.ASSET_DOWNLOAD, assetIds);
|
||||
const assets = await this.assetRepository.getByIds(assetIds);
|
||||
return (async function* () {
|
||||
yield assets;
|
||||
})();
|
||||
return usePagination(PAGINATION_SIZE, () => ({ hasNextPage: false, items: assets }));
|
||||
}
|
||||
|
||||
if (dto.albumId) {
|
||||
|
@ -37,7 +37,7 @@ describe(JobService.name, () => {
|
||||
let jobMock: jest.Mocked<IJobRepository>;
|
||||
let personMock: jest.Mocked<IPersonRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
assetMock = newAssetRepositoryMock();
|
||||
configMock = newSystemConfigRepositoryMock();
|
||||
communicationMock = newCommunicationRepositoryMock();
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
systemConfigStub,
|
||||
userStub,
|
||||
} from '@test';
|
||||
import { when } from 'jest-when';
|
||||
import { Stats } from 'node:fs';
|
||||
import { ILibraryFileJob, ILibraryRefreshJob, JobName } from '../job';
|
||||
import {
|
||||
@ -55,7 +56,7 @@ describe(LibraryService.name, () => {
|
||||
storageMock = newStorageRepositoryMock();
|
||||
|
||||
// Always validate owner access for library.
|
||||
accessMock.library.checkOwnerAccess.mockImplementation(async (_, libraryIds) => libraryIds);
|
||||
accessMock.library.checkOwnerAccess.mockImplementation((_, libraryIds) => Promise.resolve(libraryIds));
|
||||
|
||||
sut = new LibraryService(
|
||||
accessMock,
|
||||
@ -106,19 +107,13 @@ describe(LibraryService.name, () => {
|
||||
configMock.load.mockResolvedValue(systemConfigStub.libraryWatchEnabled);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
|
||||
libraryMock.get.mockImplementation(async (id) => {
|
||||
switch (id) {
|
||||
case libraryStub.externalLibraryWithImportPaths1.id: {
|
||||
return libraryStub.externalLibraryWithImportPaths1;
|
||||
}
|
||||
case libraryStub.externalLibraryWithImportPaths2.id: {
|
||||
return libraryStub.externalLibraryWithImportPaths2;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
when(libraryMock.get)
|
||||
.calledWith(libraryStub.externalLibraryWithImportPaths1.id)
|
||||
.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1);
|
||||
|
||||
when(libraryMock.get)
|
||||
.calledWith(libraryStub.externalLibraryWithImportPaths2.id)
|
||||
.mockResolvedValue(libraryStub.externalLibraryWithImportPaths2);
|
||||
|
||||
await sut.init();
|
||||
|
||||
@ -1278,19 +1273,13 @@ describe(LibraryService.name, () => {
|
||||
configMock.load.mockResolvedValue(systemConfigStub.libraryWatchEnabled);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
|
||||
libraryMock.get.mockImplementation(async (id) => {
|
||||
switch (id) {
|
||||
case libraryStub.externalLibraryWithImportPaths1.id: {
|
||||
return libraryStub.externalLibraryWithImportPaths1;
|
||||
}
|
||||
case libraryStub.externalLibraryWithImportPaths2.id: {
|
||||
return libraryStub.externalLibraryWithImportPaths2;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
when(libraryMock.get)
|
||||
.calledWith(libraryStub.externalLibraryWithImportPaths1.id)
|
||||
.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1);
|
||||
|
||||
when(libraryMock.get)
|
||||
.calledWith(libraryStub.externalLibraryWithImportPaths2.id)
|
||||
.mockResolvedValue(libraryStub.externalLibraryWithImportPaths2);
|
||||
|
||||
const mockClose = jest.fn();
|
||||
storageMock.watch.mockImplementation(makeMockWatcher({ close: mockClose }));
|
||||
@ -1304,9 +1293,8 @@ describe(LibraryService.name, () => {
|
||||
|
||||
describe('handleDeleteLibrary', () => {
|
||||
it('should not delete a nonexistent library', async () => {
|
||||
libraryMock.get.mockImplementation(async () => {
|
||||
return null;
|
||||
});
|
||||
libraryMock.get.mockResolvedValue(null);
|
||||
|
||||
libraryMock.getAssetIds.mockResolvedValue([]);
|
||||
libraryMock.delete.mockImplementation(async () => {});
|
||||
|
||||
|
@ -9,7 +9,7 @@ import picomatch from 'picomatch';
|
||||
import { AccessCore, Permission } from '../access';
|
||||
import { AuthDto } from '../auth';
|
||||
import { mimeTypes } from '../domain.constant';
|
||||
import { usePagination, validateCronExpression } from '../domain.util';
|
||||
import { handlePromiseError, usePagination, validateCronExpression } from '../domain.util';
|
||||
import { IBaseJob, IEntityJob, ILibraryFileJob, ILibraryRefreshJob, JOBS_ASSET_PAGINATION_SIZE, JobName } from '../job';
|
||||
|
||||
import {
|
||||
@ -43,7 +43,7 @@ export class LibraryService extends EventEmitter {
|
||||
private access: AccessCore;
|
||||
private configCore: SystemConfigCore;
|
||||
private watchLibraries = false;
|
||||
private watchers: Record<string, () => void> = {};
|
||||
private watchers: Record<string, () => Promise<void>> = {};
|
||||
|
||||
constructor(
|
||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
||||
@ -73,7 +73,11 @@ export class LibraryService extends EventEmitter {
|
||||
this.jobRepository.addCronJob(
|
||||
'libraryScan',
|
||||
scan.cronExpression,
|
||||
() => this.jobRepository.queue({ name: JobName.LIBRARY_QUEUE_SCAN_ALL, data: { force: false } }),
|
||||
() =>
|
||||
handlePromiseError(
|
||||
this.jobRepository.queue({ name: JobName.LIBRARY_QUEUE_SCAN_ALL, data: { force: false } }),
|
||||
this.logger,
|
||||
),
|
||||
scan.enabled,
|
||||
);
|
||||
|
||||
@ -81,12 +85,12 @@ export class LibraryService extends EventEmitter {
|
||||
await this.watchAll();
|
||||
}
|
||||
|
||||
this.configCore.config$.subscribe(async ({ library }) => {
|
||||
this.configCore.config$.subscribe(({ library }) => {
|
||||
this.jobRepository.updateCronJob('libraryScan', library.scan.cronExpression, library.scan.enabled);
|
||||
|
||||
if (library.watch.enabled !== this.watchLibraries) {
|
||||
this.watchLibraries = library.watch.enabled;
|
||||
await (this.watchLibraries ? this.watchAll() : this.unwatchAll());
|
||||
handlePromiseError(this.watchLibraries ? this.watchAll() : this.unwatchAll(), this.logger);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -124,28 +128,37 @@ export class LibraryService extends EventEmitter {
|
||||
},
|
||||
{
|
||||
onReady: () => _resolve(),
|
||||
onAdd: async (path) => {
|
||||
this.logger.debug(`File add event received for ${path} in library ${library.id}}`);
|
||||
if (matcher(path)) {
|
||||
await this.scanAssets(library.id, [path], library.ownerId, false);
|
||||
}
|
||||
this.emit('add', path);
|
||||
onAdd: (path) => {
|
||||
const handler = async () => {
|
||||
this.logger.debug(`File add event received for ${path} in library ${library.id}}`);
|
||||
if (matcher(path)) {
|
||||
await this.scanAssets(library.id, [path], library.ownerId, false);
|
||||
}
|
||||
this.emit('add', path);
|
||||
};
|
||||
return handlePromiseError(handler(), this.logger);
|
||||
},
|
||||
onChange: async (path) => {
|
||||
this.logger.debug(`Detected file change for ${path} in library ${library.id}`);
|
||||
if (matcher(path)) {
|
||||
// Note: if the changed file was not previously imported, it will be imported now.
|
||||
await this.scanAssets(library.id, [path], library.ownerId, false);
|
||||
}
|
||||
this.emit('change', path);
|
||||
onChange: (path) => {
|
||||
const handler = async () => {
|
||||
this.logger.debug(`Detected file change for ${path} in library ${library.id}`);
|
||||
if (matcher(path)) {
|
||||
// Note: if the changed file was not previously imported, it will be imported now.
|
||||
await this.scanAssets(library.id, [path], library.ownerId, false);
|
||||
}
|
||||
this.emit('change', path);
|
||||
};
|
||||
return handlePromiseError(handler(), this.logger);
|
||||
},
|
||||
onUnlink: async (path) => {
|
||||
this.logger.debug(`Detected deleted file at ${path} in library ${library.id}`);
|
||||
const asset = await this.assetRepository.getByLibraryIdAndOriginalPath(library.id, path);
|
||||
if (asset && matcher(path)) {
|
||||
await this.assetRepository.save({ id: asset.id, isOffline: true });
|
||||
}
|
||||
this.emit('unlink', path);
|
||||
onUnlink: (path) => {
|
||||
const handler = async () => {
|
||||
this.logger.debug(`Detected deleted file at ${path} in library ${library.id}`);
|
||||
const asset = await this.assetRepository.getByLibraryIdAndOriginalPath(library.id, path);
|
||||
if (asset && matcher(path)) {
|
||||
await this.assetRepository.save({ id: asset.id, isOffline: true });
|
||||
}
|
||||
this.emit('unlink', path);
|
||||
};
|
||||
return handlePromiseError(handler(), this.logger);
|
||||
},
|
||||
onError: (error) => {
|
||||
// TODO: should we log, or throw an exception?
|
||||
|
@ -48,7 +48,7 @@ describe(MediaService.name, () => {
|
||||
let storageMock: jest.Mocked<IStorageRepository>;
|
||||
let cryptoMock: jest.Mocked<ICryptoRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
assetMock = newAssetRepositoryMock();
|
||||
configMock = newSystemConfigRepositoryMock();
|
||||
jobMock = newJobRepositoryMock();
|
||||
|
@ -56,7 +56,7 @@ describe(MetadataService.name, () => {
|
||||
let databaseMock: jest.Mocked<IDatabaseRepository>;
|
||||
let sut: MetadataService;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
albumMock = newAlbumRepositoryMock();
|
||||
assetMock = newAssetRepositoryMock();
|
||||
configMock = newSystemConfigRepositoryMock();
|
||||
|
@ -7,7 +7,7 @@ import _ from 'lodash';
|
||||
import { Duration } from 'luxon';
|
||||
import { constants } from 'node:fs/promises';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { usePagination } from '../domain.util';
|
||||
import { handlePromiseError, usePagination } from '../domain.util';
|
||||
import { IBaseJob, IEntityJob, ISidecarWriteJob, JOBS_ASSET_PAGINATION_SIZE, JobName, QueueName } from '../job';
|
||||
import {
|
||||
ClientEvent,
|
||||
@ -124,7 +124,7 @@ export class MetadataService {
|
||||
|
||||
async init() {
|
||||
if (!this.subscription) {
|
||||
this.subscription = this.configCore.config$.subscribe(() => this.init());
|
||||
this.subscription = this.configCore.config$.subscribe(() => handlePromiseError(this.init(), this.logger));
|
||||
}
|
||||
|
||||
const { reverseGeocoding } = await this.configCore.getConfig();
|
||||
|
@ -49,7 +49,7 @@ describe(PartnerService.name, () => {
|
||||
let partnerMock: jest.Mocked<IPartnerRepository>;
|
||||
let accessMock: jest.Mocked<IAccessRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
partnerMock = newPartnerRepositoryMock();
|
||||
sut = new PartnerService(partnerMock, accessMock);
|
||||
});
|
||||
|
@ -80,7 +80,7 @@ describe(PersonService.name, () => {
|
||||
let cryptoMock: jest.Mocked<ICryptoRepository>;
|
||||
let sut: PersonService;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
assetMock = newAssetRepositoryMock();
|
||||
configMock = newSystemConfigRepositoryMock();
|
||||
|
@ -34,7 +34,7 @@ export interface ClientEventMap {
|
||||
[ClientEvent.NEW_RELEASE]: ReleaseNotification;
|
||||
}
|
||||
|
||||
export type OnConnectCallback = (userId: string) => Promise<void>;
|
||||
export type OnConnectCallback = (userId: string) => void | Promise<void>;
|
||||
export type OnServerEventCallback = () => Promise<void>;
|
||||
|
||||
export interface ICommunicationRepository {
|
||||
|
@ -47,6 +47,6 @@ export interface IStorageRepository {
|
||||
crawl(crawlOptions: CrawlOptionsDto): Promise<string[]>;
|
||||
copyFile(source: string, target: string): Promise<void>;
|
||||
rename(source: string, target: string): Promise<void>;
|
||||
watch(paths: string[], options: WatchOptions, events: Partial<WatchEvents>): () => void;
|
||||
watch(paths: string[], options: WatchOptions, events: Partial<WatchEvents>): () => Promise<void>;
|
||||
utimes(filepath: string, atime: Date, mtime: Date): Promise<void>;
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ export class SearchService {
|
||||
return userIds;
|
||||
}
|
||||
|
||||
private async mapResponse(assets: AssetEntity[], nextPage: string | null): Promise<SearchResponseDto> {
|
||||
private mapResponse(assets: AssetEntity[], nextPage: string | null): SearchResponseDto {
|
||||
return {
|
||||
albums: { total: 0, count: 0, items: [], facets: [] },
|
||||
assets: {
|
||||
|
@ -170,7 +170,7 @@ export class ServerInfoService {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async handleConnect(userId: string) {
|
||||
private handleConnect(userId: string) {
|
||||
this.communicationRepository.send(ClientEvent.SERVER_VERSION, userId, serverVersion);
|
||||
this.newReleaseNotification(userId);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ describe(SharedLinkService.name, () => {
|
||||
let cryptoMock: jest.Mocked<ICryptoRepository>;
|
||||
let shareMock: jest.Mocked<ISharedLinkRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
cryptoMock = newCryptoRepositoryMock();
|
||||
shareMock = newSharedLinkRepositoryMock();
|
||||
|
@ -35,7 +35,7 @@ describe(SmartInfoService.name, () => {
|
||||
let machineMock: jest.Mocked<IMachineLearningRepository>;
|
||||
let databaseMock: jest.Mocked<IDatabaseRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
assetMock = newAssetRepositoryMock();
|
||||
configMock = newSystemConfigRepositoryMock();
|
||||
searchMock = newSearchRepositoryMock();
|
||||
|
@ -45,7 +45,7 @@ describe(StorageTemplateService.name, () => {
|
||||
expect(sut).toBeDefined();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
configMock = newSystemConfigRepositoryMock();
|
||||
assetMock = newAssetRepositoryMock();
|
||||
albumMock = newAlbumRepositoryMock();
|
||||
|
@ -6,7 +6,7 @@ describe(StorageService.name, () => {
|
||||
let sut: StorageService;
|
||||
let storageMock: jest.Mocked<IStorageRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
storageMock = newStorageRepositoryMock();
|
||||
sut = new StorageService(storageMock);
|
||||
});
|
||||
|
@ -148,7 +148,7 @@ describe(SystemConfigService.name, () => {
|
||||
let communicationMock: jest.Mocked<ICommunicationRepository>;
|
||||
let smartInfoMock: jest.Mocked<ISearchRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.IMMICH_CONFIG_FILE;
|
||||
configMock = newSystemConfigRepositoryMock();
|
||||
communicationMock = newCommunicationRepositoryMock();
|
||||
|
@ -118,7 +118,7 @@ export class SystemConfigService {
|
||||
await this.core.refreshConfig();
|
||||
}
|
||||
|
||||
private async setLogLevel({ logging }: SystemConfig) {
|
||||
private setLogLevel({ logging }: SystemConfig) {
|
||||
const envLevel = this.getEnvLogLevel();
|
||||
const configLevel = logging.enabled ? logging.level : false;
|
||||
const level = envLevel ?? configLevel;
|
||||
@ -130,7 +130,7 @@ export class SystemConfigService {
|
||||
return process.env.LOG_LEVEL as LogLevel;
|
||||
}
|
||||
|
||||
private async validateConfig(newConfig: SystemConfig, oldConfig: SystemConfig) {
|
||||
private validateConfig(newConfig: SystemConfig, oldConfig: SystemConfig) {
|
||||
if (!_.isEqual(instanceToPlain(newConfig.logging), oldConfig.logging) && this.getEnvLogLevel()) {
|
||||
throw new Error('Logging cannot be changed while the environment variable LOG_LEVEL is set.');
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ describe(TrashService.name, () => {
|
||||
expect(sut).toBeDefined();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
assetMock = newAssetRepositoryMock();
|
||||
communicationMock = newCommunicationRepositoryMock();
|
||||
|
@ -49,7 +49,7 @@ describe(UserService.name, () => {
|
||||
let libraryMock: jest.Mocked<ILibraryRepository>;
|
||||
let storageMock: jest.Mocked<IStorageRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
albumMock = newAlbumRepositoryMock();
|
||||
assetMock = newAssetRepositoryMock();
|
||||
cryptoRepositoryMock = newCryptoRepositoryMock();
|
||||
|
@ -15,7 +15,7 @@ import { routeToErrorMessage } from '../app.utils';
|
||||
export class ErrorInterceptor implements NestInterceptor {
|
||||
private logger = new ImmichLogger(ErrorInterceptor.name);
|
||||
|
||||
async intercept(context: ExecutionContext, next: CallHandler<any>): Promise<Observable<any>> {
|
||||
intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> {
|
||||
return next.handle().pipe(
|
||||
catchError((error) =>
|
||||
throwError(() => {
|
||||
|
@ -40,9 +40,9 @@ interface Callback<T> {
|
||||
(error: null, result: T): void;
|
||||
}
|
||||
|
||||
const callbackify = async <T>(target: (...arguments_: any[]) => T, callback: Callback<T>) => {
|
||||
const callbackify = <T>(target: (...arguments_: any[]) => T, callback: Callback<T>) => {
|
||||
try {
|
||||
return callback(null, await target());
|
||||
return callback(null, target());
|
||||
} catch (error: Error | any) {
|
||||
return callback(error);
|
||||
}
|
||||
|
@ -544,7 +544,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
}
|
||||
|
||||
async getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats> {
|
||||
let builder = await this.repository
|
||||
let builder = this.repository
|
||||
.createQueryBuilder('asset')
|
||||
.select(`COUNT(asset.id)`, 'count')
|
||||
.addSelect(`asset.type`, 'type')
|
||||
|
@ -166,7 +166,7 @@ export class LibraryRepository implements ILibraryRepository {
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async getAssetIds(libraryId: string, withDeleted = false): Promise<string[]> {
|
||||
let query = await this.repository
|
||||
let query = this.repository
|
||||
.createQueryBuilder('library')
|
||||
.innerJoinAndSelect('library.assets', 'assets')
|
||||
.where('library.id = :id', { id: libraryId })
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { CropOptions, IMediaRepository, ResizeOptions, TranscodeOptions, VideoInfo } from '@app/domain';
|
||||
import {
|
||||
CropOptions,
|
||||
IMediaRepository,
|
||||
ResizeOptions,
|
||||
TranscodeOptions,
|
||||
VideoInfo,
|
||||
handlePromiseError,
|
||||
} from '@app/domain';
|
||||
import { Colorspace } from '@app/infra/entities';
|
||||
import { ImmichLogger } from '@app/infra/logger';
|
||||
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg';
|
||||
@ -99,8 +106,8 @@ export class MediaRepository implements IMediaRepository {
|
||||
.addOptions('-pass', '2')
|
||||
.addOptions('-passlogfile', output)
|
||||
.on('error', reject)
|
||||
.on('end', () => fs.unlink(`${output}-0.log`))
|
||||
.on('end', () => fs.rm(`${output}-0.log.mbtree`, { force: true }))
|
||||
.on('end', () => handlePromiseError(fs.unlink(`${output}-0.log`), this.logger))
|
||||
.on('end', () => handlePromiseError(fs.rm(`${output}-0.log.mbtree`, { force: true }), this.logger))
|
||||
.on('end', resolve)
|
||||
.run();
|
||||
})
|
||||
|
@ -75,22 +75,22 @@ class JobMock implements IJobRepository {
|
||||
async resume() {}
|
||||
async empty() {}
|
||||
async setConcurrency() {}
|
||||
async getQueueStatus() {
|
||||
return null as any;
|
||||
getQueueStatus() {
|
||||
return Promise.resolve(null) as any;
|
||||
}
|
||||
async getJobCounts() {
|
||||
return null as any;
|
||||
getJobCounts() {
|
||||
return Promise.resolve(null) as any;
|
||||
}
|
||||
async pause() {}
|
||||
async clear() {
|
||||
return [];
|
||||
clear() {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
async waitForQueueCompletion() {}
|
||||
}
|
||||
|
||||
class MediaMockRepository extends MediaRepository {
|
||||
async generateThumbhash() {
|
||||
return Buffer.from('mock-thumbhash');
|
||||
generateThumbhash() {
|
||||
return Promise.resolve(Buffer.from('mock-thumbhash'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { WatchOptions } from 'chokidar';
|
||||
|
||||
interface MockWatcherOptions {
|
||||
items?: Array<{ event: 'change' | 'add' | 'unlink' | 'error'; value: string }>;
|
||||
close?: () => void;
|
||||
close?: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const makeMockWatcher =
|
||||
@ -29,7 +29,12 @@ export const makeMockWatcher =
|
||||
}
|
||||
}
|
||||
}
|
||||
return () => close?.();
|
||||
|
||||
if (close) {
|
||||
return () => close();
|
||||
}
|
||||
|
||||
return () => Promise.resolve();
|
||||
};
|
||||
|
||||
export const newStorageRepositoryMock = (reset = true): jest.Mocked<IStorageRepository> => {
|
||||
|
Loading…
Reference in New Issue
Block a user