mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
refactor: library type (#9525)
This commit is contained in:
parent
4353153fe6
commit
84d824d6a7
66 changed files with 183 additions and 984 deletions
|
|
@ -4,7 +4,6 @@ import { SystemConfig } from 'src/config';
|
|||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||
import { mapLibrary } from 'src/dtos/library.dto';
|
||||
import { AssetType } from 'src/entities/asset.entity';
|
||||
import { LibraryType } from 'src/entities/library.entity';
|
||||
import { UserEntity } from 'src/entities/user.entity';
|
||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||
|
|
@ -213,18 +212,6 @@ describe(LibraryService.name, () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('should not scan upload libraries', async () => {
|
||||
const mockLibraryJob: ILibraryRefreshJob = {
|
||||
id: libraryStub.externalLibrary1.id,
|
||||
refreshModifiedFiles: false,
|
||||
refreshAllFiles: false,
|
||||
};
|
||||
|
||||
libraryMock.get.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
|
||||
await expect(sut.handleQueueAssetRefresh(mockLibraryJob)).resolves.toBe(JobStatus.FAILED);
|
||||
});
|
||||
|
||||
it('should ignore import paths that do not exist', async () => {
|
||||
storageMock.stat.mockImplementation((path): Promise<Stats> => {
|
||||
if (path === libraryStub.externalLibraryWithImportPaths1.importPaths[0]) {
|
||||
|
|
@ -707,7 +694,6 @@ describe(LibraryService.name, () => {
|
|||
describe('delete', () => {
|
||||
it('should delete a library', async () => {
|
||||
assetMock.getByLibraryIdAndOriginalPath.mockResolvedValue(assetStub.image);
|
||||
libraryMock.getUploadLibraryCount.mockResolvedValue(2);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
|
||||
await sut.delete(libraryStub.externalLibrary1.id);
|
||||
|
|
@ -720,21 +706,8 @@ describe(LibraryService.name, () => {
|
|||
expect(libraryMock.softDelete).toHaveBeenCalledWith(libraryStub.externalLibrary1.id);
|
||||
});
|
||||
|
||||
it('should throw error if the last upload library is deleted', async () => {
|
||||
assetMock.getByLibraryIdAndOriginalPath.mockResolvedValue(assetStub.image);
|
||||
libraryMock.getUploadLibraryCount.mockResolvedValue(1);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
|
||||
await expect(sut.delete(libraryStub.uploadLibrary1.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(jobMock.queue).not.toHaveBeenCalled();
|
||||
expect(jobMock.queueAll).not.toHaveBeenCalled();
|
||||
expect(libraryMock.softDelete).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allow an external library to be deleted', async () => {
|
||||
assetMock.getByLibraryIdAndOriginalPath.mockResolvedValue(assetStub.image);
|
||||
libraryMock.getUploadLibraryCount.mockResolvedValue(1);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
|
||||
await sut.delete(libraryStub.externalLibrary1.id);
|
||||
|
|
@ -749,7 +722,6 @@ describe(LibraryService.name, () => {
|
|||
|
||||
it('should unwatch an external library when deleted', async () => {
|
||||
assetMock.getByLibraryIdAndOriginalPath.mockResolvedValue(assetStub.image);
|
||||
libraryMock.getUploadLibraryCount.mockResolvedValue(1);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1);
|
||||
libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]);
|
||||
|
||||
|
|
@ -767,37 +739,37 @@ describe(LibraryService.name, () => {
|
|||
|
||||
describe('get', () => {
|
||||
it('should return a library', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
await expect(sut.get(libraryStub.uploadLibrary1.id)).resolves.toEqual(
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
await expect(sut.get(libraryStub.externalLibrary1.id)).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: libraryStub.uploadLibrary1.id,
|
||||
name: libraryStub.uploadLibrary1.name,
|
||||
ownerId: libraryStub.uploadLibrary1.ownerId,
|
||||
id: libraryStub.externalLibrary1.id,
|
||||
name: libraryStub.externalLibrary1.name,
|
||||
ownerId: libraryStub.externalLibrary1.ownerId,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(libraryMock.get).toHaveBeenCalledWith(libraryStub.uploadLibrary1.id);
|
||||
expect(libraryMock.get).toHaveBeenCalledWith(libraryStub.externalLibrary1.id);
|
||||
});
|
||||
|
||||
it('should throw an error when a library is not found', async () => {
|
||||
libraryMock.get.mockResolvedValue(null);
|
||||
await expect(sut.get(libraryStub.uploadLibrary1.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
expect(libraryMock.get).toHaveBeenCalledWith(libraryStub.uploadLibrary1.id);
|
||||
await expect(sut.get(libraryStub.externalLibrary1.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
expect(libraryMock.get).toHaveBeenCalledWith(libraryStub.externalLibrary1.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStatistics', () => {
|
||||
it('should return library statistics', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
libraryMock.getStatistics.mockResolvedValue({ photos: 10, videos: 0, total: 10, usage: 1337 });
|
||||
await expect(sut.getStatistics(libraryStub.uploadLibrary1.id)).resolves.toEqual({
|
||||
await expect(sut.getStatistics(libraryStub.externalLibrary1.id)).resolves.toEqual({
|
||||
photos: 10,
|
||||
videos: 0,
|
||||
total: 10,
|
||||
usage: 1337,
|
||||
});
|
||||
|
||||
expect(libraryMock.getStatistics).toHaveBeenCalledWith(libraryStub.uploadLibrary1.id);
|
||||
expect(libraryMock.getStatistics).toHaveBeenCalledWith(libraryStub.externalLibrary1.id);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -805,10 +777,9 @@ describe(LibraryService.name, () => {
|
|||
describe('external library', () => {
|
||||
it('should create with default settings', async () => {
|
||||
libraryMock.create.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
await expect(sut.create({ ownerId: authStub.admin.user.id, type: LibraryType.EXTERNAL })).resolves.toEqual(
|
||||
await expect(sut.create({ ownerId: authStub.admin.user.id })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: libraryStub.externalLibrary1.id,
|
||||
type: LibraryType.EXTERNAL,
|
||||
name: libraryStub.externalLibrary1.name,
|
||||
ownerId: libraryStub.externalLibrary1.ownerId,
|
||||
assetCount: 0,
|
||||
|
|
@ -823,7 +794,6 @@ describe(LibraryService.name, () => {
|
|||
expect(libraryMock.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: expect.any(String),
|
||||
type: LibraryType.EXTERNAL,
|
||||
importPaths: [],
|
||||
exclusionPatterns: [],
|
||||
}),
|
||||
|
|
@ -832,12 +802,9 @@ describe(LibraryService.name, () => {
|
|||
|
||||
it('should create with name', async () => {
|
||||
libraryMock.create.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
await expect(
|
||||
sut.create({ ownerId: authStub.admin.user.id, type: LibraryType.EXTERNAL, name: 'My Awesome Library' }),
|
||||
).resolves.toEqual(
|
||||
await expect(sut.create({ ownerId: authStub.admin.user.id, name: 'My Awesome Library' })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: libraryStub.externalLibrary1.id,
|
||||
type: LibraryType.EXTERNAL,
|
||||
name: libraryStub.externalLibrary1.name,
|
||||
ownerId: libraryStub.externalLibrary1.ownerId,
|
||||
assetCount: 0,
|
||||
|
|
@ -852,7 +819,6 @@ describe(LibraryService.name, () => {
|
|||
expect(libraryMock.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'My Awesome Library',
|
||||
type: LibraryType.EXTERNAL,
|
||||
importPaths: [],
|
||||
exclusionPatterns: [],
|
||||
}),
|
||||
|
|
@ -864,13 +830,11 @@ describe(LibraryService.name, () => {
|
|||
await expect(
|
||||
sut.create({
|
||||
ownerId: authStub.admin.user.id,
|
||||
type: LibraryType.EXTERNAL,
|
||||
importPaths: ['/data/images', '/data/videos'],
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: libraryStub.externalLibrary1.id,
|
||||
type: LibraryType.EXTERNAL,
|
||||
name: libraryStub.externalLibrary1.name,
|
||||
ownerId: libraryStub.externalLibrary1.ownerId,
|
||||
assetCount: 0,
|
||||
|
|
@ -885,7 +849,6 @@ describe(LibraryService.name, () => {
|
|||
expect(libraryMock.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: expect.any(String),
|
||||
type: LibraryType.EXTERNAL,
|
||||
importPaths: ['/data/images', '/data/videos'],
|
||||
exclusionPatterns: [],
|
||||
}),
|
||||
|
|
@ -901,7 +864,6 @@ describe(LibraryService.name, () => {
|
|||
await sut.init();
|
||||
await sut.create({
|
||||
ownerId: authStub.admin.user.id,
|
||||
type: LibraryType.EXTERNAL,
|
||||
importPaths: libraryStub.externalLibraryWithImportPaths1.importPaths,
|
||||
});
|
||||
});
|
||||
|
|
@ -911,13 +873,11 @@ describe(LibraryService.name, () => {
|
|||
await expect(
|
||||
sut.create({
|
||||
ownerId: authStub.admin.user.id,
|
||||
type: LibraryType.EXTERNAL,
|
||||
exclusionPatterns: ['*.tmp', '*.bak'],
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: libraryStub.externalLibrary1.id,
|
||||
type: LibraryType.EXTERNAL,
|
||||
name: libraryStub.externalLibrary1.name,
|
||||
ownerId: libraryStub.externalLibrary1.ownerId,
|
||||
assetCount: 0,
|
||||
|
|
@ -932,105 +892,22 @@ describe(LibraryService.name, () => {
|
|||
expect(libraryMock.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: expect.any(String),
|
||||
type: LibraryType.EXTERNAL,
|
||||
importPaths: [],
|
||||
exclusionPatterns: ['*.tmp', '*.bak'],
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('upload library', () => {
|
||||
it('should create with default settings', async () => {
|
||||
libraryMock.create.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
await expect(sut.create({ ownerId: authStub.admin.user.id, type: LibraryType.UPLOAD })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: libraryStub.uploadLibrary1.id,
|
||||
type: LibraryType.UPLOAD,
|
||||
name: libraryStub.uploadLibrary1.name,
|
||||
ownerId: libraryStub.uploadLibrary1.ownerId,
|
||||
assetCount: 0,
|
||||
importPaths: [],
|
||||
exclusionPatterns: [],
|
||||
createdAt: libraryStub.uploadLibrary1.createdAt,
|
||||
updatedAt: libraryStub.uploadLibrary1.updatedAt,
|
||||
refreshedAt: null,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(libraryMock.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'New Upload Library',
|
||||
type: LibraryType.UPLOAD,
|
||||
importPaths: [],
|
||||
exclusionPatterns: [],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should create with name', async () => {
|
||||
libraryMock.create.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
await expect(
|
||||
sut.create({ ownerId: authStub.admin.user.id, type: LibraryType.UPLOAD, name: 'My Awesome Library' }),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: libraryStub.uploadLibrary1.id,
|
||||
type: LibraryType.UPLOAD,
|
||||
name: libraryStub.uploadLibrary1.name,
|
||||
ownerId: libraryStub.uploadLibrary1.ownerId,
|
||||
assetCount: 0,
|
||||
importPaths: [],
|
||||
exclusionPatterns: [],
|
||||
createdAt: libraryStub.uploadLibrary1.createdAt,
|
||||
updatedAt: libraryStub.uploadLibrary1.updatedAt,
|
||||
refreshedAt: null,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(libraryMock.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: 'My Awesome Library',
|
||||
type: LibraryType.UPLOAD,
|
||||
importPaths: [],
|
||||
exclusionPatterns: [],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not create with import paths', async () => {
|
||||
await expect(
|
||||
sut.create({
|
||||
ownerId: authStub.admin.user.id,
|
||||
type: LibraryType.UPLOAD,
|
||||
importPaths: ['/data/images', '/data/videos'],
|
||||
}),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(libraryMock.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not create with exclusion patterns', async () => {
|
||||
await expect(
|
||||
sut.create({
|
||||
ownerId: authStub.admin.user.id,
|
||||
type: LibraryType.UPLOAD,
|
||||
exclusionPatterns: ['*.tmp', '*.bak'],
|
||||
}),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(libraryMock.create).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleQueueCleanup', () => {
|
||||
it('should queue cleanup jobs', async () => {
|
||||
libraryMock.getAllDeleted.mockResolvedValue([libraryStub.uploadLibrary1, libraryStub.externalLibrary1]);
|
||||
libraryMock.getAllDeleted.mockResolvedValue([libraryStub.externalLibrary1, libraryStub.externalLibrary2]);
|
||||
await expect(sut.handleQueueCleanup()).resolves.toBe(JobStatus.SUCCESS);
|
||||
|
||||
expect(jobMock.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.LIBRARY_DELETE, data: { id: libraryStub.uploadLibrary1.id } },
|
||||
{ name: JobName.LIBRARY_DELETE, data: { id: libraryStub.externalLibrary1.id } },
|
||||
{ name: JobName.LIBRARY_DELETE, data: { id: libraryStub.externalLibrary2.id } },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1044,9 +921,9 @@ describe(LibraryService.name, () => {
|
|||
});
|
||||
|
||||
it('should update library', async () => {
|
||||
libraryMock.update.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
await expect(sut.update('library-id', {})).resolves.toEqual(mapLibrary(libraryStub.uploadLibrary1));
|
||||
libraryMock.update.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
await expect(sut.update('library-id', {})).resolves.toEqual(mapLibrary(libraryStub.externalLibrary1));
|
||||
expect(libraryMock.update).toHaveBeenCalledWith(expect.objectContaining({ id: 'library-id' }));
|
||||
});
|
||||
});
|
||||
|
|
@ -1109,15 +986,6 @@ describe(LibraryService.name, () => {
|
|||
expect(storageMock.watch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should throw error when watching upload library', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
libraryMock.getAll.mockResolvedValue([libraryStub.uploadLibrary1]);
|
||||
|
||||
await expect(sut.watchAll()).rejects.toThrow('Can only watch external libraries');
|
||||
|
||||
expect(storageMock.watch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle a new file event', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1);
|
||||
libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]);
|
||||
|
|
@ -1253,25 +1121,25 @@ describe(LibraryService.name, () => {
|
|||
libraryMock.getAssetIds.mockResolvedValue([]);
|
||||
libraryMock.delete.mockImplementation(async () => {});
|
||||
|
||||
await expect(sut.handleDeleteLibrary({ id: libraryStub.uploadLibrary1.id })).resolves.toBe(JobStatus.FAILED);
|
||||
await expect(sut.handleDeleteLibrary({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.FAILED);
|
||||
});
|
||||
|
||||
it('should delete an empty library', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
libraryMock.getAssetIds.mockResolvedValue([]);
|
||||
libraryMock.delete.mockImplementation(async () => {});
|
||||
|
||||
await expect(sut.handleDeleteLibrary({ id: libraryStub.uploadLibrary1.id })).resolves.toBe(JobStatus.SUCCESS);
|
||||
await expect(sut.handleDeleteLibrary({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SUCCESS);
|
||||
});
|
||||
|
||||
it('should delete a library with assets', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
libraryMock.getAssetIds.mockResolvedValue([assetStub.image1.id]);
|
||||
libraryMock.delete.mockImplementation(async () => {});
|
||||
|
||||
assetMock.getById.mockResolvedValue(assetStub.image1);
|
||||
|
||||
await expect(sut.handleDeleteLibrary({ id: libraryStub.uploadLibrary1.id })).resolves.toBe(JobStatus.SUCCESS);
|
||||
await expect(sut.handleDeleteLibrary({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SUCCESS);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1295,14 +1163,6 @@ describe(LibraryService.name, () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('should not queue a library scan of upload library', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.uploadLibrary1);
|
||||
|
||||
await expect(sut.queueScan(libraryStub.uploadLibrary1.id, {})).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(jobMock.queue).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should queue a library scan of all modified assets', async () => {
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue