mirror of
https://github.com/immich-app/immich.git
synced 2026-03-02 02:28:50 +03:00
refactor: more small tests (#26159)
This commit is contained in:
@@ -7,11 +7,10 @@ import { AssetType, CronJob, ImmichWorker, JobName, JobStatus } from 'src/enum';
|
||||
import { LibraryService } from 'src/services/library.service';
|
||||
import { ILibraryBulkIdsJob, ILibraryFileJob } from 'src/types';
|
||||
import { AssetFactory } from 'test/factories/asset.factory';
|
||||
import { assetStub } from 'test/fixtures/asset.stub';
|
||||
import { authStub } from 'test/fixtures/auth.stub';
|
||||
import { systemConfigStub } from 'test/fixtures/system-config.stub';
|
||||
import { makeMockWatcher } from 'test/repositories/storage.repository.mock';
|
||||
import { factory, newUuid } from 'test/small.factory';
|
||||
import { factory, newDate, newUuid } from 'test/small.factory';
|
||||
import { makeStream, newTestService, ServiceMocks } from 'test/utils';
|
||||
import { vitest } from 'vitest';
|
||||
|
||||
@@ -307,13 +306,13 @@ describe(LibraryService.name, () => {
|
||||
|
||||
it('should queue asset sync', async () => {
|
||||
const library = factory.library({ importPaths: ['/foo', '/bar'] });
|
||||
const asset = AssetFactory.create({ libraryId: library.id, isExternal: true });
|
||||
|
||||
mocks.library.get.mockResolvedValue(library);
|
||||
mocks.storage.walk.mockImplementation(async function* generator() {});
|
||||
mocks.library.streamAssetIds.mockReturnValue(makeStream([assetStub.external]));
|
||||
mocks.library.streamAssetIds.mockReturnValue(makeStream([asset]));
|
||||
mocks.asset.getLibraryAssetCount.mockResolvedValue(1);
|
||||
mocks.asset.detectOfflineExternalAssets.mockResolvedValue({ numUpdatedRows: 0n });
|
||||
mocks.library.streamAssetIds.mockReturnValue(makeStream([assetStub.external]));
|
||||
|
||||
const response = await sut.handleQueueSyncAssets({ id: library.id });
|
||||
|
||||
@@ -323,7 +322,7 @@ describe(LibraryService.name, () => {
|
||||
libraryId: library.id,
|
||||
importPaths: library.importPaths,
|
||||
exclusionPatterns: library.exclusionPatterns,
|
||||
assetIds: [assetStub.external.id],
|
||||
assetIds: [asset.id],
|
||||
progressCounter: 1,
|
||||
totalAssets: 1,
|
||||
},
|
||||
@@ -344,8 +343,9 @@ describe(LibraryService.name, () => {
|
||||
|
||||
describe('handleSyncAssets', () => {
|
||||
it('should offline assets no longer on disk', async () => {
|
||||
const asset = AssetFactory.create({ libraryId: 'library-id', isExternal: true });
|
||||
const mockAssetJob: ILibraryBulkIdsJob = {
|
||||
assetIds: [assetStub.external.id],
|
||||
assetIds: [asset.id],
|
||||
libraryId: newUuid(),
|
||||
importPaths: ['/'],
|
||||
exclusionPatterns: [],
|
||||
@@ -353,20 +353,21 @@ describe(LibraryService.name, () => {
|
||||
progressCounter: 0,
|
||||
};
|
||||
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.external]);
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([asset]);
|
||||
mocks.storage.stat.mockRejectedValue(new Error('ENOENT, no such file or directory'));
|
||||
|
||||
await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.Success);
|
||||
|
||||
expect(mocks.asset.updateAll).toHaveBeenCalledWith([assetStub.external.id], {
|
||||
expect(mocks.asset.updateAll).toHaveBeenCalledWith([asset.id], {
|
||||
isOffline: true,
|
||||
deletedAt: expect.anything(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should set assets deleted from disk as offline', async () => {
|
||||
const asset = AssetFactory.create({ libraryId: 'library-id', isExternal: true });
|
||||
const mockAssetJob: ILibraryBulkIdsJob = {
|
||||
assetIds: [assetStub.external.id],
|
||||
assetIds: [asset.id],
|
||||
libraryId: newUuid(),
|
||||
importPaths: ['/data/user2'],
|
||||
exclusionPatterns: [],
|
||||
@@ -374,20 +375,21 @@ describe(LibraryService.name, () => {
|
||||
progressCounter: 0,
|
||||
};
|
||||
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.external]);
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([asset]);
|
||||
mocks.storage.stat.mockRejectedValue(new Error('Could not read file'));
|
||||
|
||||
await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.Success);
|
||||
|
||||
expect(mocks.asset.updateAll).toHaveBeenCalledWith([assetStub.external.id], {
|
||||
expect(mocks.asset.updateAll).toHaveBeenCalledWith([asset.id], {
|
||||
isOffline: true,
|
||||
deletedAt: expect.anything(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should do nothing with offline assets deleted from disk', async () => {
|
||||
const asset = AssetFactory.create({ isOffline: true, deletedAt: newDate() });
|
||||
const mockAssetJob: ILibraryBulkIdsJob = {
|
||||
assetIds: [assetStub.trashedOffline.id],
|
||||
assetIds: [asset.id],
|
||||
libraryId: newUuid(),
|
||||
importPaths: ['/data/user2'],
|
||||
exclusionPatterns: [],
|
||||
@@ -395,7 +397,7 @@ describe(LibraryService.name, () => {
|
||||
progressCounter: 0,
|
||||
};
|
||||
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]);
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([asset]);
|
||||
mocks.storage.stat.mockRejectedValue(new Error('Could not read file'));
|
||||
|
||||
await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.Success);
|
||||
@@ -404,8 +406,9 @@ describe(LibraryService.name, () => {
|
||||
});
|
||||
|
||||
it('should un-trash an asset previously marked as offline', async () => {
|
||||
const asset = AssetFactory.create({ originalPath: '/original/path.jpg', isOffline: true, deletedAt: newDate() });
|
||||
const mockAssetJob: ILibraryBulkIdsJob = {
|
||||
assetIds: [assetStub.trashedOffline.id],
|
||||
assetIds: [asset.id],
|
||||
libraryId: newUuid(),
|
||||
importPaths: ['/original/'],
|
||||
exclusionPatterns: [],
|
||||
@@ -413,20 +416,21 @@ describe(LibraryService.name, () => {
|
||||
progressCounter: 0,
|
||||
};
|
||||
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: assetStub.external.fileModifiedAt } as Stats);
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([asset]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: newDate() } as Stats);
|
||||
|
||||
await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.Success);
|
||||
|
||||
expect(mocks.asset.updateAll).toHaveBeenCalledWith([assetStub.external.id], {
|
||||
expect(mocks.asset.updateAll).toHaveBeenCalledWith([asset.id], {
|
||||
isOffline: false,
|
||||
deletedAt: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('should do nothing with offline asset if covered by exclusion pattern', async () => {
|
||||
const asset = AssetFactory.create({ originalPath: '/original/path.jpg', isOffline: true, deletedAt: newDate() });
|
||||
const mockAssetJob: ILibraryBulkIdsJob = {
|
||||
assetIds: [assetStub.trashedOffline.id],
|
||||
assetIds: [asset.id],
|
||||
libraryId: newUuid(),
|
||||
importPaths: ['/original/'],
|
||||
exclusionPatterns: ['**/path.jpg'],
|
||||
@@ -434,8 +438,8 @@ describe(LibraryService.name, () => {
|
||||
progressCounter: 0,
|
||||
};
|
||||
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: assetStub.external.fileModifiedAt } as Stats);
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([asset]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: newDate() } as Stats);
|
||||
|
||||
await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.Success);
|
||||
|
||||
@@ -445,8 +449,9 @@ describe(LibraryService.name, () => {
|
||||
});
|
||||
|
||||
it('should do nothing with offline asset if not in import path', async () => {
|
||||
const asset = AssetFactory.create({ originalPath: '/original/path.jpg', isOffline: true, deletedAt: newDate() });
|
||||
const mockAssetJob: ILibraryBulkIdsJob = {
|
||||
assetIds: [assetStub.trashedOffline.id],
|
||||
assetIds: [asset.id],
|
||||
libraryId: newUuid(),
|
||||
importPaths: ['/import/'],
|
||||
exclusionPatterns: [],
|
||||
@@ -454,8 +459,8 @@ describe(LibraryService.name, () => {
|
||||
progressCounter: 0,
|
||||
};
|
||||
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: assetStub.external.fileModifiedAt } as Stats);
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([asset]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: newDate() } as Stats);
|
||||
|
||||
await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.Success);
|
||||
|
||||
@@ -465,8 +470,9 @@ describe(LibraryService.name, () => {
|
||||
});
|
||||
|
||||
it('should do nothing with unchanged online assets', async () => {
|
||||
const asset = AssetFactory.create({ libraryId: 'library-id', isExternal: true });
|
||||
const mockAssetJob: ILibraryBulkIdsJob = {
|
||||
assetIds: [assetStub.external.id],
|
||||
assetIds: [asset.id],
|
||||
libraryId: newUuid(),
|
||||
importPaths: ['/'],
|
||||
exclusionPatterns: [],
|
||||
@@ -474,8 +480,8 @@ describe(LibraryService.name, () => {
|
||||
progressCounter: 0,
|
||||
};
|
||||
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.external]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: assetStub.external.fileModifiedAt } as Stats);
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([asset]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: asset.fileModifiedAt } as Stats);
|
||||
|
||||
await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.Success);
|
||||
|
||||
@@ -483,8 +489,9 @@ describe(LibraryService.name, () => {
|
||||
});
|
||||
|
||||
it('should not touch fileCreatedAt when un-trashing an asset previously marked as offline', async () => {
|
||||
const asset = AssetFactory.create({ isOffline: true, deletedAt: newDate() });
|
||||
const mockAssetJob: ILibraryBulkIdsJob = {
|
||||
assetIds: [assetStub.trashedOffline.id],
|
||||
assetIds: [asset.id],
|
||||
libraryId: newUuid(),
|
||||
importPaths: ['/'],
|
||||
exclusionPatterns: [],
|
||||
@@ -492,13 +499,13 @@ describe(LibraryService.name, () => {
|
||||
progressCounter: 0,
|
||||
};
|
||||
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.trashedOffline]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: assetStub.trashedOffline.fileModifiedAt } as Stats);
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([asset]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime: newDate() } as Stats);
|
||||
|
||||
await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.Success);
|
||||
|
||||
expect(mocks.asset.updateAll).toHaveBeenCalledWith(
|
||||
[assetStub.trashedOffline.id],
|
||||
[asset.id],
|
||||
expect.not.objectContaining({
|
||||
fileCreatedAt: expect.anything(),
|
||||
}),
|
||||
@@ -506,8 +513,9 @@ describe(LibraryService.name, () => {
|
||||
});
|
||||
|
||||
it('should update with online assets that have changed', async () => {
|
||||
const asset = AssetFactory.create({ libraryId: 'library-id', isExternal: true });
|
||||
const mockAssetJob: ILibraryBulkIdsJob = {
|
||||
assetIds: [assetStub.external.id],
|
||||
assetIds: [asset.id],
|
||||
libraryId: newUuid(),
|
||||
importPaths: ['/'],
|
||||
exclusionPatterns: [],
|
||||
@@ -515,13 +523,9 @@ describe(LibraryService.name, () => {
|
||||
progressCounter: 0,
|
||||
};
|
||||
|
||||
if (assetStub.external.fileModifiedAt == null) {
|
||||
throw new Error('fileModifiedAt is null');
|
||||
}
|
||||
const mtime = new Date(asset.fileModifiedAt.getDate() + 1);
|
||||
|
||||
const mtime = new Date(assetStub.external.fileModifiedAt.getDate() + 1);
|
||||
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([assetStub.external]);
|
||||
mocks.assetJob.getForSyncAssets.mockResolvedValue([asset]);
|
||||
mocks.storage.stat.mockResolvedValue({ mtime } as Stats);
|
||||
|
||||
await expect(sut.handleSyncAssets(mockAssetJob)).resolves.toBe(JobStatus.Success);
|
||||
@@ -530,7 +534,7 @@ describe(LibraryService.name, () => {
|
||||
{
|
||||
name: JobName.SidecarCheck,
|
||||
data: {
|
||||
id: assetStub.external.id,
|
||||
id: asset.id,
|
||||
source: 'upload',
|
||||
},
|
||||
},
|
||||
@@ -1023,9 +1027,10 @@ describe(LibraryService.name, () => {
|
||||
|
||||
it('should handle an error event', async () => {
|
||||
const library = factory.library({ importPaths: ['/foo', '/bar'] });
|
||||
const asset = AssetFactory.create({ libraryId: library.id, isExternal: true });
|
||||
|
||||
mocks.library.get.mockResolvedValue(library);
|
||||
mocks.asset.getByLibraryIdAndOriginalPath.mockResolvedValue(assetStub.external);
|
||||
mocks.asset.getByLibraryIdAndOriginalPath.mockResolvedValue(asset);
|
||||
mocks.library.getAll.mockResolvedValue([library]);
|
||||
mocks.storage.watch.mockImplementation(
|
||||
makeMockWatcher({
|
||||
|
||||
Reference in New Issue
Block a user