mirror of
https://github.com/immich-app/immich.git
synced 2025-03-11 02:23:09 -05:00
refactor event type mocks
This commit is contained in:
parent
0f331a3b50
commit
3d9c677c4a
7 changed files with 60 additions and 41 deletions
|
@ -1,4 +1,4 @@
|
|||
import { LibraryResponseDto, LibraryService, LoginResponseDto, StorageEvent } from '@app/domain';
|
||||
import { LibraryResponseDto, LibraryService, LoginResponseDto, StorageEventType } from '@app/domain';
|
||||
import { AssetType, LibraryType } from '@app/infra/entities';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
@ -57,7 +57,7 @@ describe(`Library watcher (e2e)`, () => {
|
|||
`${IMMICH_TEST_ASSET_TEMP_PATH}/file.jpg`,
|
||||
);
|
||||
|
||||
await waitForEvent(libraryService, StorageEvent.ADD);
|
||||
await waitForEvent(libraryService, StorageEventType.ADD);
|
||||
|
||||
const afterAssets = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
expect(afterAssets.length).toEqual(1);
|
||||
|
@ -84,7 +84,7 @@ describe(`Library watcher (e2e)`, () => {
|
|||
`${IMMICH_TEST_ASSET_TEMP_PATH}/file5.jPg`,
|
||||
);
|
||||
|
||||
await waitForEvent(libraryService, StorageEvent.ADD, 4);
|
||||
await waitForEvent(libraryService, StorageEventType.ADD, 4);
|
||||
|
||||
const afterAssets = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
expect(afterAssets.length).toEqual(4);
|
||||
|
@ -96,7 +96,7 @@ describe(`Library watcher (e2e)`, () => {
|
|||
`${IMMICH_TEST_ASSET_TEMP_PATH}/file.jpg`,
|
||||
);
|
||||
|
||||
await waitForEvent(libraryService, StorageEvent.ADD);
|
||||
await waitForEvent(libraryService, StorageEventType.ADD);
|
||||
|
||||
const originalAssets = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
expect(originalAssets.length).toEqual(1);
|
||||
|
@ -106,7 +106,7 @@ describe(`Library watcher (e2e)`, () => {
|
|||
`${IMMICH_TEST_ASSET_TEMP_PATH}/file.jpg`,
|
||||
);
|
||||
|
||||
await waitForEvent(libraryService, StorageEvent.CHANGE);
|
||||
await waitForEvent(libraryService, StorageEventType.CHANGE);
|
||||
|
||||
const afterAssets = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
expect(afterAssets).toEqual([
|
||||
|
@ -158,7 +158,7 @@ describe(`Library watcher (e2e)`, () => {
|
|||
`${IMMICH_TEST_ASSET_TEMP_PATH}/dir3/file4.jpg`,
|
||||
);
|
||||
|
||||
await waitForEvent(libraryService, StorageEvent.ADD, 3);
|
||||
await waitForEvent(libraryService, StorageEventType.ADD, 3);
|
||||
|
||||
const assets = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
expect(assets.length).toEqual(3);
|
||||
|
@ -170,14 +170,14 @@ describe(`Library watcher (e2e)`, () => {
|
|||
`${IMMICH_TEST_ASSET_TEMP_PATH}/dir1/file.jpg`,
|
||||
);
|
||||
|
||||
await waitForEvent(libraryService, StorageEvent.ADD);
|
||||
await waitForEvent(libraryService, StorageEventType.ADD);
|
||||
|
||||
const addedAssets = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
expect(addedAssets.length).toEqual(1);
|
||||
|
||||
await fs.unlink(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir1/file.jpg`);
|
||||
|
||||
await waitForEvent(libraryService, StorageEvent.UNLINK);
|
||||
await waitForEvent(libraryService, StorageEventType.UNLINK);
|
||||
|
||||
const afterAssets = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
expect(afterAssets[0].isOffline).toEqual(true);
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
IStorageRepository,
|
||||
ISystemConfigRepository,
|
||||
IUserRepository,
|
||||
StorageEventType,
|
||||
} from '../repositories';
|
||||
import { SystemConfigCore } from '../system-config/system-config.core';
|
||||
import { mapLibrary } from './library.dto';
|
||||
|
@ -1187,7 +1188,9 @@ describe(LibraryService.name, () => {
|
|||
it('should handle a new file event', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1);
|
||||
libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]);
|
||||
storageMock.watch.mockImplementation(makeMockWatcher({ items: [{ event: 'add', value: '/foo/photo.jpg' }] }));
|
||||
storageMock.watch.mockImplementation(
|
||||
makeMockWatcher({ items: [{ event: StorageEventType.ADD, value: '/foo/photo.jpg' }] }),
|
||||
);
|
||||
|
||||
await sut.watchAll();
|
||||
|
||||
|
@ -1208,7 +1211,7 @@ describe(LibraryService.name, () => {
|
|||
libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1);
|
||||
libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]);
|
||||
storageMock.watch.mockImplementation(
|
||||
makeMockWatcher({ items: [{ event: 'change', value: '/foo/photo.jpg' }] }),
|
||||
makeMockWatcher({ items: [{ event: StorageEventType.CHANGE, value: '/foo/photo.jpg' }] }),
|
||||
);
|
||||
|
||||
await sut.watchAll();
|
||||
|
@ -1231,7 +1234,7 @@ describe(LibraryService.name, () => {
|
|||
libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]);
|
||||
assetMock.getByLibraryIdAndOriginalPath.mockResolvedValue(assetStub.external);
|
||||
storageMock.watch.mockImplementation(
|
||||
makeMockWatcher({ items: [{ event: 'unlink', value: '/foo/photo.jpg' }] }),
|
||||
makeMockWatcher({ items: [{ event: StorageEventType.UNLINK, value: '/foo/photo.jpg' }] }),
|
||||
);
|
||||
|
||||
await sut.watchAll();
|
||||
|
@ -1245,17 +1248,19 @@ describe(LibraryService.name, () => {
|
|||
libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]);
|
||||
storageMock.watch.mockImplementation(
|
||||
makeMockWatcher({
|
||||
items: [{ event: 'error', value: 'Error!' }],
|
||||
items: [{ event: StorageEventType.ERROR, value: 'Error!' }],
|
||||
}),
|
||||
);
|
||||
|
||||
await sut.watchAll();
|
||||
await expect(sut.watchAll()).rejects.toEqual([expect.stringMatching('/Error: Error/')]);
|
||||
});
|
||||
|
||||
it('should ignore unknown extensions', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1);
|
||||
libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]);
|
||||
storageMock.watch.mockImplementation(makeMockWatcher({ items: [{ event: 'add', value: '/foo/photo.jpg' }] }));
|
||||
storageMock.watch.mockImplementation(
|
||||
makeMockWatcher({ items: [{ event: StorageEventType.ADD, value: '/foo/photo.jpg' }] }),
|
||||
);
|
||||
|
||||
await sut.watchAll();
|
||||
|
||||
|
@ -1265,7 +1270,9 @@ describe(LibraryService.name, () => {
|
|||
it('should ignore excluded paths', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.patternPath);
|
||||
libraryMock.getAll.mockResolvedValue([libraryStub.patternPath]);
|
||||
storageMock.watch.mockImplementation(makeMockWatcher({ items: [{ event: 'add', value: '/dir1/photo.txt' }] }));
|
||||
storageMock.watch.mockImplementation(
|
||||
makeMockWatcher({ items: [{ event: StorageEventType.ADD, value: '/dir1/photo.txt' }] }),
|
||||
);
|
||||
|
||||
await sut.watchAll();
|
||||
|
||||
|
@ -1275,7 +1282,9 @@ describe(LibraryService.name, () => {
|
|||
it('should ignore excluded paths without case sensitivity', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.patternPath);
|
||||
libraryMock.getAll.mockResolvedValue([libraryStub.patternPath]);
|
||||
storageMock.watch.mockImplementation(makeMockWatcher({ items: [{ event: 'add', value: '/DIR1/photo.txt' }] }));
|
||||
storageMock.watch.mockImplementation(
|
||||
makeMockWatcher({ items: [{ event: StorageEventType.ADD, value: '/DIR1/photo.txt' }] }),
|
||||
);
|
||||
|
||||
await sut.watchAll();
|
||||
|
||||
|
@ -1284,7 +1293,7 @@ describe(LibraryService.name, () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('tearDown', () => {
|
||||
describe('teardown', () => {
|
||||
it('should tear down all watchers', async () => {
|
||||
libraryMock.getAll.mockResolvedValue([
|
||||
libraryStub.externalLibraryWithImportPaths1,
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
IStorageRepository,
|
||||
ISystemConfigRepository,
|
||||
IUserRepository,
|
||||
StorageEvent,
|
||||
StorageEventType,
|
||||
WithProperty,
|
||||
} from '../repositories';
|
||||
import { SystemConfigCore } from '../system-config';
|
||||
|
@ -97,9 +97,11 @@ export class LibraryService extends EventEmitter {
|
|||
this.configCore.config$.subscribe(async ({ library }) => {
|
||||
this.jobRepository.updateCronJob('libraryScan', library.scan.cronExpression, library.scan.enabled);
|
||||
|
||||
if (this.watchLock && library.watch.enabled !== this.watchLibraries) {
|
||||
this.watchLibraries = library.watch.enabled;
|
||||
await (this.watchLibraries ? this.watchAll() : this.unwatchAll());
|
||||
if (this.watchLock) {
|
||||
if (library.watch.enabled !== this.watchLibraries) {
|
||||
this.watchLibraries = library.watch.enabled;
|
||||
await (this.watchLibraries ? this.watchAll() : this.unwatchAll());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -142,7 +144,7 @@ export class LibraryService extends EventEmitter {
|
|||
if (matcher(path)) {
|
||||
await this.scanAssets(library.id, [path], library.ownerId, false);
|
||||
}
|
||||
this.emit(StorageEvent.ADD, path);
|
||||
this.emit(StorageEventType.ADD, path);
|
||||
},
|
||||
onChange: async (path) => {
|
||||
this.logger.debug(`Detected file change for ${path} in library ${library.id}`);
|
||||
|
@ -150,7 +152,7 @@ export class LibraryService extends EventEmitter {
|
|||
// 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(StorageEvent.CHANGE, path);
|
||||
this.emit(StorageEventType.CHANGE, path);
|
||||
},
|
||||
onUnlink: async (path) => {
|
||||
this.logger.debug(`Detected deleted file at ${path} in library ${library.id}`);
|
||||
|
@ -158,12 +160,12 @@ export class LibraryService extends EventEmitter {
|
|||
if (asset && matcher(path)) {
|
||||
await this.assetRepository.save({ id: asset.id, isOffline: true });
|
||||
}
|
||||
this.emit(StorageEvent.UNLINK, path);
|
||||
this.emit(StorageEventType.UNLINK, path);
|
||||
},
|
||||
onError: (error) => {
|
||||
// TODO: should we log, or throw an exception?
|
||||
this.logger.error(`Library watcher for library ${library.id} encountered error: ${error}`);
|
||||
this.emit(StorageEvent.ERROR, error);
|
||||
this.emit(StorageEventType.ERROR, error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
|
@ -31,7 +31,7 @@ export interface WatchEvents {
|
|||
onError(error: Error): void;
|
||||
}
|
||||
|
||||
export enum StorageEvent {
|
||||
export enum StorageEventType {
|
||||
READY = 'ready',
|
||||
ADD = 'add',
|
||||
CHANGE = 'change',
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
IStorageRepository,
|
||||
ImmichReadStream,
|
||||
ImmichZipStream,
|
||||
StorageEvent,
|
||||
StorageEventType,
|
||||
WatchEvents,
|
||||
mimeTypes,
|
||||
} from '@app/domain';
|
||||
|
@ -142,11 +142,11 @@ export class FilesystemProvider implements IStorageRepository {
|
|||
watch(paths: string[], options: WatchOptions, events: Partial<WatchEvents>) {
|
||||
const watcher = chokidar.watch(paths, options);
|
||||
|
||||
watcher.on(StorageEvent.READY, () => events.onReady?.());
|
||||
watcher.on(StorageEvent.ADD, (path) => events.onAdd?.(path));
|
||||
watcher.on(StorageEvent.CHANGE, (path) => events.onChange?.(path));
|
||||
watcher.on(StorageEvent.UNLINK, (path) => events.onUnlink?.(path));
|
||||
watcher.on(StorageEvent.ERROR, (error) => events.onError?.(error));
|
||||
watcher.on(StorageEventType.READY, () => events.onReady?.());
|
||||
watcher.on(StorageEventType.ADD, (path) => events.onAdd?.(path));
|
||||
watcher.on(StorageEventType.CHANGE, (path) => events.onChange?.(path));
|
||||
watcher.on(StorageEventType.UNLINK, (path) => events.onUnlink?.(path));
|
||||
watcher.on(StorageEventType.ERROR, (error) => events.onError?.(error));
|
||||
|
||||
return () => watcher.close();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { IJobRepository, IMediaRepository, JobItem, JobItemHandler, QueueName, StorageEvent } from '@app/domain';
|
||||
import { IJobRepository, IMediaRepository, JobItem, JobItemHandler, QueueName, StorageEventType } from '@app/domain';
|
||||
import { AppModule } from '@app/immich';
|
||||
import { InfraModule, InfraTestModule, dataSource } from '@app/infra';
|
||||
import { MediaRepository } from '@app/infra/repositories';
|
||||
|
@ -145,7 +145,7 @@ export function waitForEvent(emitter: EventEmitter, event: string, times = 1): P
|
|||
promises.push(
|
||||
new Promise((resolve, reject) => {
|
||||
const success = (value: any) => {
|
||||
emitter.off(StorageEvent.ERROR, fail);
|
||||
emitter.off(StorageEventType.ERROR, fail);
|
||||
resolve(value);
|
||||
};
|
||||
const fail = (error: Error) => {
|
||||
|
@ -153,7 +153,7 @@ export function waitForEvent(emitter: EventEmitter, event: string, times = 1): P
|
|||
reject(error);
|
||||
};
|
||||
emitter.once(event, success);
|
||||
emitter.once(StorageEvent.ERROR, fail);
|
||||
emitter.once(StorageEventType.ERROR, fail);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
import { IStorageRepository, StorageCore, WatchEvents } from '@app/domain';
|
||||
import { IStorageRepository, StorageCore, StorageEventType, WatchEvents } from '@app/domain';
|
||||
import { WatchOptions } from 'chokidar';
|
||||
|
||||
interface MockWatcherOptions {
|
||||
items?: Array<{ event: 'change' | 'add' | 'unlink' | 'error'; value: string }>;
|
||||
items?: Array<{
|
||||
event:
|
||||
| StorageEventType.READY
|
||||
| StorageEventType.ADD
|
||||
| StorageEventType.CHANGE
|
||||
| StorageEventType.UNLINK
|
||||
| StorageEventType.ERROR;
|
||||
value: string;
|
||||
}>;
|
||||
close?: () => void;
|
||||
}
|
||||
|
||||
|
@ -12,19 +20,19 @@ export const makeMockWatcher =
|
|||
events.onReady?.();
|
||||
for (const item of items || []) {
|
||||
switch (item.event) {
|
||||
case 'add': {
|
||||
case StorageEventType.ADD: {
|
||||
events.onAdd?.(item.value);
|
||||
break;
|
||||
}
|
||||
case 'change': {
|
||||
case StorageEventType.CHANGE: {
|
||||
events.onChange?.(item.value);
|
||||
break;
|
||||
}
|
||||
case 'unlink': {
|
||||
case StorageEventType.UNLINK: {
|
||||
events.onUnlink?.(item.value);
|
||||
break;
|
||||
}
|
||||
case 'error': {
|
||||
case StorageEventType.ERROR: {
|
||||
events.onError?.(new Error(item.value));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue