mirror of
https://github.com/immich-app/immich.git
synced 2026-03-01 18:19:10 +03:00
refactor: small tests (#26141)
This commit is contained in:
@@ -3,7 +3,7 @@ import { DateTime } from 'luxon';
|
||||
import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { AssetJobName, AssetStatsResponseDto } from 'src/dtos/asset.dto';
|
||||
import { AssetEditAction } from 'src/dtos/editing.dto';
|
||||
import { AssetMetadataKey, AssetStatus, AssetType, AssetVisibility, JobName, JobStatus } from 'src/enum';
|
||||
import { AssetFileType, AssetMetadataKey, AssetStatus, AssetType, AssetVisibility, JobName, JobStatus } from 'src/enum';
|
||||
import { AssetStats } from 'src/repositories/asset.repository';
|
||||
import { AssetService } from 'src/services/asset.service';
|
||||
import { AssetFactory } from 'test/factories/asset.factory';
|
||||
@@ -79,7 +79,7 @@ describe(AssetService.name, () => {
|
||||
describe('getRandom', () => {
|
||||
it('should get own random assets', async () => {
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
mocks.asset.getRandom.mockResolvedValue([assetStub.image]);
|
||||
mocks.asset.getRandom.mockResolvedValue([AssetFactory.create()]);
|
||||
|
||||
await sut.getRandom(authStub.admin, 1);
|
||||
|
||||
@@ -90,7 +90,7 @@ describe(AssetService.name, () => {
|
||||
const partner = factory.partner({ inTimeline: false });
|
||||
const auth = factory.auth({ user: { id: partner.sharedWithId } });
|
||||
|
||||
mocks.asset.getRandom.mockResolvedValue([assetStub.image]);
|
||||
mocks.asset.getRandom.mockResolvedValue([AssetFactory.create()]);
|
||||
mocks.partner.getAll.mockResolvedValue([partner]);
|
||||
|
||||
await sut.getRandom(auth, 1);
|
||||
@@ -102,7 +102,7 @@ describe(AssetService.name, () => {
|
||||
const partner = factory.partner({ inTimeline: true });
|
||||
const auth = factory.auth({ user: { id: partner.sharedWithId } });
|
||||
|
||||
mocks.asset.getRandom.mockResolvedValue([assetStub.image]);
|
||||
mocks.asset.getRandom.mockResolvedValue([AssetFactory.create()]);
|
||||
mocks.partner.getAll.mockResolvedValue([partner]);
|
||||
|
||||
await sut.getRandom(auth, 1);
|
||||
@@ -113,88 +113,90 @@ describe(AssetService.name, () => {
|
||||
|
||||
describe('get', () => {
|
||||
it('should allow owner access', async () => {
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetStub.image.id]));
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
const asset = AssetFactory.create();
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.asset.getById.mockResolvedValue(asset);
|
||||
|
||||
await sut.get(authStub.admin, assetStub.image.id);
|
||||
await sut.get(authStub.admin, asset.id);
|
||||
|
||||
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(
|
||||
authStub.admin.user.id,
|
||||
new Set([assetStub.image.id]),
|
||||
new Set([asset.id]),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow shared link access', async () => {
|
||||
mocks.access.asset.checkSharedLinkAccess.mockResolvedValue(new Set([assetStub.image.id]));
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
const asset = AssetFactory.create();
|
||||
mocks.access.asset.checkSharedLinkAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.asset.getById.mockResolvedValue(asset);
|
||||
|
||||
await sut.get(authStub.adminSharedLink, assetStub.image.id);
|
||||
await sut.get(authStub.adminSharedLink, asset.id);
|
||||
|
||||
expect(mocks.access.asset.checkSharedLinkAccess).toHaveBeenCalledWith(
|
||||
authStub.adminSharedLink.sharedLink?.id,
|
||||
new Set([assetStub.image.id]),
|
||||
new Set([asset.id]),
|
||||
);
|
||||
});
|
||||
|
||||
it('should strip metadata for shared link if exif is disabled', async () => {
|
||||
mocks.access.asset.checkSharedLinkAccess.mockResolvedValue(new Set([assetStub.image.id]));
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
const asset = AssetFactory.from().exif({ description: 'foo' }).build();
|
||||
mocks.access.asset.checkSharedLinkAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.asset.getById.mockResolvedValue(asset);
|
||||
|
||||
const result = await sut.get(
|
||||
{ ...authStub.adminSharedLink, sharedLink: { ...authStub.adminSharedLink.sharedLink!, showExif: false } },
|
||||
assetStub.image.id,
|
||||
asset.id,
|
||||
);
|
||||
|
||||
expect(result).toEqual(expect.objectContaining({ hasMetadata: false }));
|
||||
expect(result).not.toHaveProperty('exifInfo');
|
||||
expect(mocks.access.asset.checkSharedLinkAccess).toHaveBeenCalledWith(
|
||||
authStub.adminSharedLink.sharedLink?.id,
|
||||
new Set([assetStub.image.id]),
|
||||
new Set([asset.id]),
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow partner sharing access', async () => {
|
||||
mocks.access.asset.checkPartnerAccess.mockResolvedValue(new Set([assetStub.image.id]));
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
const asset = AssetFactory.create();
|
||||
mocks.access.asset.checkPartnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.asset.getById.mockResolvedValue(asset);
|
||||
|
||||
await sut.get(authStub.admin, assetStub.image.id);
|
||||
await sut.get(authStub.admin, asset.id);
|
||||
|
||||
expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(
|
||||
authStub.admin.user.id,
|
||||
new Set([assetStub.image.id]),
|
||||
);
|
||||
expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set([asset.id]));
|
||||
});
|
||||
|
||||
it('should allow shared album access', async () => {
|
||||
mocks.access.asset.checkAlbumAccess.mockResolvedValue(new Set([assetStub.image.id]));
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
const asset = AssetFactory.create();
|
||||
mocks.access.asset.checkAlbumAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.asset.getById.mockResolvedValue(asset);
|
||||
|
||||
await sut.get(authStub.admin, assetStub.image.id);
|
||||
await sut.get(authStub.admin, asset.id);
|
||||
|
||||
expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(
|
||||
authStub.admin.user.id,
|
||||
new Set([assetStub.image.id]),
|
||||
);
|
||||
expect(mocks.access.asset.checkAlbumAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set([asset.id]));
|
||||
});
|
||||
|
||||
it('should throw an error for no access', async () => {
|
||||
await expect(sut.get(authStub.admin, assetStub.image.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
await expect(sut.get(authStub.admin, AssetFactory.create().id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(mocks.asset.getById).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should throw an error for an invalid shared link', async () => {
|
||||
await expect(sut.get(authStub.adminSharedLink, assetStub.image.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
await expect(sut.get(authStub.adminSharedLink, AssetFactory.create().id)).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
|
||||
expect(mocks.access.asset.checkOwnerAccess).not.toHaveBeenCalled();
|
||||
expect(mocks.asset.getById).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should throw an error if the asset could not be found', async () => {
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetStub.image.id]));
|
||||
const asset = AssetFactory.create();
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||
|
||||
await expect(sut.get(authStub.admin, assetStub.image.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
await expect(sut.get(authStub.admin, asset.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -208,38 +210,41 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should update the asset', async () => {
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1']));
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
mocks.asset.update.mockResolvedValue(assetStub.image);
|
||||
const asset = AssetFactory.create();
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.asset.getById.mockResolvedValue(asset);
|
||||
mocks.asset.update.mockResolvedValue(asset);
|
||||
|
||||
await sut.update(authStub.admin, 'asset-1', { isFavorite: true });
|
||||
await sut.update(authStub.admin, asset.id, { isFavorite: true });
|
||||
|
||||
expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'asset-1', isFavorite: true });
|
||||
expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, isFavorite: true });
|
||||
});
|
||||
|
||||
it('should update the exif description', async () => {
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1']));
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
mocks.asset.update.mockResolvedValue(assetStub.image);
|
||||
const asset = AssetFactory.create();
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.asset.getById.mockResolvedValue(asset);
|
||||
mocks.asset.update.mockResolvedValue(asset);
|
||||
|
||||
await sut.update(authStub.admin, 'asset-1', { description: 'Test description' });
|
||||
await sut.update(authStub.admin, asset.id, { description: 'Test description' });
|
||||
|
||||
expect(mocks.asset.upsertExif).toHaveBeenCalledWith(
|
||||
{ assetId: 'asset-1', description: 'Test description', lockedProperties: ['description'] },
|
||||
{ assetId: asset.id, description: 'Test description', lockedProperties: ['description'] },
|
||||
{ lockedPropertiesBehavior: 'append' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should update the exif rating', async () => {
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1']));
|
||||
mocks.asset.getById.mockResolvedValueOnce(assetStub.image);
|
||||
mocks.asset.update.mockResolvedValueOnce(assetStub.image);
|
||||
const asset = AssetFactory.create();
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.asset.getById.mockResolvedValueOnce(asset);
|
||||
mocks.asset.update.mockResolvedValueOnce(asset);
|
||||
|
||||
await sut.update(authStub.admin, 'asset-1', { rating: 3 });
|
||||
await sut.update(authStub.admin, asset.id, { rating: 3 });
|
||||
|
||||
expect(mocks.asset.upsertExif).toHaveBeenCalledWith(
|
||||
{
|
||||
assetId: 'asset-1',
|
||||
assetId: asset.id,
|
||||
rating: 3,
|
||||
lockedProperties: ['rating'],
|
||||
},
|
||||
@@ -346,10 +351,11 @@ describe(AssetService.name, () => {
|
||||
|
||||
it('should unlink a live video', async () => {
|
||||
const auth = AuthFactory.create();
|
||||
const asset = AssetFactory.create();
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetStub.livePhotoStillAsset.id]));
|
||||
mocks.asset.getById.mockResolvedValueOnce(assetStub.livePhotoStillAsset);
|
||||
mocks.asset.getById.mockResolvedValueOnce(assetStub.livePhotoMotionAsset);
|
||||
mocks.asset.update.mockResolvedValueOnce(assetStub.image);
|
||||
mocks.asset.update.mockResolvedValueOnce(asset);
|
||||
|
||||
await sut.update(auth, assetStub.livePhotoStillAsset.id, { livePhotoVideoId: null });
|
||||
|
||||
@@ -555,7 +561,11 @@ describe(AssetService.name, () => {
|
||||
|
||||
describe('handleAssetDeletion', () => {
|
||||
it('should clean up files', async () => {
|
||||
const asset = assetStub.image;
|
||||
const asset = AssetFactory.from()
|
||||
.file({ type: AssetFileType.Thumbnail })
|
||||
.file({ type: AssetFileType.Preview })
|
||||
.file({ type: AssetFileType.FullSize })
|
||||
.build();
|
||||
mocks.assetJob.getForAssetDeletion.mockResolvedValue(asset);
|
||||
|
||||
await sut.handleAssetDeletion({ id: asset.id, deleteOnDisk: true });
|
||||
@@ -565,12 +575,7 @@ describe(AssetService.name, () => {
|
||||
{
|
||||
name: JobName.FileDelete,
|
||||
data: {
|
||||
files: [
|
||||
'/uploads/user-id/webp/path.ext',
|
||||
'/uploads/user-id/thumbs/path.jpg',
|
||||
'/uploads/user-id/fullsize/path.webp',
|
||||
asset.originalPath,
|
||||
],
|
||||
files: [...asset.files.map(({ path }) => path), asset.originalPath],
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -656,14 +661,15 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should update usage', async () => {
|
||||
mocks.assetJob.getForAssetDeletion.mockResolvedValue(assetStub.image);
|
||||
await sut.handleAssetDeletion({ id: assetStub.image.id, deleteOnDisk: true });
|
||||
expect(mocks.user.updateUsage).toHaveBeenCalledWith(assetStub.image.ownerId, -5000);
|
||||
const asset = AssetFactory.from().exif({ fileSizeInByte: 5000 }).build();
|
||||
mocks.assetJob.getForAssetDeletion.mockResolvedValue(asset);
|
||||
await sut.handleAssetDeletion({ id: asset.id, deleteOnDisk: true });
|
||||
expect(mocks.user.updateUsage).toHaveBeenCalledWith(asset.ownerId, -5000);
|
||||
});
|
||||
|
||||
it('should fail if asset could not be found', async () => {
|
||||
mocks.assetJob.getForAssetDeletion.mockResolvedValue(void 0);
|
||||
await expect(sut.handleAssetDeletion({ id: assetStub.image.id, deleteOnDisk: true })).resolves.toBe(
|
||||
await expect(sut.handleAssetDeletion({ id: AssetFactory.create().id, deleteOnDisk: true })).resolves.toBe(
|
||||
JobStatus.Failed,
|
||||
);
|
||||
});
|
||||
@@ -681,28 +687,30 @@ describe(AssetService.name, () => {
|
||||
it('should return OCR data for an asset', async () => {
|
||||
const ocr1 = factory.assetOcr({ text: 'Hello World' });
|
||||
const ocr2 = factory.assetOcr({ text: 'Test Image' });
|
||||
const asset = AssetFactory.from().exif().build();
|
||||
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1']));
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.ocr.getByAssetId.mockResolvedValue([ocr1, ocr2]);
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
mocks.asset.getById.mockResolvedValue(asset);
|
||||
|
||||
await expect(sut.getOcr(authStub.admin, 'asset-1')).resolves.toEqual([ocr1, ocr2]);
|
||||
await expect(sut.getOcr(authStub.admin, asset.id)).resolves.toEqual([ocr1, ocr2]);
|
||||
|
||||
expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith(
|
||||
authStub.admin.user.id,
|
||||
new Set(['asset-1']),
|
||||
new Set([asset.id]),
|
||||
undefined,
|
||||
);
|
||||
expect(mocks.ocr.getByAssetId).toHaveBeenCalledWith('asset-1');
|
||||
expect(mocks.ocr.getByAssetId).toHaveBeenCalledWith(asset.id);
|
||||
});
|
||||
|
||||
it('should return empty array when no OCR data exists', async () => {
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1']));
|
||||
const asset = AssetFactory.from().exif().build();
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.ocr.getByAssetId.mockResolvedValue([]);
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
await expect(sut.getOcr(authStub.admin, 'asset-1')).resolves.toEqual([]);
|
||||
mocks.asset.getById.mockResolvedValue(asset);
|
||||
await expect(sut.getOcr(authStub.admin, asset.id)).resolves.toEqual([]);
|
||||
|
||||
expect(mocks.ocr.getByAssetId).toHaveBeenCalledWith('asset-1');
|
||||
expect(mocks.ocr.getByAssetId).toHaveBeenCalledWith(asset.id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -746,7 +754,7 @@ describe(AssetService.name, () => {
|
||||
|
||||
describe('getUserAssetsByDeviceId', () => {
|
||||
it('get assets by device id', async () => {
|
||||
const assets = [assetStub.image, assetStub.image1];
|
||||
const assets = [AssetFactory.create(), AssetFactory.create()];
|
||||
|
||||
mocks.asset.getAllByDeviceId.mockResolvedValue(assets.map((asset) => asset.deviceAssetId));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user