mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
refactor(server): test fixtures (#3491)
This commit is contained in:
parent
5f9dfa9493
commit
9e085c1071
@ -8,7 +8,7 @@ import {
|
||||
newAssetRepositoryMock,
|
||||
newJobRepositoryMock,
|
||||
newUserRepositoryMock,
|
||||
userEntityStub,
|
||||
userStub,
|
||||
} from '@test';
|
||||
import _ from 'lodash';
|
||||
import { IAssetRepository } from '../asset';
|
||||
@ -326,12 +326,12 @@ describe(AlbumService.name, () => {
|
||||
accessMock.album.hasOwnerAccess.mockResolvedValue(true);
|
||||
albumMock.getByIds.mockResolvedValue([_.cloneDeep(albumStub.sharedWithAdmin)]);
|
||||
albumMock.update.mockResolvedValue(albumStub.sharedWithAdmin);
|
||||
userMock.get.mockResolvedValue(userEntityStub.user2);
|
||||
userMock.get.mockResolvedValue(userStub.user2);
|
||||
await sut.addUsers(authStub.user1, albumStub.sharedWithAdmin.id, { sharedUserIds: [authStub.user2.id] });
|
||||
expect(albumMock.update).toHaveBeenCalledWith({
|
||||
id: albumStub.sharedWithAdmin.id,
|
||||
updatedAt: expect.any(Date),
|
||||
sharedUsers: [userEntityStub.admin, { id: authStub.user2.id }],
|
||||
sharedUsers: [userStub.admin, { id: authStub.user2.id }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -349,7 +349,7 @@ describe(AlbumService.name, () => {
|
||||
albumMock.getByIds.mockResolvedValue([albumStub.sharedWithUser]);
|
||||
|
||||
await expect(
|
||||
sut.removeUser(authStub.admin, albumStub.sharedWithUser.id, userEntityStub.user1.id),
|
||||
sut.removeUser(authStub.admin, albumStub.sharedWithUser.id, userStub.user1.id),
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(albumMock.update).toHaveBeenCalledTimes(1);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { AssetType } from '@app/infra/entities';
|
||||
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
|
||||
import {
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
authStub,
|
||||
IAccessRepositoryMock,
|
||||
newAccessRepositoryMock,
|
||||
@ -246,7 +246,7 @@ describe(AssetService.name, () => {
|
||||
describe('getMapMarkers', () => {
|
||||
it('should get geo information of assets', async () => {
|
||||
assetMock.getMapMarkers.mockResolvedValue(
|
||||
[assetEntityStub.withLocation].map((asset) => ({
|
||||
[assetStub.withLocation].map((asset) => ({
|
||||
id: asset.id,
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
||||
@ -261,7 +261,7 @@ describe(AssetService.name, () => {
|
||||
|
||||
expect(markers).toHaveLength(1);
|
||||
expect(markers[0]).toEqual({
|
||||
id: assetEntityStub.withLocation.id,
|
||||
id: assetStub.withLocation.id,
|
||||
lat: 100,
|
||||
lon: 100,
|
||||
});
|
||||
@ -308,14 +308,14 @@ describe(AssetService.name, () => {
|
||||
it('should set the title correctly', async () => {
|
||||
when(assetMock.getByDate)
|
||||
.calledWith(authStub.admin.id, new Date('2022-06-15T00:00:00.000Z'))
|
||||
.mockResolvedValue([assetEntityStub.image]);
|
||||
.mockResolvedValue([assetStub.image]);
|
||||
when(assetMock.getByDate)
|
||||
.calledWith(authStub.admin.id, new Date('2021-06-15T00:00:00.000Z'))
|
||||
.mockResolvedValue([assetEntityStub.video]);
|
||||
.mockResolvedValue([assetStub.video]);
|
||||
|
||||
await expect(sut.getMemoryLane(authStub.admin, { timestamp: new Date(2023, 5, 15), years: 2 })).resolves.toEqual([
|
||||
{ title: '1 year since...', assets: [mapAsset(assetEntityStub.image)] },
|
||||
{ title: '2 years since...', assets: [mapAsset(assetEntityStub.video)] },
|
||||
{ title: '1 year since...', assets: [mapAsset(assetStub.image)] },
|
||||
{ title: '2 years since...', assets: [mapAsset(assetStub.video)] },
|
||||
]);
|
||||
|
||||
expect(assetMock.getByDate).toHaveBeenCalledTimes(2);
|
||||
@ -352,12 +352,12 @@ describe(AssetService.name, () => {
|
||||
const stream = new Readable();
|
||||
|
||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
storageMock.createReadStream.mockResolvedValue({ stream });
|
||||
|
||||
await expect(sut.downloadFile(authStub.admin, 'asset-1')).resolves.toEqual({ stream });
|
||||
|
||||
expect(storageMock.createReadStream).toHaveBeenCalledWith(assetEntityStub.image.originalPath, 'image/jpeg');
|
||||
expect(storageMock.createReadStream).toHaveBeenCalledWith(assetStub.image.originalPath, 'image/jpeg');
|
||||
});
|
||||
|
||||
it('should download an archive', async () => {
|
||||
@ -368,7 +368,7 @@ describe(AssetService.name, () => {
|
||||
};
|
||||
|
||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath, assetEntityStub.noWebpPath]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath, assetStub.noWebpPath]);
|
||||
storageMock.createZipStream.mockReturnValue(archiveMock);
|
||||
|
||||
await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1', 'asset-2'] })).resolves.toEqual({
|
||||
@ -388,7 +388,7 @@ describe(AssetService.name, () => {
|
||||
};
|
||||
|
||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath, assetEntityStub.noResizePath]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath, assetStub.noResizePath]);
|
||||
storageMock.createZipStream.mockReturnValue(archiveMock);
|
||||
|
||||
await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1', 'asset-2'] })).resolves.toEqual({
|
||||
@ -408,7 +408,7 @@ describe(AssetService.name, () => {
|
||||
|
||||
it('should return a list of archives (assetIds)', async () => {
|
||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image, assetEntityStub.video]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image, assetStub.video]);
|
||||
|
||||
const assetIds = ['asset-1', 'asset-2'];
|
||||
await expect(sut.getDownloadInfo(authStub.admin, { assetIds })).resolves.toEqual(downloadResponse);
|
||||
@ -419,7 +419,7 @@ describe(AssetService.name, () => {
|
||||
it('should return a list of archives (albumId)', async () => {
|
||||
accessMock.album.hasOwnerAccess.mockResolvedValue(true);
|
||||
assetMock.getByAlbumId.mockResolvedValue({
|
||||
items: [assetEntityStub.image, assetEntityStub.video],
|
||||
items: [assetStub.image, assetStub.video],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
@ -431,7 +431,7 @@ describe(AssetService.name, () => {
|
||||
|
||||
it('should return a list of archives (userId)', async () => {
|
||||
assetMock.getByUserId.mockResolvedValue({
|
||||
items: [assetEntityStub.image, assetEntityStub.video],
|
||||
items: [assetStub.image, assetStub.video],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
@ -445,10 +445,10 @@ describe(AssetService.name, () => {
|
||||
it('should split archives by size', async () => {
|
||||
assetMock.getByUserId.mockResolvedValue({
|
||||
items: [
|
||||
{ ...assetEntityStub.image, id: 'asset-1' },
|
||||
{ ...assetEntityStub.video, id: 'asset-2' },
|
||||
{ ...assetEntityStub.withLocation, id: 'asset-3' },
|
||||
{ ...assetEntityStub.noWebpPath, id: 'asset-4' },
|
||||
{ ...assetStub.image, id: 'asset-1' },
|
||||
{ ...assetStub.video, id: 'asset-2' },
|
||||
{ ...assetStub.withLocation, id: 'asset-3' },
|
||||
{ ...assetStub.noWebpPath, id: 'asset-4' },
|
||||
],
|
||||
hasNextPage: false,
|
||||
});
|
||||
@ -470,18 +470,18 @@ describe(AssetService.name, () => {
|
||||
it('should include the video portion of a live photo', async () => {
|
||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||
when(assetMock.getByIds)
|
||||
.calledWith([assetEntityStub.livePhotoStillAsset.id])
|
||||
.mockResolvedValue([assetEntityStub.livePhotoStillAsset]);
|
||||
.calledWith([assetStub.livePhotoStillAsset.id])
|
||||
.mockResolvedValue([assetStub.livePhotoStillAsset]);
|
||||
when(assetMock.getByIds)
|
||||
.calledWith([assetEntityStub.livePhotoMotionAsset.id])
|
||||
.mockResolvedValue([assetEntityStub.livePhotoMotionAsset]);
|
||||
.calledWith([assetStub.livePhotoMotionAsset.id])
|
||||
.mockResolvedValue([assetStub.livePhotoMotionAsset]);
|
||||
|
||||
const assetIds = [assetEntityStub.livePhotoStillAsset.id];
|
||||
const assetIds = [assetStub.livePhotoStillAsset.id];
|
||||
await expect(sut.getDownloadInfo(authStub.admin, { assetIds })).resolves.toEqual({
|
||||
totalSize: 125_000,
|
||||
archives: [
|
||||
{
|
||||
assetIds: [assetEntityStub.livePhotoStillAsset.id, assetEntityStub.livePhotoMotionAsset.id],
|
||||
assetIds: [assetStub.livePhotoStillAsset.id, assetStub.livePhotoMotionAsset.id],
|
||||
size: 125_000,
|
||||
},
|
||||
],
|
||||
|
@ -12,8 +12,8 @@ import {
|
||||
newUserTokenRepositoryMock,
|
||||
sharedLinkStub,
|
||||
systemConfigStub,
|
||||
userEntityStub,
|
||||
userTokenEntityStub,
|
||||
userStub,
|
||||
userTokenStub,
|
||||
} from '@test';
|
||||
import { IncomingHttpHeaders } from 'http';
|
||||
import { generators, Issuer } from 'openid-client';
|
||||
@ -112,15 +112,15 @@ describe('AuthService', () => {
|
||||
});
|
||||
|
||||
it('should successfully log the user in', async () => {
|
||||
userMock.getByEmail.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
userMock.getByEmail.mockResolvedValue(userStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||
await expect(sut.login(fixtures.login, loginDetails)).resolves.toEqual(loginResponseStub.user1password);
|
||||
expect(userMock.getByEmail).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should generate the cookie headers (insecure)', async () => {
|
||||
userMock.getByEmail.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
userMock.getByEmail.mockResolvedValue(userStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||
await expect(
|
||||
sut.login(fixtures.login, {
|
||||
clientIp: '127.0.0.1',
|
||||
@ -246,10 +246,10 @@ describe('AuthService', () => {
|
||||
});
|
||||
|
||||
it('should validate using authorization header', async () => {
|
||||
userMock.get.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.getByToken.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
userMock.get.mockResolvedValue(userStub.user1);
|
||||
userTokenMock.getByToken.mockResolvedValue(userTokenStub.userToken);
|
||||
const client = { request: { headers: { authorization: 'Bearer auth_token' } } };
|
||||
await expect(sut.validate((client as Socket).request.headers, {})).resolves.toEqual(userEntityStub.user1);
|
||||
await expect(sut.validate((client as Socket).request.headers, {})).resolves.toEqual(userStub.user1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -275,7 +275,7 @@ describe('AuthService', () => {
|
||||
|
||||
it('should accept a base64url key', async () => {
|
||||
shareMock.getByKey.mockResolvedValue(sharedLinkStub.valid);
|
||||
userMock.get.mockResolvedValue(userEntityStub.admin);
|
||||
userMock.get.mockResolvedValue(userStub.admin);
|
||||
const headers: IncomingHttpHeaders = { 'x-immich-share-key': sharedLinkStub.valid.key.toString('base64url') };
|
||||
await expect(sut.validate(headers, {})).resolves.toEqual(authStub.adminSharedLink);
|
||||
expect(shareMock.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key);
|
||||
@ -283,7 +283,7 @@ describe('AuthService', () => {
|
||||
|
||||
it('should accept a hex key', async () => {
|
||||
shareMock.getByKey.mockResolvedValue(sharedLinkStub.valid);
|
||||
userMock.get.mockResolvedValue(userEntityStub.admin);
|
||||
userMock.get.mockResolvedValue(userStub.admin);
|
||||
const headers: IncomingHttpHeaders = { 'x-immich-share-key': sharedLinkStub.valid.key.toString('hex') };
|
||||
await expect(sut.validate(headers, {})).resolves.toEqual(authStub.adminSharedLink);
|
||||
expect(shareMock.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key);
|
||||
@ -298,16 +298,16 @@ describe('AuthService', () => {
|
||||
});
|
||||
|
||||
it('should return an auth dto', async () => {
|
||||
userTokenMock.getByToken.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
userTokenMock.getByToken.mockResolvedValue(userTokenStub.userToken);
|
||||
const headers: IncomingHttpHeaders = { cookie: 'immich_access_token=auth_token' };
|
||||
await expect(sut.validate(headers, {})).resolves.toEqual(userEntityStub.user1);
|
||||
await expect(sut.validate(headers, {})).resolves.toEqual(userStub.user1);
|
||||
});
|
||||
|
||||
it('should update when access time exceeds an hour', async () => {
|
||||
userTokenMock.getByToken.mockResolvedValue(userTokenEntityStub.inactiveToken);
|
||||
userTokenMock.save.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
userTokenMock.getByToken.mockResolvedValue(userTokenStub.inactiveToken);
|
||||
userTokenMock.save.mockResolvedValue(userTokenStub.userToken);
|
||||
const headers: IncomingHttpHeaders = { cookie: 'immich_access_token=auth_token' };
|
||||
await expect(sut.validate(headers, {})).resolves.toEqual(userEntityStub.user1);
|
||||
await expect(sut.validate(headers, {})).resolves.toEqual(userStub.user1);
|
||||
expect(userTokenMock.save.mock.calls[0][0]).toMatchObject({
|
||||
id: 'not_active',
|
||||
token: 'auth_token',
|
||||
@ -338,7 +338,7 @@ describe('AuthService', () => {
|
||||
|
||||
describe('getDevices', () => {
|
||||
it('should get the devices', async () => {
|
||||
userTokenMock.getAll.mockResolvedValue([userTokenEntityStub.userToken, userTokenEntityStub.inactiveToken]);
|
||||
userTokenMock.getAll.mockResolvedValue([userTokenStub.userToken, userTokenStub.inactiveToken]);
|
||||
await expect(sut.getDevices(authStub.user1)).resolves.toEqual([
|
||||
{
|
||||
createdAt: '2021-01-01T00:00:00.000Z',
|
||||
@ -364,7 +364,7 @@ describe('AuthService', () => {
|
||||
|
||||
describe('logoutDevices', () => {
|
||||
it('should logout all devices', async () => {
|
||||
userTokenMock.getAll.mockResolvedValue([userTokenEntityStub.inactiveToken, userTokenEntityStub.userToken]);
|
||||
userTokenMock.getAll.mockResolvedValue([userTokenStub.inactiveToken, userTokenStub.userToken]);
|
||||
|
||||
await sut.logoutDevices(authStub.user1);
|
||||
|
||||
@ -429,24 +429,24 @@ describe('AuthService', () => {
|
||||
|
||||
it('should link an existing user', async () => {
|
||||
configMock.load.mockResolvedValue(systemConfigStub.noAutoRegister);
|
||||
userMock.getByEmail.mockResolvedValue(userEntityStub.user1);
|
||||
userMock.update.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
userMock.getByEmail.mockResolvedValue(userStub.user1);
|
||||
userMock.update.mockResolvedValue(userStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||
|
||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||
loginResponseStub.user1oauth,
|
||||
);
|
||||
|
||||
expect(userMock.getByEmail).toHaveBeenCalledTimes(1);
|
||||
expect(userMock.update).toHaveBeenCalledWith(userEntityStub.user1.id, { oauthId: sub });
|
||||
expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, { oauthId: sub });
|
||||
});
|
||||
|
||||
it('should allow auto registering by default', async () => {
|
||||
configMock.load.mockResolvedValue(systemConfigStub.enabled);
|
||||
userMock.getByEmail.mockResolvedValue(null);
|
||||
userMock.getAdmin.mockResolvedValue(userEntityStub.user1);
|
||||
userMock.create.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
userMock.getAdmin.mockResolvedValue(userStub.user1);
|
||||
userMock.create.mockResolvedValue(userStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||
|
||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||
loginResponseStub.user1oauth,
|
||||
@ -458,8 +458,8 @@ describe('AuthService', () => {
|
||||
|
||||
it('should use the mobile redirect override', async () => {
|
||||
configMock.load.mockResolvedValue(systemConfigStub.override);
|
||||
userMock.getByOAuthId.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
userMock.getByOAuthId.mockResolvedValue(userStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||
|
||||
await sut.callback({ url: `app.immich:/?code=abc123` }, loginDetails);
|
||||
|
||||
@ -468,8 +468,8 @@ describe('AuthService', () => {
|
||||
|
||||
it('should use the mobile redirect override for ios urls with multiple slashes', async () => {
|
||||
configMock.load.mockResolvedValue(systemConfigStub.override);
|
||||
userMock.getByOAuthId.mockResolvedValue(userEntityStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
||||
userMock.getByOAuthId.mockResolvedValue(userStub.user1);
|
||||
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||
|
||||
await sut.callback({ url: `app.immich:///?code=abc123` }, loginDetails);
|
||||
|
||||
@ -480,7 +480,7 @@ describe('AuthService', () => {
|
||||
describe('link', () => {
|
||||
it('should link an account', async () => {
|
||||
configMock.load.mockResolvedValue(systemConfigStub.enabled);
|
||||
userMock.update.mockResolvedValue(userEntityStub.user1);
|
||||
userMock.update.mockResolvedValue(userStub.user1);
|
||||
|
||||
await sut.link(authStub.user1, { url: 'http://immich/user-settings?code=abc123' });
|
||||
|
||||
@ -502,7 +502,7 @@ describe('AuthService', () => {
|
||||
describe('unlink', () => {
|
||||
it('should unlink an account', async () => {
|
||||
configMock.load.mockResolvedValue(systemConfigStub.enabled);
|
||||
userMock.update.mockResolvedValue(userEntityStub.user1);
|
||||
userMock.update.mockResolvedValue(userStub.user1);
|
||||
|
||||
await sut.unlink(authStub.user1);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
faceStub,
|
||||
newAssetRepositoryMock,
|
||||
newFaceRepositoryMock,
|
||||
@ -133,7 +133,7 @@ describe(FacialRecognitionService.name, () => {
|
||||
describe('handleQueueRecognizeFaces', () => {
|
||||
it('should queue missing assets', async () => {
|
||||
assetMock.getWithout.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
await sut.handleQueueRecognizeFaces({});
|
||||
@ -141,13 +141,13 @@ describe(FacialRecognitionService.name, () => {
|
||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.FACES);
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.RECOGNIZE_FACES,
|
||||
data: { id: assetEntityStub.image.id },
|
||||
data: { id: assetStub.image.id },
|
||||
});
|
||||
});
|
||||
|
||||
it('should queue all assets', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
personMock.deleteAll.mockResolvedValue(5);
|
||||
@ -158,24 +158,24 @@ describe(FacialRecognitionService.name, () => {
|
||||
expect(assetMock.getAll).toHaveBeenCalled();
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.RECOGNIZE_FACES,
|
||||
data: { id: assetEntityStub.image.id },
|
||||
data: { id: assetStub.image.id },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleRecognizeFaces', () => {
|
||||
it('should skip when no resize path', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath]);
|
||||
await sut.handleRecognizeFaces({ id: assetEntityStub.noResizePath.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
||||
await sut.handleRecognizeFaces({ id: assetStub.noResizePath.id });
|
||||
expect(machineLearningMock.detectFaces).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle no results', async () => {
|
||||
machineLearningMock.detectFaces.mockResolvedValue([]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
await sut.handleRecognizeFaces({ id: assetEntityStub.image.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
await sut.handleRecognizeFaces({ id: assetStub.image.id });
|
||||
expect(machineLearningMock.detectFaces).toHaveBeenCalledWith({
|
||||
imagePath: assetEntityStub.image.resizePath,
|
||||
imagePath: assetStub.image.resizePath,
|
||||
});
|
||||
expect(faceMock.create).not.toHaveBeenCalled();
|
||||
expect(jobMock.queue).not.toHaveBeenCalled();
|
||||
@ -184,8 +184,8 @@ describe(FacialRecognitionService.name, () => {
|
||||
it('should match existing people', async () => {
|
||||
machineLearningMock.detectFaces.mockResolvedValue([face.middle]);
|
||||
searchMock.searchFaces.mockResolvedValue(faceSearch.oneMatch);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
await sut.handleRecognizeFaces({ id: assetEntityStub.image.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
await sut.handleRecognizeFaces({ id: assetStub.image.id });
|
||||
|
||||
expect(faceMock.create).toHaveBeenCalledWith({
|
||||
personId: 'person-1',
|
||||
@ -204,11 +204,11 @@ describe(FacialRecognitionService.name, () => {
|
||||
machineLearningMock.detectFaces.mockResolvedValue([face.middle]);
|
||||
searchMock.searchFaces.mockResolvedValue(faceSearch.oneRemoteMatch);
|
||||
personMock.create.mockResolvedValue(personStub.noName);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await sut.handleRecognizeFaces({ id: assetEntityStub.image.id });
|
||||
await sut.handleRecognizeFaces({ id: assetStub.image.id });
|
||||
|
||||
expect(personMock.create).toHaveBeenCalledWith({ ownerId: assetEntityStub.image.ownerId });
|
||||
expect(personMock.create).toHaveBeenCalledWith({ ownerId: assetStub.image.ownerId });
|
||||
expect(faceMock.create).toHaveBeenCalledWith({
|
||||
personId: 'person-1',
|
||||
assetId: 'asset-id',
|
||||
@ -254,7 +254,7 @@ describe(FacialRecognitionService.name, () => {
|
||||
});
|
||||
|
||||
it('should skip an asset without a thumbnail', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
||||
|
||||
await sut.handleGenerateFaceThumbnail(face.middle);
|
||||
|
||||
@ -262,7 +262,7 @@ describe(FacialRecognitionService.name, () => {
|
||||
});
|
||||
|
||||
it('should generate a thumbnail', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await sut.handleGenerateFaceThumbnail(face.middle);
|
||||
|
||||
@ -285,7 +285,7 @@ describe(FacialRecognitionService.name, () => {
|
||||
});
|
||||
|
||||
it('should generate a thumbnail without going negative', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await sut.handleGenerateFaceThumbnail(face.start);
|
||||
|
||||
@ -302,7 +302,7 @@ describe(FacialRecognitionService.name, () => {
|
||||
});
|
||||
|
||||
it('should generate a thumbnail without overflowing', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await sut.handleGenerateFaceThumbnail(face.end);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { SystemConfig } from '@app/infra/entities';
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import {
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
asyncTick,
|
||||
newAssetRepositoryMock,
|
||||
newCommunicationRepositoryMock,
|
||||
@ -300,7 +300,7 @@ describe(JobService.name, () => {
|
||||
for (const { item, jobs } of tests) {
|
||||
it(`should queue ${jobs.length} jobs when a ${item.name} job finishes successfully`, async () => {
|
||||
if (item.name === JobName.GENERATE_JPEG_THUMBNAIL && item.data.source === 'upload') {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.livePhotoMotionAsset]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.livePhotoMotionAsset]);
|
||||
} else {
|
||||
assetMock.getByIds.mockResolvedValue([]);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { AssetType, SystemConfigKey, TranscodePolicy, VideoCodec } from '@app/infra/entities';
|
||||
import {
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
newAssetRepositoryMock,
|
||||
newJobRepositoryMock,
|
||||
newMediaRepositoryMock,
|
||||
@ -40,7 +40,7 @@ describe(MediaService.name, () => {
|
||||
describe('handleQueueGenerateThumbnails', () => {
|
||||
it('should queue all assets', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
@ -50,13 +50,13 @@ describe(MediaService.name, () => {
|
||||
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.GENERATE_JPEG_THUMBNAIL,
|
||||
data: { id: assetEntityStub.image.id },
|
||||
data: { id: assetStub.image.id },
|
||||
});
|
||||
});
|
||||
|
||||
it('should queue all assets with missing resize path', async () => {
|
||||
assetMock.getWithout.mockResolvedValue({
|
||||
items: [assetEntityStub.noResizePath],
|
||||
items: [assetStub.noResizePath],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
@ -66,13 +66,13 @@ describe(MediaService.name, () => {
|
||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.GENERATE_JPEG_THUMBNAIL,
|
||||
data: { id: assetEntityStub.image.id },
|
||||
data: { id: assetStub.image.id },
|
||||
});
|
||||
});
|
||||
|
||||
it('should queue all assets with missing webp path', async () => {
|
||||
assetMock.getWithout.mockResolvedValue({
|
||||
items: [assetEntityStub.noWebpPath],
|
||||
items: [assetStub.noWebpPath],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
@ -82,13 +82,13 @@ describe(MediaService.name, () => {
|
||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.GENERATE_WEBP_THUMBNAIL,
|
||||
data: { id: assetEntityStub.image.id },
|
||||
data: { id: assetStub.image.id },
|
||||
});
|
||||
});
|
||||
|
||||
it('should queue all assets with missing thumbhash', async () => {
|
||||
assetMock.getWithout.mockResolvedValue({
|
||||
items: [assetEntityStub.noThumbhash],
|
||||
items: [assetStub.noThumbhash],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
@ -98,7 +98,7 @@ describe(MediaService.name, () => {
|
||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.GENERATE_THUMBHASH_THUMBNAIL,
|
||||
data: { id: assetEntityStub.image.id },
|
||||
data: { id: assetStub.image.id },
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -106,14 +106,14 @@ describe(MediaService.name, () => {
|
||||
describe('handleGenerateJpegThumbnail', () => {
|
||||
it('should skip thumbnail generation if asset not found', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([]);
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetEntityStub.image.id });
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||
expect(mediaMock.resize).not.toHaveBeenCalled();
|
||||
expect(assetMock.save).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should generate a thumbnail for an image', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetEntityStub.image.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||
|
||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id');
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/asset-id.jpeg', {
|
||||
@ -127,8 +127,8 @@ describe(MediaService.name, () => {
|
||||
});
|
||||
|
||||
it('should generate a thumbnail for a video', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.video.id });
|
||||
|
||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id');
|
||||
expect(mediaMock.extractVideoThumbnail).toHaveBeenCalledWith(
|
||||
@ -143,28 +143,28 @@ describe(MediaService.name, () => {
|
||||
});
|
||||
|
||||
it('should run successfully', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetEntityStub.image.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleGenerateWebpThumbnail', () => {
|
||||
it('should skip thumbnail generation if asset not found', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([]);
|
||||
await sut.handleGenerateWebpThumbnail({ id: assetEntityStub.image.id });
|
||||
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
|
||||
expect(mediaMock.resize).not.toHaveBeenCalled();
|
||||
expect(assetMock.save).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should skip thumbnail generate if resize path is missing', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath]);
|
||||
await sut.handleGenerateWebpThumbnail({ id: assetEntityStub.noResizePath.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
||||
await sut.handleGenerateWebpThumbnail({ id: assetStub.noResizePath.id });
|
||||
expect(mediaMock.resize).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should generate a thumbnail', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
await sut.handleGenerateWebpThumbnail({ id: assetEntityStub.image.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
|
||||
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith(
|
||||
'/uploads/user-id/thumbs/path.jpg',
|
||||
@ -178,22 +178,22 @@ describe(MediaService.name, () => {
|
||||
describe('handleGenerateThumbhashThumbnail', () => {
|
||||
it('should skip thumbhash generation if asset not found', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([]);
|
||||
await sut.handleGenerateThumbhashThumbnail({ id: assetEntityStub.image.id });
|
||||
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.image.id });
|
||||
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip thumbhash generation if resize path is missing', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath]);
|
||||
await sut.handleGenerateThumbhashThumbnail({ id: assetEntityStub.noResizePath.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
||||
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.noResizePath.id });
|
||||
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should generate a thumbhash', async () => {
|
||||
const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8');
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
mediaMock.generateThumbhash.mockResolvedValue(thumbhashBuffer);
|
||||
|
||||
await sut.handleGenerateThumbhashThumbnail({ id: assetEntityStub.image.id });
|
||||
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.image.id });
|
||||
|
||||
expect(mediaMock.generateThumbhash).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.jpg');
|
||||
expect(assetMock.save).toHaveBeenCalledWith({ id: 'asset-id', thumbhash: thumbhashBuffer });
|
||||
@ -203,7 +203,7 @@ describe(MediaService.name, () => {
|
||||
describe('handleQueueVideoConversion', () => {
|
||||
it('should queue all video assets', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.video],
|
||||
items: [assetStub.video],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
@ -213,13 +213,13 @@ describe(MediaService.name, () => {
|
||||
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.VIDEO_CONVERSION,
|
||||
data: { id: assetEntityStub.video.id },
|
||||
data: { id: assetStub.video.id },
|
||||
});
|
||||
});
|
||||
|
||||
it('should queue all video assets without encoded videos', async () => {
|
||||
assetMock.getWithout.mockResolvedValue({
|
||||
items: [assetEntityStub.video],
|
||||
items: [assetStub.video],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
@ -229,35 +229,35 @@ describe(MediaService.name, () => {
|
||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.ENCODED_VIDEO);
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.VIDEO_CONVERSION,
|
||||
data: { id: assetEntityStub.video.id },
|
||||
data: { id: assetStub.video.id },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleVideoConversion', () => {
|
||||
beforeEach(() => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
});
|
||||
|
||||
it('should skip transcoding if asset not found', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.probe).not.toHaveBeenCalled();
|
||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip transcoding if non-video asset', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.image.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
await sut.handleVideoConversion({ id: assetStub.image.id });
|
||||
expect(mediaMock.probe).not.toHaveBeenCalled();
|
||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should transcode the longest stream', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
mediaMock.probe.mockResolvedValue(probeStub.multipleVideoStreams);
|
||||
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
|
||||
expect(mediaMock.probe).toHaveBeenCalledWith('/original/path.ext');
|
||||
expect(configMock.load).toHaveBeenCalled();
|
||||
@ -282,23 +282,23 @@ describe(MediaService.name, () => {
|
||||
|
||||
it('should skip a video without any streams', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.noVideoStreams);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip a video without any height', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.noHeight);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should transcode when set to all', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.multipleVideoStreams);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.ALL }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -320,7 +320,7 @@ describe(MediaService.name, () => {
|
||||
it('should transcode when optimal and too big', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -346,7 +346,7 @@ describe(MediaService.name, () => {
|
||||
{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.ALL },
|
||||
{ key: SystemConfigKey.FFMPEG_TARGET_RESOLUTION, value: 'original' },
|
||||
]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -368,8 +368,8 @@ describe(MediaService.name, () => {
|
||||
it('should transcode with alternate scaling video is vertical', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.videoStreamVertical2160p);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -392,8 +392,8 @@ describe(MediaService.name, () => {
|
||||
it('should transcode when audio doesnt match target', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.audioStreamMp3);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -416,8 +416,8 @@ describe(MediaService.name, () => {
|
||||
it('should transcode when container doesnt match target', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -440,32 +440,32 @@ describe(MediaService.name, () => {
|
||||
it('should not transcode an invalid transcode value', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: 'invalid' }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not transcode if transcoding is disabled', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.DISABLED }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not transcode if target codec is invalid', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: 'invalid' }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set max bitrate if above 0', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '4500k' }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -493,8 +493,8 @@ describe(MediaService.name, () => {
|
||||
{ key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '4500k' },
|
||||
{ key: SystemConfigKey.FFMPEG_TWO_PASS, value: true },
|
||||
]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -519,8 +519,8 @@ describe(MediaService.name, () => {
|
||||
it('should fallback to one pass for h264/h265 if two-pass is enabled but no max bitrate is set', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TWO_PASS, value: true }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -547,8 +547,8 @@ describe(MediaService.name, () => {
|
||||
{ key: SystemConfigKey.FFMPEG_TWO_PASS, value: true },
|
||||
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.VP9 },
|
||||
]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -577,8 +577,8 @@ describe(MediaService.name, () => {
|
||||
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.VP9 },
|
||||
{ key: SystemConfigKey.FFMPEG_PRESET, value: 'slow' },
|
||||
]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -606,8 +606,8 @@ describe(MediaService.name, () => {
|
||||
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.VP9 },
|
||||
{ key: SystemConfigKey.FFMPEG_PRESET, value: 'invalid' },
|
||||
]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -634,8 +634,8 @@ describe(MediaService.name, () => {
|
||||
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.VP9 },
|
||||
{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 },
|
||||
]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -661,8 +661,8 @@ describe(MediaService.name, () => {
|
||||
it('should disable thread pooling for h264 if thread limit is above 0', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -688,8 +688,8 @@ describe(MediaService.name, () => {
|
||||
it('should omit thread flags for h264 if thread limit is at or below 0', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_THREADS, value: 0 }]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -715,8 +715,8 @@ describe(MediaService.name, () => {
|
||||
{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 },
|
||||
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC },
|
||||
]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
@ -745,8 +745,8 @@ describe(MediaService.name, () => {
|
||||
{ key: SystemConfigKey.FFMPEG_THREADS, value: 0 },
|
||||
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC },
|
||||
]);
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/encoded-video/user-id/asset-id.mp4',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { assetEntityStub, newAssetRepositoryMock, newJobRepositoryMock, newStorageRepositoryMock } from '@test';
|
||||
import { assetStub, newAssetRepositoryMock, newJobRepositoryMock, newStorageRepositoryMock } from '@test';
|
||||
import { constants } from 'fs/promises';
|
||||
import { IAssetRepository, WithoutProperty, WithProperty } from '../asset';
|
||||
import { IJobRepository, JobName } from '../job';
|
||||
@ -25,7 +25,7 @@ describe(MetadataService.name, () => {
|
||||
|
||||
describe('handleQueueSidecar', () => {
|
||||
it('should queue assets with sidecar files', async () => {
|
||||
assetMock.getWith.mockResolvedValue({ items: [assetEntityStub.sidecar], hasNextPage: false });
|
||||
assetMock.getWith.mockResolvedValue({ items: [assetStub.sidecar], hasNextPage: false });
|
||||
|
||||
await sut.handleQueueSidecar({ force: true });
|
||||
|
||||
@ -33,12 +33,12 @@ describe(MetadataService.name, () => {
|
||||
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SIDECAR_SYNC,
|
||||
data: { id: assetEntityStub.sidecar.id },
|
||||
data: { id: assetStub.sidecar.id },
|
||||
});
|
||||
});
|
||||
|
||||
it('should queue assets without sidecar files', async () => {
|
||||
assetMock.getWithout.mockResolvedValue({ items: [assetEntityStub.image], hasNextPage: false });
|
||||
assetMock.getWithout.mockResolvedValue({ items: [assetStub.image], hasNextPage: false });
|
||||
|
||||
await sut.handleQueueSidecar({ force: false });
|
||||
|
||||
@ -46,7 +46,7 @@ describe(MetadataService.name, () => {
|
||||
expect(assetMock.getWith).not.toHaveBeenCalled();
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SIDECAR_DISCOVERY,
|
||||
data: { id: assetEntityStub.image.id },
|
||||
data: { id: assetStub.image.id },
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -59,44 +59,44 @@ describe(MetadataService.name, () => {
|
||||
|
||||
describe('handleSidecarDiscovery', () => {
|
||||
it('should skip hidden assets', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.livePhotoMotionAsset]);
|
||||
await sut.handleSidecarDiscovery({ id: assetEntityStub.livePhotoMotionAsset.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.livePhotoMotionAsset]);
|
||||
await sut.handleSidecarDiscovery({ id: assetStub.livePhotoMotionAsset.id });
|
||||
expect(storageMock.checkFileExists).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip assets with a sidecar path', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.sidecar]);
|
||||
await sut.handleSidecarDiscovery({ id: assetEntityStub.sidecar.id });
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.sidecar]);
|
||||
await sut.handleSidecarDiscovery({ id: assetStub.sidecar.id });
|
||||
expect(storageMock.checkFileExists).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should do nothing when a sidecar is not found ', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
storageMock.checkFileExists.mockResolvedValue(false);
|
||||
await sut.handleSidecarDiscovery({ id: assetEntityStub.image.id });
|
||||
await sut.handleSidecarDiscovery({ id: assetStub.image.id });
|
||||
expect(assetMock.save).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should update a image asset when a sidecar is found', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
assetMock.save.mockResolvedValue(assetStub.image);
|
||||
storageMock.checkFileExists.mockResolvedValue(true);
|
||||
await sut.handleSidecarDiscovery({ id: assetEntityStub.image.id });
|
||||
await sut.handleSidecarDiscovery({ id: assetStub.image.id });
|
||||
expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.jpg.xmp', constants.R_OK);
|
||||
expect(assetMock.save).toHaveBeenCalledWith({
|
||||
id: assetEntityStub.image.id,
|
||||
id: assetStub.image.id,
|
||||
sidecarPath: '/original/path.jpg.xmp',
|
||||
});
|
||||
});
|
||||
|
||||
it('should update a video asset when a sidecar is found', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
||||
assetMock.save.mockResolvedValue(assetEntityStub.video);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
assetMock.save.mockResolvedValue(assetStub.video);
|
||||
storageMock.checkFileExists.mockResolvedValue(true);
|
||||
await sut.handleSidecarDiscovery({ id: assetEntityStub.video.id });
|
||||
await sut.handleSidecarDiscovery({ id: assetStub.video.id });
|
||||
expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.R_OK);
|
||||
expect(assetMock.save).toHaveBeenCalledWith({
|
||||
id: assetEntityStub.image.id,
|
||||
id: assetStub.image.id,
|
||||
sidecarPath: '/original/path.ext.xmp',
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import {
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
authStub,
|
||||
faceStub,
|
||||
newJobRepositoryMock,
|
||||
@ -112,7 +112,7 @@ describe(PersonService.name, () => {
|
||||
|
||||
describe('getAssets', () => {
|
||||
it("should return a person's assets", async () => {
|
||||
personMock.getAssets.mockResolvedValue([assetEntityStub.image, assetEntityStub.video]);
|
||||
personMock.getAssets.mockResolvedValue([assetStub.image, assetStub.video]);
|
||||
await sut.getAssets(authStub.admin, 'person-1');
|
||||
expect(personMock.getAssets).toHaveBeenCalledWith('admin_id', 'person-1');
|
||||
});
|
||||
@ -130,7 +130,7 @@ describe(PersonService.name, () => {
|
||||
it("should update a person's name", async () => {
|
||||
personMock.getById.mockResolvedValue(personStub.noName);
|
||||
personMock.update.mockResolvedValue(personStub.withName);
|
||||
personMock.getAssets.mockResolvedValue([assetEntityStub.image]);
|
||||
personMock.getAssets.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await expect(sut.update(authStub.admin, 'person-1', { name: 'Person 1' })).resolves.toEqual(responseDto);
|
||||
|
||||
@ -138,14 +138,14 @@ describe(PersonService.name, () => {
|
||||
expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', name: 'Person 1' });
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SEARCH_INDEX_ASSET,
|
||||
data: { ids: [assetEntityStub.image.id] },
|
||||
data: { ids: [assetStub.image.id] },
|
||||
});
|
||||
});
|
||||
|
||||
it('should update a person visibility', async () => {
|
||||
personMock.getById.mockResolvedValue(personStub.hidden);
|
||||
personMock.update.mockResolvedValue(personStub.withName);
|
||||
personMock.getAssets.mockResolvedValue([assetEntityStub.image]);
|
||||
personMock.getAssets.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await expect(sut.update(authStub.admin, 'person-1', { isHidden: false })).resolves.toEqual(responseDto);
|
||||
|
||||
@ -153,7 +153,7 @@ describe(PersonService.name, () => {
|
||||
expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', isHidden: false });
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SEARCH_INDEX_ASSET,
|
||||
data: { ids: [assetEntityStub.image.id] },
|
||||
data: { ids: [assetStub.image.id] },
|
||||
});
|
||||
});
|
||||
|
||||
@ -239,7 +239,7 @@ describe(PersonService.name, () => {
|
||||
it('should delete conflicting faces before merging', async () => {
|
||||
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
||||
personMock.getById.mockResolvedValue(personStub.mergePerson);
|
||||
personMock.prepareReassignFaces.mockResolvedValue([assetEntityStub.image.id]);
|
||||
personMock.prepareReassignFaces.mockResolvedValue([assetStub.image.id]);
|
||||
|
||||
await expect(sut.mergePerson(authStub.admin, 'person-1', { ids: ['person-2'] })).resolves.toEqual([
|
||||
{ id: 'person-2', success: true },
|
||||
@ -252,7 +252,7 @@ describe(PersonService.name, () => {
|
||||
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SEARCH_REMOVE_FACE,
|
||||
data: { assetId: assetEntityStub.image.id, personId: personStub.mergePerson.id },
|
||||
data: { assetId: assetStub.image.id, personId: personStub.mergePerson.id },
|
||||
});
|
||||
});
|
||||
|
||||
@ -282,7 +282,7 @@ describe(PersonService.name, () => {
|
||||
it('should handle an error reassigning faces', async () => {
|
||||
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
||||
personMock.getById.mockResolvedValue(personStub.mergePerson);
|
||||
personMock.prepareReassignFaces.mockResolvedValue([assetEntityStub.image.id]);
|
||||
personMock.prepareReassignFaces.mockResolvedValue([assetStub.image.id]);
|
||||
personMock.reassignFaces.mockRejectedValue(new Error('update failed'));
|
||||
|
||||
await expect(sut.mergePerson(authStub.admin, 'person-1', { ids: ['person-2'] })).resolves.toEqual([
|
||||
|
@ -2,7 +2,7 @@ import { BadRequestException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import {
|
||||
albumStub,
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
asyncTick,
|
||||
authStub,
|
||||
faceStub,
|
||||
@ -192,14 +192,14 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should index all the assets', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
await sut.handleIndexAssets();
|
||||
|
||||
expect(searchMock.importAssets.mock.calls).toEqual([
|
||||
[[assetEntityStub.image], false],
|
||||
[[assetStub.image], false],
|
||||
[[], true],
|
||||
]);
|
||||
});
|
||||
@ -217,11 +217,11 @@ describe(SearchService.name, () => {
|
||||
describe('handleIndexAsset', () => {
|
||||
it('should skip if search is disabled', () => {
|
||||
const sut = makeSut('false');
|
||||
sut.handleIndexAsset({ ids: [assetEntityStub.image.id] });
|
||||
sut.handleIndexAsset({ ids: [assetStub.image.id] });
|
||||
});
|
||||
|
||||
it('should index the asset', () => {
|
||||
sut.handleIndexAsset({ ids: [assetEntityStub.image.id] });
|
||||
sut.handleIndexAsset({ ids: [assetStub.image.id] });
|
||||
});
|
||||
});
|
||||
|
||||
@ -367,7 +367,7 @@ describe(SearchService.name, () => {
|
||||
});
|
||||
|
||||
it('should flush queued asset updates', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
|
||||
sut.handleIndexAsset({ ids: ['asset1'] });
|
||||
|
||||
@ -376,7 +376,7 @@ describe(SearchService.name, () => {
|
||||
await asyncTick(4);
|
||||
|
||||
expect(assetMock.getByIds).toHaveBeenCalledWith(['asset1']);
|
||||
expect(searchMock.importAssets).toHaveBeenCalledWith([assetEntityStub.image], false);
|
||||
expect(searchMock.importAssets).toHaveBeenCalledWith([assetStub.image], false);
|
||||
});
|
||||
|
||||
it('should flush queued asset deletes', async () => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
|
||||
import {
|
||||
albumStub,
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
authStub,
|
||||
IAccessRepositoryMock,
|
||||
newAccessRepositoryMock,
|
||||
@ -136,20 +136,20 @@ describe(SharedLinkService.name, () => {
|
||||
|
||||
await sut.create(authStub.admin, {
|
||||
type: SharedLinkType.INDIVIDUAL,
|
||||
assetIds: [assetEntityStub.image.id],
|
||||
assetIds: [assetStub.image.id],
|
||||
showExif: true,
|
||||
allowDownload: true,
|
||||
allowUpload: true,
|
||||
});
|
||||
|
||||
expect(accessMock.asset.hasOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetEntityStub.image.id);
|
||||
expect(accessMock.asset.hasOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetStub.image.id);
|
||||
expect(shareMock.create).toHaveBeenCalledWith({
|
||||
type: SharedLinkType.INDIVIDUAL,
|
||||
userId: authStub.admin.id,
|
||||
albumId: null,
|
||||
allowDownload: true,
|
||||
allowUpload: true,
|
||||
assets: [{ id: assetEntityStub.image.id }],
|
||||
assets: [{ id: assetStub.image.id }],
|
||||
description: null,
|
||||
expiresAt: null,
|
||||
showExif: true,
|
||||
@ -211,9 +211,9 @@ describe(SharedLinkService.name, () => {
|
||||
when(accessMock.asset.hasOwnerAccess).calledWith(authStub.admin.id, 'asset-3').mockResolvedValue(true);
|
||||
|
||||
await expect(
|
||||
sut.addAssets(authStub.admin, 'link-1', { assetIds: [assetEntityStub.image.id, 'asset-2', 'asset-3'] }),
|
||||
sut.addAssets(authStub.admin, 'link-1', { assetIds: [assetStub.image.id, 'asset-2', 'asset-3'] }),
|
||||
).resolves.toEqual([
|
||||
{ assetId: assetEntityStub.image.id, success: false, error: AssetIdErrorReason.DUPLICATE },
|
||||
{ assetId: assetStub.image.id, success: false, error: AssetIdErrorReason.DUPLICATE },
|
||||
{ assetId: 'asset-2', success: false, error: AssetIdErrorReason.NO_PERMISSION },
|
||||
{ assetId: 'asset-3', success: true },
|
||||
]);
|
||||
@ -221,7 +221,7 @@ describe(SharedLinkService.name, () => {
|
||||
expect(accessMock.asset.hasOwnerAccess).toHaveBeenCalledTimes(2);
|
||||
expect(shareMock.update).toHaveBeenCalledWith({
|
||||
...sharedLinkStub.individual,
|
||||
assets: [assetEntityStub.image, { id: 'asset-3' }],
|
||||
assets: [assetStub.image, { id: 'asset-3' }],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -239,9 +239,9 @@ describe(SharedLinkService.name, () => {
|
||||
shareMock.create.mockResolvedValue(sharedLinkStub.individual);
|
||||
|
||||
await expect(
|
||||
sut.removeAssets(authStub.admin, 'link-1', { assetIds: [assetEntityStub.image.id, 'asset-2'] }),
|
||||
sut.removeAssets(authStub.admin, 'link-1', { assetIds: [assetStub.image.id, 'asset-2'] }),
|
||||
).resolves.toEqual([
|
||||
{ assetId: assetEntityStub.image.id, success: true },
|
||||
{ assetId: assetStub.image.id, success: true },
|
||||
{ assetId: 'asset-2', success: false, error: AssetIdErrorReason.NOT_FOUND },
|
||||
]);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { AssetEntity } from '@app/infra/entities';
|
||||
import {
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
newAssetRepositoryMock,
|
||||
newJobRepositoryMock,
|
||||
newMachineLearningRepositoryMock,
|
||||
@ -41,29 +41,25 @@ describe(SmartInfoService.name, () => {
|
||||
describe('handleQueueObjectTagging', () => {
|
||||
it('should queue the assets without tags', async () => {
|
||||
assetMock.getWithout.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
await sut.handleQueueObjectTagging({ force: false });
|
||||
|
||||
expect(jobMock.queue.mock.calls).toEqual([
|
||||
[{ name: JobName.CLASSIFY_IMAGE, data: { id: assetEntityStub.image.id } }],
|
||||
]);
|
||||
expect(jobMock.queue.mock.calls).toEqual([[{ name: JobName.CLASSIFY_IMAGE, data: { id: assetStub.image.id } }]]);
|
||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.OBJECT_TAGS);
|
||||
});
|
||||
|
||||
it('should queue all the assets', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
await sut.handleQueueObjectTagging({ force: true });
|
||||
|
||||
expect(jobMock.queue.mock.calls).toEqual([
|
||||
[{ name: JobName.CLASSIFY_IMAGE, data: { id: assetEntityStub.image.id } }],
|
||||
]);
|
||||
expect(jobMock.queue.mock.calls).toEqual([[{ name: JobName.CLASSIFY_IMAGE, data: { id: assetStub.image.id } }]]);
|
||||
expect(assetMock.getAll).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -104,25 +100,25 @@ describe(SmartInfoService.name, () => {
|
||||
describe('handleQueueEncodeClip', () => {
|
||||
it('should queue the assets without clip embeddings', async () => {
|
||||
assetMock.getWithout.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
await sut.handleQueueEncodeClip({ force: false });
|
||||
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.ENCODE_CLIP, data: { id: assetEntityStub.image.id } });
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.ENCODE_CLIP, data: { id: assetStub.image.id } });
|
||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.CLIP_ENCODING);
|
||||
});
|
||||
|
||||
it('should queue all the assets', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
|
||||
await sut.handleQueueEncodeClip({ force: true });
|
||||
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.ENCODE_CLIP, data: { id: assetEntityStub.image.id } });
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.ENCODE_CLIP, data: { id: assetStub.image.id } });
|
||||
expect(assetMock.getAll).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
newAssetRepositoryMock,
|
||||
newStorageRepositoryMock,
|
||||
newSystemConfigRepositoryMock,
|
||||
newUserRepositoryMock,
|
||||
userEntityStub,
|
||||
userStub,
|
||||
} from '@test';
|
||||
import { when } from 'jest-when';
|
||||
import { StorageTemplateService } from '.';
|
||||
@ -49,11 +49,11 @@ describe(StorageTemplateService.name, () => {
|
||||
|
||||
it('should handle an asset with a duplicate destination', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
||||
assetMock.save.mockResolvedValue(assetStub.image);
|
||||
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||
|
||||
when(storageMock.checkFileExists)
|
||||
.calledWith('upload/library/user-id/2023/2023-02-23/asset-id.jpg')
|
||||
@ -68,7 +68,7 @@ describe(StorageTemplateService.name, () => {
|
||||
expect(assetMock.getAll).toHaveBeenCalled();
|
||||
expect(storageMock.checkFileExists).toHaveBeenCalledTimes(2);
|
||||
expect(assetMock.save).toHaveBeenCalledWith({
|
||||
id: assetEntityStub.image.id,
|
||||
id: assetStub.image.id,
|
||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
||||
});
|
||||
expect(userMock.getList).toHaveBeenCalled();
|
||||
@ -78,13 +78,13 @@ describe(StorageTemplateService.name, () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
...assetEntityStub.image,
|
||||
...assetStub.image,
|
||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
||||
},
|
||||
],
|
||||
hasNextPage: false,
|
||||
});
|
||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
||||
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||
|
||||
await sut.handleMigration();
|
||||
|
||||
@ -98,13 +98,13 @@ describe(StorageTemplateService.name, () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
...assetEntityStub.image,
|
||||
...assetStub.image,
|
||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
||||
},
|
||||
],
|
||||
hasNextPage: false,
|
||||
});
|
||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
||||
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||
|
||||
await sut.handleMigration();
|
||||
|
||||
@ -116,11 +116,11 @@ describe(StorageTemplateService.name, () => {
|
||||
|
||||
it('should move an asset', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
||||
assetMock.save.mockResolvedValue(assetStub.image);
|
||||
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||
|
||||
await sut.handleMigration();
|
||||
|
||||
@ -130,18 +130,18 @@ describe(StorageTemplateService.name, () => {
|
||||
'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
||||
);
|
||||
expect(assetMock.save).toHaveBeenCalledWith({
|
||||
id: assetEntityStub.image.id,
|
||||
id: assetStub.image.id,
|
||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the user storage label', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
||||
userMock.getList.mockResolvedValue([userEntityStub.storageLabel]);
|
||||
assetMock.save.mockResolvedValue(assetStub.image);
|
||||
userMock.getList.mockResolvedValue([userStub.storageLabel]);
|
||||
|
||||
await sut.handleMigration();
|
||||
|
||||
@ -151,18 +151,18 @@ describe(StorageTemplateService.name, () => {
|
||||
'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
|
||||
);
|
||||
expect(assetMock.save).toHaveBeenCalledWith({
|
||||
id: assetEntityStub.image.id,
|
||||
id: assetStub.image.id,
|
||||
originalPath: 'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not update the database if the move fails', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
storageMock.moveFile.mockRejectedValue(new Error('Read only system'));
|
||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
||||
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||
|
||||
await sut.handleMigration();
|
||||
|
||||
@ -176,17 +176,17 @@ describe(StorageTemplateService.name, () => {
|
||||
|
||||
it('should move the asset back if the database fails', async () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [assetEntityStub.image],
|
||||
items: [assetStub.image],
|
||||
hasNextPage: false,
|
||||
});
|
||||
assetMock.save.mockRejectedValue('Connection Error!');
|
||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
||||
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||
|
||||
await sut.handleMigration();
|
||||
|
||||
expect(assetMock.getAll).toHaveBeenCalled();
|
||||
expect(assetMock.save).toHaveBeenCalledWith({
|
||||
id: assetEntityStub.image.id,
|
||||
id: assetStub.image.id,
|
||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
||||
});
|
||||
expect(storageMock.moveFile.mock.calls).toEqual([
|
||||
@ -199,15 +199,15 @@ describe(StorageTemplateService.name, () => {
|
||||
assetMock.getAll.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
...assetEntityStub.image,
|
||||
...assetStub.image,
|
||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
||||
isReadOnly: true,
|
||||
},
|
||||
],
|
||||
hasNextPage: false,
|
||||
});
|
||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
||||
assetMock.save.mockResolvedValue(assetStub.image);
|
||||
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||
|
||||
await sut.handleMigration();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { TagType } from '@app/infra/entities';
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { assetEntityStub, authStub, newTagRepositoryMock, tagResponseStub, tagStub } from '@test';
|
||||
import { assetStub, authStub, newTagRepositoryMock, tagResponseStub, tagStub } from '@test';
|
||||
import { when } from 'jest-when';
|
||||
import { AssetIdErrorReason } from '../asset';
|
||||
import { ITagRepository } from './tag.repository';
|
||||
@ -107,7 +107,7 @@ describe(TagService.name, () => {
|
||||
|
||||
it('should get the assets for a tag', async () => {
|
||||
tagMock.getById.mockResolvedValue(tagStub.tag1);
|
||||
tagMock.getAssets.mockResolvedValue([assetEntityStub.image]);
|
||||
tagMock.getAssets.mockResolvedValue([assetStub.image]);
|
||||
await sut.getAssets(authStub.admin, 'tag-1');
|
||||
expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1');
|
||||
expect(tagMock.getAssets).toHaveBeenCalledWith(authStub.admin.id, 'tag-1');
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { AlbumResponseDto, AuthUserDto, mapUser } from '@app/domain';
|
||||
import { AlbumEntity, UserEntity } from '@app/infra/entities';
|
||||
import { ForbiddenException, NotFoundException } from '@nestjs/common';
|
||||
import { userEntityStub } from '@test';
|
||||
import { userStub } from '@test';
|
||||
import { IAlbumRepository } from './album-repository';
|
||||
import { AlbumService } from './album.service';
|
||||
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
||||
@ -61,11 +61,11 @@ describe('Album service', () => {
|
||||
albumEntity.albumThumbnailAssetId = null;
|
||||
albumEntity.sharedUsers = [
|
||||
{
|
||||
...userEntityStub.user1,
|
||||
...userStub.user1,
|
||||
id: authUser.id,
|
||||
},
|
||||
{
|
||||
...userEntityStub.user1,
|
||||
...userStub.user1,
|
||||
id: sharedAlbumSharedAlsoWithId,
|
||||
},
|
||||
];
|
||||
|
@ -2,7 +2,7 @@ import { ICryptoRepository, IJobRepository, IStorageRepository, JobName } from '
|
||||
import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import {
|
||||
assetEntityStub,
|
||||
assetStub,
|
||||
authStub,
|
||||
fileStub,
|
||||
IAccessRepositoryMock,
|
||||
@ -132,11 +132,11 @@ describe('AssetService', () => {
|
||||
sut = new AssetService(accessMock, assetRepositoryMock, a, cryptoMock, jobMock, storageMock);
|
||||
|
||||
when(assetRepositoryMock.get)
|
||||
.calledWith(assetEntityStub.livePhotoStillAsset.id)
|
||||
.mockResolvedValue(assetEntityStub.livePhotoStillAsset);
|
||||
.calledWith(assetStub.livePhotoStillAsset.id)
|
||||
.mockResolvedValue(assetStub.livePhotoStillAsset);
|
||||
when(assetRepositoryMock.get)
|
||||
.calledWith(assetEntityStub.livePhotoMotionAsset.id)
|
||||
.mockResolvedValue(assetEntityStub.livePhotoMotionAsset);
|
||||
.calledWith(assetStub.livePhotoMotionAsset.id)
|
||||
.mockResolvedValue(assetStub.livePhotoMotionAsset);
|
||||
});
|
||||
|
||||
describe('uploadFile', () => {
|
||||
@ -185,8 +185,8 @@ describe('AssetService', () => {
|
||||
const error = new QueryFailedError('', [], '');
|
||||
(error as any).constraint = 'UQ_userid_checksum';
|
||||
|
||||
assetRepositoryMock.create.mockResolvedValueOnce(assetEntityStub.livePhotoMotionAsset);
|
||||
assetRepositoryMock.create.mockResolvedValueOnce(assetEntityStub.livePhotoStillAsset);
|
||||
assetRepositoryMock.create.mockResolvedValueOnce(assetStub.livePhotoMotionAsset);
|
||||
assetRepositoryMock.create.mockResolvedValueOnce(assetStub.livePhotoStillAsset);
|
||||
|
||||
await expect(
|
||||
sut.uploadFile(authStub.user1, dto, fileStub.livePhotoStill, fileStub.livePhotoMotion),
|
||||
@ -199,10 +199,10 @@ describe('AssetService', () => {
|
||||
[
|
||||
{
|
||||
name: JobName.METADATA_EXTRACTION,
|
||||
data: { id: assetEntityStub.livePhotoMotionAsset.id, source: 'upload' },
|
||||
data: { id: assetStub.livePhotoMotionAsset.id, source: 'upload' },
|
||||
},
|
||||
],
|
||||
[{ name: JobName.METADATA_EXTRACTION, data: { id: assetEntityStub.livePhotoStillAsset.id, source: 'upload' } }],
|
||||
[{ name: JobName.METADATA_EXTRACTION, data: { id: assetStub.livePhotoStillAsset.id, source: 'upload' } }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -263,9 +263,9 @@ describe('AssetService', () => {
|
||||
it('should delete a live photo', async () => {
|
||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||
|
||||
await expect(sut.deleteAll(authStub.user1, { ids: [assetEntityStub.livePhotoStillAsset.id] })).resolves.toEqual([
|
||||
{ id: assetEntityStub.livePhotoStillAsset.id, status: 'SUCCESS' },
|
||||
{ id: assetEntityStub.livePhotoMotionAsset.id, status: 'SUCCESS' },
|
||||
await expect(sut.deleteAll(authStub.user1, { ids: [assetStub.livePhotoStillAsset.id] })).resolves.toEqual([
|
||||
{ id: assetStub.livePhotoStillAsset.id, status: 'SUCCESS' },
|
||||
{ id: assetStub.livePhotoMotionAsset.id, status: 'SUCCESS' },
|
||||
]);
|
||||
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
@ -373,7 +373,7 @@ describe('AssetService', () => {
|
||||
|
||||
describe('importFile', () => {
|
||||
it('should handle a file import', async () => {
|
||||
assetRepositoryMock.create.mockResolvedValue(assetEntityStub.image);
|
||||
assetRepositoryMock.create.mockResolvedValue(assetStub.image);
|
||||
storageMock.checkFileExists.mockResolvedValue(true);
|
||||
|
||||
await expect(
|
||||
@ -392,7 +392,7 @@ describe('AssetService', () => {
|
||||
(error as any).constraint = 'UQ_userid_checksum';
|
||||
|
||||
assetRepositoryMock.create.mockRejectedValue(error);
|
||||
assetRepositoryMock.getAssetsByChecksums.mockResolvedValue([assetEntityStub.image]);
|
||||
assetRepositoryMock.getAssetsByChecksums.mockResolvedValue([assetStub.image]);
|
||||
storageMock.checkFileExists.mockResolvedValue(true);
|
||||
cryptoMock.hashFile.mockResolvedValue(Buffer.from('file hash', 'utf8'));
|
||||
|
||||
@ -411,36 +411,36 @@ describe('AssetService', () => {
|
||||
describe('getAssetById', () => {
|
||||
it('should allow owner access', async () => {
|
||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||
assetRepositoryMock.getById.mockResolvedValue(assetEntityStub.image);
|
||||
await sut.getAssetById(authStub.admin, assetEntityStub.image.id);
|
||||
expect(accessMock.asset.hasOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetEntityStub.image.id);
|
||||
assetRepositoryMock.getById.mockResolvedValue(assetStub.image);
|
||||
await sut.getAssetById(authStub.admin, assetStub.image.id);
|
||||
expect(accessMock.asset.hasOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetStub.image.id);
|
||||
});
|
||||
|
||||
it('should allow shared link access', async () => {
|
||||
accessMock.asset.hasSharedLinkAccess.mockResolvedValue(true);
|
||||
assetRepositoryMock.getById.mockResolvedValue(assetEntityStub.image);
|
||||
await sut.getAssetById(authStub.adminSharedLink, assetEntityStub.image.id);
|
||||
assetRepositoryMock.getById.mockResolvedValue(assetStub.image);
|
||||
await sut.getAssetById(authStub.adminSharedLink, assetStub.image.id);
|
||||
expect(accessMock.asset.hasSharedLinkAccess).toHaveBeenCalledWith(
|
||||
authStub.adminSharedLink.sharedLinkId,
|
||||
assetEntityStub.image.id,
|
||||
assetStub.image.id,
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow partner sharing access', async () => {
|
||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(false);
|
||||
accessMock.asset.hasPartnerAccess.mockResolvedValue(true);
|
||||
assetRepositoryMock.getById.mockResolvedValue(assetEntityStub.image);
|
||||
await sut.getAssetById(authStub.admin, assetEntityStub.image.id);
|
||||
expect(accessMock.asset.hasPartnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetEntityStub.image.id);
|
||||
assetRepositoryMock.getById.mockResolvedValue(assetStub.image);
|
||||
await sut.getAssetById(authStub.admin, assetStub.image.id);
|
||||
expect(accessMock.asset.hasPartnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetStub.image.id);
|
||||
});
|
||||
|
||||
it('should allow shared album access', async () => {
|
||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(false);
|
||||
accessMock.asset.hasPartnerAccess.mockResolvedValue(false);
|
||||
accessMock.asset.hasAlbumAccess.mockResolvedValue(true);
|
||||
assetRepositoryMock.getById.mockResolvedValue(assetEntityStub.image);
|
||||
await sut.getAssetById(authStub.admin, assetEntityStub.image.id);
|
||||
expect(accessMock.asset.hasAlbumAccess).toHaveBeenCalledWith(authStub.admin.id, assetEntityStub.image.id);
|
||||
assetRepositoryMock.getById.mockResolvedValue(assetStub.image);
|
||||
await sut.getAssetById(authStub.admin, assetStub.image.id);
|
||||
expect(accessMock.asset.hasAlbumAccess).toHaveBeenCalledWith(authStub.admin.id, assetStub.image.id);
|
||||
});
|
||||
|
||||
it('should throw an error for no access', async () => {
|
||||
@ -448,15 +448,13 @@ describe('AssetService', () => {
|
||||
accessMock.asset.hasPartnerAccess.mockResolvedValue(false);
|
||||
accessMock.asset.hasSharedLinkAccess.mockResolvedValue(false);
|
||||
accessMock.asset.hasAlbumAccess.mockResolvedValue(false);
|
||||
await expect(sut.getAssetById(authStub.admin, assetEntityStub.image.id)).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
await expect(sut.getAssetById(authStub.admin, assetStub.image.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
expect(assetRepositoryMock.getById).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should throw an error for an invalid shared link', async () => {
|
||||
accessMock.asset.hasSharedLinkAccess.mockResolvedValue(false);
|
||||
await expect(sut.getAssetById(authStub.adminSharedLink, assetEntityStub.image.id)).rejects.toBeInstanceOf(
|
||||
await expect(sut.getAssetById(authStub.adminSharedLink, assetStub.image.id)).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
expect(accessMock.asset.hasOwnerAccess).not.toHaveBeenCalled();
|
||||
|
File diff suppressed because it is too large
Load Diff
124
server/test/fixtures/album.stub.ts
vendored
Normal file
124
server/test/fixtures/album.stub.ts
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
import { AlbumEntity } from '@app/infra/entities';
|
||||
import { assetStub } from './asset.stub';
|
||||
import { authStub } from './auth.stub';
|
||||
import { userStub } from './user.stub';
|
||||
|
||||
export const albumStub = {
|
||||
empty: Object.freeze<AlbumEntity>({
|
||||
id: 'album-1',
|
||||
albumName: 'Empty album',
|
||||
ownerId: authStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
assets: [],
|
||||
albumThumbnailAsset: null,
|
||||
albumThumbnailAssetId: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
}),
|
||||
sharedWithUser: Object.freeze<AlbumEntity>({
|
||||
id: 'album-2',
|
||||
albumName: 'Empty album shared with user',
|
||||
ownerId: authStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
assets: [],
|
||||
albumThumbnailAsset: null,
|
||||
albumThumbnailAssetId: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [userStub.user1],
|
||||
}),
|
||||
sharedWithMultiple: Object.freeze<AlbumEntity>({
|
||||
id: 'album-3',
|
||||
albumName: 'Empty album shared with users',
|
||||
ownerId: authStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
assets: [],
|
||||
albumThumbnailAsset: null,
|
||||
albumThumbnailAssetId: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [userStub.user1, userStub.user2],
|
||||
}),
|
||||
sharedWithAdmin: Object.freeze<AlbumEntity>({
|
||||
id: 'album-3',
|
||||
albumName: 'Empty album shared with admin',
|
||||
ownerId: authStub.user1.id,
|
||||
owner: userStub.user1,
|
||||
assets: [],
|
||||
albumThumbnailAsset: null,
|
||||
albumThumbnailAssetId: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [userStub.admin],
|
||||
}),
|
||||
oneAsset: Object.freeze<AlbumEntity>({
|
||||
id: 'album-4',
|
||||
albumName: 'Album with one asset',
|
||||
ownerId: authStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
assets: [assetStub.image],
|
||||
albumThumbnailAsset: null,
|
||||
albumThumbnailAssetId: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
}),
|
||||
emptyWithInvalidThumbnail: Object.freeze<AlbumEntity>({
|
||||
id: 'album-5',
|
||||
albumName: 'Empty album with invalid thumbnail',
|
||||
ownerId: authStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
assets: [],
|
||||
albumThumbnailAsset: assetStub.image,
|
||||
albumThumbnailAssetId: assetStub.image.id,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
}),
|
||||
emptyWithValidThumbnail: Object.freeze<AlbumEntity>({
|
||||
id: 'album-5',
|
||||
albumName: 'Empty album with invalid thumbnail',
|
||||
ownerId: authStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
assets: [],
|
||||
albumThumbnailAsset: null,
|
||||
albumThumbnailAssetId: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
}),
|
||||
oneAssetInvalidThumbnail: Object.freeze<AlbumEntity>({
|
||||
id: 'album-6',
|
||||
albumName: 'Album with one asset and invalid thumbnail',
|
||||
ownerId: authStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
assets: [assetStub.image],
|
||||
albumThumbnailAsset: assetStub.livePhotoMotionAsset,
|
||||
albumThumbnailAssetId: assetStub.livePhotoMotionAsset.id,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
}),
|
||||
oneAssetValidThumbnail: Object.freeze<AlbumEntity>({
|
||||
id: 'album-6',
|
||||
albumName: 'Album with one asset and invalid thumbnail',
|
||||
ownerId: authStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
assets: [assetStub.image],
|
||||
albumThumbnailAsset: assetStub.image,
|
||||
albumThumbnailAssetId: assetStub.image.id,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
}),
|
||||
};
|
13
server/test/fixtures/api-key.stub.ts
vendored
Normal file
13
server/test/fixtures/api-key.stub.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
import { APIKeyEntity } from '@app/infra/entities';
|
||||
import { authStub } from './auth.stub';
|
||||
import { userStub } from './user.stub';
|
||||
|
||||
export const keyStub = {
|
||||
admin: Object.freeze({
|
||||
id: 'my-random-guid',
|
||||
name: 'My Key',
|
||||
key: 'my-api-key (hashed)',
|
||||
userId: authStub.admin.id,
|
||||
user: userStub.admin,
|
||||
} as APIKeyEntity),
|
||||
};
|
291
server/test/fixtures/asset.stub.ts
vendored
Normal file
291
server/test/fixtures/asset.stub.ts
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
|
||||
import { authStub } from './auth.stub';
|
||||
import { fileStub } from './file.stub';
|
||||
import { userStub } from './user.stub';
|
||||
|
||||
export const assetStub = {
|
||||
noResizePath: Object.freeze<AssetEntity>({
|
||||
id: 'asset-id',
|
||||
originalFileName: 'IMG_123',
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: 'upload/library/IMG_123.jpg',
|
||||
resizePath: null,
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
isFavorite: true,
|
||||
isArchived: false,
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
faces: [],
|
||||
sidecarPath: null,
|
||||
isReadOnly: false,
|
||||
}),
|
||||
noWebpPath: Object.freeze<AssetEntity>({
|
||||
id: 'asset-id',
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: 'upload/library/IMG_456.jpg',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
webpPath: null,
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
isFavorite: true,
|
||||
isArchived: false,
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
originalFileName: 'IMG_456',
|
||||
faces: [],
|
||||
sidecarPath: null,
|
||||
isReadOnly: false,
|
||||
exifInfo: {
|
||||
fileSizeInByte: 123_000,
|
||||
} as ExifEntity,
|
||||
}),
|
||||
noThumbhash: Object.freeze<AssetEntity>({
|
||||
id: 'asset-id',
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
isFavorite: true,
|
||||
isArchived: false,
|
||||
isReadOnly: false,
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
originalFileName: 'asset-id.ext',
|
||||
faces: [],
|
||||
sidecarPath: null,
|
||||
}),
|
||||
image: Object.freeze<AssetEntity>({
|
||||
id: 'asset-id',
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.jpg',
|
||||
resizePath: '/uploads/user-id/thumbs/path.jpg',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
isFavorite: true,
|
||||
isArchived: false,
|
||||
isReadOnly: false,
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
originalFileName: 'asset-id.jpg',
|
||||
faces: [],
|
||||
sidecarPath: null,
|
||||
exifInfo: {
|
||||
fileSizeInByte: 5_000,
|
||||
} as ExifEntity,
|
||||
}),
|
||||
image1: Object.freeze<AssetEntity>({
|
||||
id: 'asset-id-1',
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
isFavorite: true,
|
||||
isArchived: false,
|
||||
isReadOnly: false,
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
originalFileName: 'asset-id.ext',
|
||||
faces: [],
|
||||
sidecarPath: null,
|
||||
exifInfo: {
|
||||
fileSizeInByte: 5_000,
|
||||
} as ExifEntity,
|
||||
}),
|
||||
video: Object.freeze<AssetEntity>({
|
||||
id: 'asset-id',
|
||||
originalFileName: 'asset-id.ext',
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.VIDEO,
|
||||
webpPath: null,
|
||||
thumbhash: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
isFavorite: true,
|
||||
isArchived: false,
|
||||
isReadOnly: false,
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
faces: [],
|
||||
sidecarPath: null,
|
||||
exifInfo: {
|
||||
fileSizeInByte: 100_000,
|
||||
} as ExifEntity,
|
||||
}),
|
||||
livePhotoMotionAsset: Object.freeze({
|
||||
id: 'live-photo-motion-asset',
|
||||
originalPath: fileStub.livePhotoMotion.originalPath,
|
||||
ownerId: authStub.user1.id,
|
||||
type: AssetType.VIDEO,
|
||||
isVisible: false,
|
||||
fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'),
|
||||
fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'),
|
||||
exifInfo: {
|
||||
fileSizeInByte: 100_000,
|
||||
},
|
||||
} as AssetEntity),
|
||||
|
||||
livePhotoStillAsset: Object.freeze({
|
||||
id: 'live-photo-still-asset',
|
||||
originalPath: fileStub.livePhotoStill.originalPath,
|
||||
ownerId: authStub.user1.id,
|
||||
type: AssetType.IMAGE,
|
||||
livePhotoVideoId: 'live-photo-motion-asset',
|
||||
isVisible: true,
|
||||
fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'),
|
||||
fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'),
|
||||
exifInfo: {
|
||||
fileSizeInByte: 25_000,
|
||||
},
|
||||
} as AssetEntity),
|
||||
|
||||
withLocation: Object.freeze<AssetEntity>({
|
||||
id: 'asset-with-favorite-id',
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
originalPath: '/original/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
sidecarPath: null,
|
||||
type: AssetType.IMAGE,
|
||||
webpPath: null,
|
||||
thumbhash: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
isFavorite: false,
|
||||
isArchived: false,
|
||||
isReadOnly: false,
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
originalFileName: 'asset-id.ext',
|
||||
faces: [],
|
||||
exifInfo: {
|
||||
latitude: 100,
|
||||
longitude: 100,
|
||||
fileSizeInByte: 23_456,
|
||||
} as ExifEntity,
|
||||
}),
|
||||
sidecar: Object.freeze<AssetEntity>({
|
||||
id: 'asset-id',
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
thumbhash: null,
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
webpPath: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
isFavorite: true,
|
||||
isArchived: false,
|
||||
isReadOnly: false,
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
originalFileName: 'asset-id.ext',
|
||||
faces: [],
|
||||
sidecarPath: '/original/path.ext.xmp',
|
||||
}),
|
||||
};
|
127
server/test/fixtures/auth.stub.ts
vendored
Normal file
127
server/test/fixtures/auth.stub.ts
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
import { AuthUserDto } from '@app/domain';
|
||||
|
||||
export const authStub = {
|
||||
admin: Object.freeze<AuthUserDto>({
|
||||
id: 'admin_id',
|
||||
email: 'admin@test.com',
|
||||
isAdmin: true,
|
||||
isPublicUser: false,
|
||||
isAllowUpload: true,
|
||||
externalPath: null,
|
||||
}),
|
||||
user1: Object.freeze<AuthUserDto>({
|
||||
id: 'user-id',
|
||||
email: 'immich@test.com',
|
||||
isAdmin: false,
|
||||
isPublicUser: false,
|
||||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isShowExif: true,
|
||||
accessTokenId: 'token-id',
|
||||
externalPath: null,
|
||||
}),
|
||||
user2: Object.freeze<AuthUserDto>({
|
||||
id: 'user-2',
|
||||
email: 'user2@immich.app',
|
||||
isAdmin: false,
|
||||
isPublicUser: false,
|
||||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isShowExif: true,
|
||||
accessTokenId: 'token-id',
|
||||
externalPath: null,
|
||||
}),
|
||||
external1: Object.freeze<AuthUserDto>({
|
||||
id: 'user-id',
|
||||
email: 'immich@test.com',
|
||||
isAdmin: false,
|
||||
isPublicUser: false,
|
||||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isShowExif: true,
|
||||
accessTokenId: 'token-id',
|
||||
externalPath: '/data/user1',
|
||||
}),
|
||||
adminSharedLink: Object.freeze<AuthUserDto>({
|
||||
id: 'admin_id',
|
||||
email: 'admin@test.com',
|
||||
isAdmin: true,
|
||||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isPublicUser: true,
|
||||
isShowExif: true,
|
||||
sharedLinkId: '123',
|
||||
}),
|
||||
adminSharedLinkNoExif: Object.freeze<AuthUserDto>({
|
||||
id: 'admin_id',
|
||||
email: 'admin@test.com',
|
||||
isAdmin: true,
|
||||
isAllowUpload: true,
|
||||
isAllowDownload: true,
|
||||
isPublicUser: true,
|
||||
isShowExif: false,
|
||||
sharedLinkId: '123',
|
||||
}),
|
||||
readonlySharedLink: Object.freeze<AuthUserDto>({
|
||||
id: 'admin_id',
|
||||
email: 'admin@test.com',
|
||||
isAdmin: true,
|
||||
isAllowUpload: false,
|
||||
isAllowDownload: false,
|
||||
isPublicUser: true,
|
||||
isShowExif: true,
|
||||
sharedLinkId: '123',
|
||||
accessTokenId: 'token-id',
|
||||
}),
|
||||
};
|
||||
|
||||
export const loginResponseStub = {
|
||||
user1oauth: {
|
||||
response: {
|
||||
accessToken: 'cmFuZG9tLWJ5dGVz',
|
||||
userId: 'user-id',
|
||||
userEmail: 'immich@test.com',
|
||||
firstName: 'immich_first_name',
|
||||
lastName: 'immich_last_name',
|
||||
profileImagePath: '',
|
||||
isAdmin: false,
|
||||
shouldChangePassword: false,
|
||||
},
|
||||
cookie: [
|
||||
'immich_access_token=cmFuZG9tLWJ5dGVz; HttpOnly; Secure; Path=/; Max-Age=34560000; SameSite=Lax;',
|
||||
'immich_auth_type=oauth; HttpOnly; Secure; Path=/; Max-Age=34560000; SameSite=Lax;',
|
||||
],
|
||||
},
|
||||
user1password: {
|
||||
response: {
|
||||
accessToken: 'cmFuZG9tLWJ5dGVz',
|
||||
userId: 'user-id',
|
||||
userEmail: 'immich@test.com',
|
||||
firstName: 'immich_first_name',
|
||||
lastName: 'immich_last_name',
|
||||
profileImagePath: '',
|
||||
isAdmin: false,
|
||||
shouldChangePassword: false,
|
||||
},
|
||||
cookie: [
|
||||
'immich_access_token=cmFuZG9tLWJ5dGVz; HttpOnly; Secure; Path=/; Max-Age=34560000; SameSite=Lax;',
|
||||
'immich_auth_type=password; HttpOnly; Secure; Path=/; Max-Age=34560000; SameSite=Lax;',
|
||||
],
|
||||
},
|
||||
user1insecure: {
|
||||
response: {
|
||||
accessToken: 'cmFuZG9tLWJ5dGVz',
|
||||
userId: 'user-id',
|
||||
userEmail: 'immich@test.com',
|
||||
firstName: 'immich_first_name',
|
||||
lastName: 'immich_last_name',
|
||||
profileImagePath: '',
|
||||
isAdmin: false,
|
||||
shouldChangePassword: false,
|
||||
},
|
||||
cookie: [
|
||||
'immich_access_token=cmFuZG9tLWJ5dGVz; HttpOnly; Path=/; Max-Age=34560000; SameSite=Lax;',
|
||||
'immich_auth_type=password; HttpOnly; Path=/; Max-Age=34560000; SameSite=Lax;',
|
||||
],
|
||||
},
|
||||
};
|
58
server/test/fixtures/face.stub.ts
vendored
Normal file
58
server/test/fixtures/face.stub.ts
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
import { AssetFaceEntity } from '@app/infra/entities';
|
||||
import { assetStub } from './asset.stub';
|
||||
import { personStub } from './person.stub';
|
||||
|
||||
export const faceStub = {
|
||||
face1: Object.freeze<AssetFaceEntity>({
|
||||
assetId: assetStub.image.id,
|
||||
asset: assetStub.image,
|
||||
personId: personStub.withName.id,
|
||||
person: personStub.withName,
|
||||
embedding: [1, 2, 3, 4],
|
||||
boundingBoxX1: 0,
|
||||
boundingBoxY1: 0,
|
||||
boundingBoxX2: 1,
|
||||
boundingBoxY2: 1,
|
||||
imageHeight: 1024,
|
||||
imageWidth: 1024,
|
||||
}),
|
||||
primaryFace1: Object.freeze<AssetFaceEntity>({
|
||||
assetId: assetStub.image.id,
|
||||
asset: assetStub.image,
|
||||
personId: personStub.primaryPerson.id,
|
||||
person: personStub.primaryPerson,
|
||||
embedding: [1, 2, 3, 4],
|
||||
boundingBoxX1: 0,
|
||||
boundingBoxY1: 0,
|
||||
boundingBoxX2: 1,
|
||||
boundingBoxY2: 1,
|
||||
imageHeight: 1024,
|
||||
imageWidth: 1024,
|
||||
}),
|
||||
mergeFace1: Object.freeze<AssetFaceEntity>({
|
||||
assetId: assetStub.image.id,
|
||||
asset: assetStub.image,
|
||||
personId: personStub.mergePerson.id,
|
||||
person: personStub.mergePerson,
|
||||
embedding: [1, 2, 3, 4],
|
||||
boundingBoxX1: 0,
|
||||
boundingBoxY1: 0,
|
||||
boundingBoxX2: 1,
|
||||
boundingBoxY2: 1,
|
||||
imageHeight: 1024,
|
||||
imageWidth: 1024,
|
||||
}),
|
||||
mergeFace2: Object.freeze<AssetFaceEntity>({
|
||||
assetId: assetStub.image1.id,
|
||||
asset: assetStub.image1,
|
||||
personId: personStub.mergePerson.id,
|
||||
person: personStub.mergePerson,
|
||||
embedding: [1, 2, 3, 4],
|
||||
boundingBoxX1: 0,
|
||||
boundingBoxY1: 0,
|
||||
boundingBoxX2: 1,
|
||||
boundingBoxY2: 1,
|
||||
imageHeight: 1024,
|
||||
imageWidth: 1024,
|
||||
}),
|
||||
};
|
12
server/test/fixtures/file.stub.ts
vendored
Normal file
12
server/test/fixtures/file.stub.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
export const fileStub = {
|
||||
livePhotoStill: Object.freeze({
|
||||
originalPath: 'fake_path/asset_1.jpeg',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
originalName: 'asset_1.jpeg',
|
||||
}),
|
||||
livePhotoMotion: Object.freeze({
|
||||
originalPath: 'fake_path/asset_1.mp4',
|
||||
checksum: Buffer.from('live photo file hash', 'utf8'),
|
||||
originalName: 'asset_1.mp4',
|
||||
}),
|
||||
};
|
15
server/test/fixtures/index.ts
vendored
Normal file
15
server/test/fixtures/index.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
export * from './album.stub';
|
||||
export * from './api-key.stub';
|
||||
export * from './asset.stub';
|
||||
export * from './auth.stub';
|
||||
export * from './face.stub';
|
||||
export * from './file.stub';
|
||||
export * from './media.stub';
|
||||
export * from './partner.stub';
|
||||
export * from './person.stub';
|
||||
export * from './search.stub';
|
||||
export * from './shared-link.stub';
|
||||
export * from './system-config.stub';
|
||||
export * from './tag.stub';
|
||||
export * from './user-token.stub';
|
||||
export * from './user.stub';
|
95
server/test/fixtures/media.stub.ts
vendored
Normal file
95
server/test/fixtures/media.stub.ts
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
import { AudioStreamInfo, VideoFormat, VideoInfo, VideoStreamInfo } from '@app/domain';
|
||||
|
||||
const probeStubDefaultFormat: VideoFormat = {
|
||||
formatName: 'mov,mp4,m4a,3gp,3g2,mj2',
|
||||
formatLongName: 'QuickTime / MOV',
|
||||
duration: 0,
|
||||
};
|
||||
|
||||
const probeStubDefaultVideoStream: VideoStreamInfo[] = [
|
||||
{ height: 1080, width: 1920, codecName: 'h265', codecType: 'video', frameCount: 100, rotation: 0 },
|
||||
];
|
||||
|
||||
const probeStubDefaultAudioStream: AudioStreamInfo[] = [{ codecName: 'aac', codecType: 'audio' }];
|
||||
|
||||
const probeStubDefault: VideoInfo = {
|
||||
format: probeStubDefaultFormat,
|
||||
videoStreams: probeStubDefaultVideoStream,
|
||||
audioStreams: probeStubDefaultAudioStream,
|
||||
};
|
||||
|
||||
export const probeStub = {
|
||||
noVideoStreams: Object.freeze<VideoInfo>({ ...probeStubDefault, videoStreams: [] }),
|
||||
multipleVideoStreams: Object.freeze<VideoInfo>({
|
||||
...probeStubDefault,
|
||||
videoStreams: [
|
||||
{
|
||||
height: 1080,
|
||||
width: 400,
|
||||
codecName: 'h265',
|
||||
codecType: 'video',
|
||||
frameCount: 100,
|
||||
rotation: 0,
|
||||
},
|
||||
{
|
||||
height: 1080,
|
||||
width: 400,
|
||||
codecName: 'h7000',
|
||||
codecType: 'video',
|
||||
frameCount: 99,
|
||||
rotation: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
noHeight: Object.freeze<VideoInfo>({
|
||||
...probeStubDefault,
|
||||
videoStreams: [
|
||||
{
|
||||
height: 0,
|
||||
width: 400,
|
||||
codecName: 'h265',
|
||||
codecType: 'video',
|
||||
frameCount: 100,
|
||||
rotation: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
videoStream2160p: Object.freeze<VideoInfo>({
|
||||
...probeStubDefault,
|
||||
videoStreams: [
|
||||
{
|
||||
height: 2160,
|
||||
width: 3840,
|
||||
codecName: 'h264',
|
||||
codecType: 'video',
|
||||
frameCount: 100,
|
||||
rotation: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
videoStreamVertical2160p: Object.freeze<VideoInfo>({
|
||||
...probeStubDefault,
|
||||
videoStreams: [
|
||||
{
|
||||
height: 2160,
|
||||
width: 3840,
|
||||
codecName: 'h264',
|
||||
codecType: 'video',
|
||||
frameCount: 100,
|
||||
rotation: 90,
|
||||
},
|
||||
],
|
||||
}),
|
||||
audioStreamMp3: Object.freeze<VideoInfo>({
|
||||
...probeStubDefault,
|
||||
audioStreams: [{ codecType: 'audio', codecName: 'aac' }],
|
||||
}),
|
||||
matroskaContainer: Object.freeze<VideoInfo>({
|
||||
...probeStubDefault,
|
||||
format: {
|
||||
formatName: 'matroska,webm',
|
||||
formatLongName: 'Matroska / WebM',
|
||||
duration: 0,
|
||||
},
|
||||
}),
|
||||
};
|
21
server/test/fixtures/partner.stub.ts
vendored
Normal file
21
server/test/fixtures/partner.stub.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
import { PartnerEntity } from '@app/infra/entities';
|
||||
import { userStub } from './user.stub';
|
||||
|
||||
export const partnerStub = {
|
||||
adminToUser1: Object.freeze<PartnerEntity>({
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
sharedById: userStub.admin.id,
|
||||
sharedBy: userStub.admin,
|
||||
sharedWith: userStub.user1,
|
||||
sharedWithId: userStub.user1.id,
|
||||
}),
|
||||
user1ToAdmin1: Object.freeze<PartnerEntity>({
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
sharedBy: userStub.user1,
|
||||
sharedById: userStub.user1.id,
|
||||
sharedWithId: userStub.admin.id,
|
||||
sharedWith: userStub.admin,
|
||||
}),
|
||||
};
|
82
server/test/fixtures/person.stub.ts
vendored
Normal file
82
server/test/fixtures/person.stub.ts
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
import { PersonEntity } from '@app/infra/entities';
|
||||
import { userStub } from './user.stub';
|
||||
|
||||
export const personStub = {
|
||||
noName: Object.freeze<PersonEntity>({
|
||||
id: 'person-1',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
ownerId: userStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
name: '',
|
||||
thumbnailPath: '/path/to/thumbnail.jpg',
|
||||
faces: [],
|
||||
isHidden: false,
|
||||
}),
|
||||
hidden: Object.freeze<PersonEntity>({
|
||||
id: 'person-1',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
ownerId: userStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
name: '',
|
||||
thumbnailPath: '/path/to/thumbnail.jpg',
|
||||
faces: [],
|
||||
isHidden: true,
|
||||
}),
|
||||
withName: Object.freeze<PersonEntity>({
|
||||
id: 'person-1',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
ownerId: userStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
name: 'Person 1',
|
||||
thumbnailPath: '/path/to/thumbnail.jpg',
|
||||
faces: [],
|
||||
isHidden: false,
|
||||
}),
|
||||
noThumbnail: Object.freeze<PersonEntity>({
|
||||
id: 'person-1',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
ownerId: userStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
name: '',
|
||||
thumbnailPath: '',
|
||||
faces: [],
|
||||
isHidden: false,
|
||||
}),
|
||||
newThumbnail: Object.freeze<PersonEntity>({
|
||||
id: 'person-1',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
ownerId: userStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
name: '',
|
||||
thumbnailPath: '/new/path/to/thumbnail.jpg',
|
||||
faces: [],
|
||||
isHidden: false,
|
||||
}),
|
||||
primaryPerson: Object.freeze<PersonEntity>({
|
||||
id: 'person-1',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
ownerId: userStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
name: 'Person 1',
|
||||
thumbnailPath: '/path/to/thumbnail',
|
||||
faces: [],
|
||||
isHidden: false,
|
||||
}),
|
||||
mergePerson: Object.freeze<PersonEntity>({
|
||||
id: 'person-2',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
ownerId: userStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
name: 'Person 2',
|
||||
thumbnailPath: '/path/to/thumbnail',
|
||||
faces: [],
|
||||
isHidden: false,
|
||||
}),
|
||||
};
|
12
server/test/fixtures/search.stub.ts
vendored
Normal file
12
server/test/fixtures/search.stub.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
import { SearchResult } from '@app/domain';
|
||||
|
||||
export const searchStub = {
|
||||
emptyResults: Object.freeze<SearchResult<any>>({
|
||||
total: 0,
|
||||
count: 0,
|
||||
page: 1,
|
||||
items: [],
|
||||
facets: [],
|
||||
distances: [],
|
||||
}),
|
||||
};
|
282
server/test/fixtures/shared-link.stub.ts
vendored
Normal file
282
server/test/fixtures/shared-link.stub.ts
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
import { AlbumResponseDto, AssetResponseDto, ExifResponseDto, mapUser, SharedLinkResponseDto } from '@app/domain';
|
||||
import { AssetType, SharedLinkEntity, SharedLinkType } from '@app/infra/entities';
|
||||
import { assetStub } from './asset.stub';
|
||||
import { authStub } from './auth.stub';
|
||||
import { userStub } from './user.stub';
|
||||
|
||||
const today = new Date();
|
||||
const tomorrow = new Date();
|
||||
const yesterday = new Date();
|
||||
tomorrow.setDate(today.getDate() + 1);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
|
||||
const sharedLinkBytes = Buffer.from(
|
||||
'2c2b646895f84753bff43fb696ad124f3b0faf2a0bd547406f26fa4a76b5c71990092baa536275654b2ab7a191fb21a6d6cd',
|
||||
'hex',
|
||||
);
|
||||
|
||||
const assetInfo: ExifResponseDto = {
|
||||
make: 'camera-make',
|
||||
model: 'camera-model',
|
||||
exifImageWidth: 500,
|
||||
exifImageHeight: 500,
|
||||
fileSizeInByte: 100,
|
||||
orientation: 'orientation',
|
||||
dateTimeOriginal: today,
|
||||
modifyDate: today,
|
||||
timeZone: 'America/Los_Angeles',
|
||||
lensModel: 'fancy',
|
||||
fNumber: 100,
|
||||
focalLength: 100,
|
||||
iso: 100,
|
||||
exposureTime: '1/16',
|
||||
latitude: 100,
|
||||
longitude: 100,
|
||||
city: 'city',
|
||||
state: 'state',
|
||||
country: 'country',
|
||||
description: 'description',
|
||||
projectionType: null,
|
||||
};
|
||||
|
||||
const assetResponse: AssetResponseDto = {
|
||||
id: 'id_1',
|
||||
deviceAssetId: 'device_asset_id_1',
|
||||
ownerId: 'user_id_1',
|
||||
deviceId: 'device_id_1',
|
||||
type: AssetType.VIDEO,
|
||||
originalPath: 'fake_path/jpeg',
|
||||
originalFileName: 'asset_1.jpeg',
|
||||
resized: false,
|
||||
thumbhash: null,
|
||||
fileModifiedAt: today,
|
||||
fileCreatedAt: today,
|
||||
updatedAt: today,
|
||||
isFavorite: false,
|
||||
isArchived: false,
|
||||
smartInfo: {
|
||||
tags: [],
|
||||
objects: ['a', 'b', 'c'],
|
||||
},
|
||||
duration: '0:00:00.00000',
|
||||
exifInfo: assetInfo,
|
||||
livePhotoVideoId: null,
|
||||
tags: [],
|
||||
people: [],
|
||||
checksum: 'ZmlsZSBoYXNo',
|
||||
};
|
||||
|
||||
const albumResponse: AlbumResponseDto = {
|
||||
albumName: 'Test Album',
|
||||
albumThumbnailAssetId: null,
|
||||
createdAt: today,
|
||||
updatedAt: today,
|
||||
id: 'album-123',
|
||||
ownerId: 'admin_id',
|
||||
owner: mapUser(userStub.admin),
|
||||
sharedUsers: [],
|
||||
shared: false,
|
||||
assets: [],
|
||||
assetCount: 1,
|
||||
};
|
||||
|
||||
export const sharedLinkStub = {
|
||||
individual: Object.freeze({
|
||||
id: '123',
|
||||
userId: authStub.admin.id,
|
||||
user: userStub.admin,
|
||||
key: sharedLinkBytes,
|
||||
type: SharedLinkType.INDIVIDUAL,
|
||||
createdAt: today,
|
||||
expiresAt: tomorrow,
|
||||
allowUpload: true,
|
||||
allowDownload: true,
|
||||
showExif: true,
|
||||
album: undefined,
|
||||
description: null,
|
||||
assets: [assetStub.image],
|
||||
} as SharedLinkEntity),
|
||||
valid: Object.freeze({
|
||||
id: '123',
|
||||
userId: authStub.admin.id,
|
||||
user: userStub.admin,
|
||||
key: sharedLinkBytes,
|
||||
type: SharedLinkType.ALBUM,
|
||||
createdAt: today,
|
||||
expiresAt: tomorrow,
|
||||
allowUpload: true,
|
||||
allowDownload: true,
|
||||
showExif: true,
|
||||
album: undefined,
|
||||
albumId: null,
|
||||
description: null,
|
||||
assets: [],
|
||||
} as SharedLinkEntity),
|
||||
expired: Object.freeze({
|
||||
id: '123',
|
||||
userId: authStub.admin.id,
|
||||
user: userStub.admin,
|
||||
key: sharedLinkBytes,
|
||||
type: SharedLinkType.ALBUM,
|
||||
createdAt: today,
|
||||
expiresAt: yesterday,
|
||||
allowUpload: true,
|
||||
allowDownload: true,
|
||||
showExif: true,
|
||||
description: null,
|
||||
albumId: null,
|
||||
assets: [],
|
||||
} as SharedLinkEntity),
|
||||
readonlyNoExif: Object.freeze<SharedLinkEntity>({
|
||||
id: '123',
|
||||
userId: authStub.admin.id,
|
||||
user: userStub.admin,
|
||||
key: sharedLinkBytes,
|
||||
type: SharedLinkType.ALBUM,
|
||||
createdAt: today,
|
||||
expiresAt: tomorrow,
|
||||
allowUpload: false,
|
||||
allowDownload: false,
|
||||
showExif: false,
|
||||
description: null,
|
||||
assets: [],
|
||||
albumId: 'album-123',
|
||||
album: {
|
||||
id: 'album-123',
|
||||
ownerId: authStub.admin.id,
|
||||
owner: userStub.admin,
|
||||
albumName: 'Test Album',
|
||||
createdAt: today,
|
||||
updatedAt: today,
|
||||
albumThumbnailAsset: null,
|
||||
albumThumbnailAssetId: null,
|
||||
sharedUsers: [],
|
||||
sharedLinks: [],
|
||||
assets: [
|
||||
{
|
||||
id: 'id_1',
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user_id_1',
|
||||
deviceAssetId: 'device_asset_id_1',
|
||||
deviceId: 'device_id_1',
|
||||
type: AssetType.VIDEO,
|
||||
originalPath: 'fake_path/jpeg',
|
||||
resizePath: '',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
fileModifiedAt: today,
|
||||
fileCreatedAt: today,
|
||||
createdAt: today,
|
||||
updatedAt: today,
|
||||
isFavorite: false,
|
||||
isArchived: false,
|
||||
isReadOnly: false,
|
||||
smartInfo: {
|
||||
assetId: 'id_1',
|
||||
tags: [],
|
||||
objects: ['a', 'b', 'c'],
|
||||
asset: null as any,
|
||||
clipEmbedding: [0.12, 0.13, 0.14],
|
||||
},
|
||||
webpPath: '',
|
||||
thumbhash: null,
|
||||
encodedVideoPath: '',
|
||||
duration: null,
|
||||
isVisible: true,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
originalFileName: 'asset_1.jpeg',
|
||||
exifInfo: {
|
||||
projectionType: null,
|
||||
livePhotoCID: null,
|
||||
assetId: 'id_1',
|
||||
description: 'description',
|
||||
exifImageWidth: 500,
|
||||
exifImageHeight: 500,
|
||||
fileSizeInByte: 100,
|
||||
orientation: 'orientation',
|
||||
dateTimeOriginal: today,
|
||||
modifyDate: today,
|
||||
timeZone: 'America/Los_Angeles',
|
||||
latitude: 100,
|
||||
longitude: 100,
|
||||
city: 'city',
|
||||
state: 'state',
|
||||
country: 'country',
|
||||
make: 'camera-make',
|
||||
model: 'camera-model',
|
||||
lensModel: 'fancy',
|
||||
fNumber: 100,
|
||||
focalLength: 100,
|
||||
iso: 100,
|
||||
exposureTime: '1/16',
|
||||
fps: 100,
|
||||
asset: null as any,
|
||||
exifTextSearchableColumn: '',
|
||||
},
|
||||
tags: [],
|
||||
sharedLinks: [],
|
||||
faces: [],
|
||||
sidecarPath: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export const sharedLinkResponseStub = {
|
||||
valid: Object.freeze<SharedLinkResponseDto>({
|
||||
allowDownload: true,
|
||||
allowUpload: true,
|
||||
assets: [],
|
||||
createdAt: today,
|
||||
description: null,
|
||||
expiresAt: tomorrow,
|
||||
id: '123',
|
||||
key: sharedLinkBytes.toString('base64url'),
|
||||
showExif: true,
|
||||
type: SharedLinkType.ALBUM,
|
||||
userId: 'admin_id',
|
||||
}),
|
||||
expired: Object.freeze<SharedLinkResponseDto>({
|
||||
album: undefined,
|
||||
allowDownload: true,
|
||||
allowUpload: true,
|
||||
assets: [],
|
||||
createdAt: today,
|
||||
description: null,
|
||||
expiresAt: yesterday,
|
||||
id: '123',
|
||||
key: sharedLinkBytes.toString('base64url'),
|
||||
showExif: true,
|
||||
type: SharedLinkType.ALBUM,
|
||||
userId: 'admin_id',
|
||||
}),
|
||||
readonly: Object.freeze<SharedLinkResponseDto>({
|
||||
id: '123',
|
||||
userId: 'admin_id',
|
||||
key: sharedLinkBytes.toString('base64url'),
|
||||
type: SharedLinkType.ALBUM,
|
||||
createdAt: today,
|
||||
expiresAt: tomorrow,
|
||||
description: null,
|
||||
allowUpload: false,
|
||||
allowDownload: false,
|
||||
showExif: true,
|
||||
album: albumResponse,
|
||||
assets: [assetResponse],
|
||||
}),
|
||||
readonlyNoExif: Object.freeze<SharedLinkResponseDto>({
|
||||
id: '123',
|
||||
userId: 'admin_id',
|
||||
key: sharedLinkBytes.toString('base64url'),
|
||||
type: SharedLinkType.ALBUM,
|
||||
createdAt: today,
|
||||
expiresAt: tomorrow,
|
||||
description: null,
|
||||
allowUpload: false,
|
||||
allowDownload: false,
|
||||
showExif: false,
|
||||
album: albumResponse,
|
||||
assets: [{ ...assetResponse, exifInfo: undefined }],
|
||||
}),
|
||||
};
|
25
server/test/fixtures/system-config.stub.ts
vendored
Normal file
25
server/test/fixtures/system-config.stub.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
import { SystemConfigEntity, SystemConfigKey } from '@app/infra/entities';
|
||||
|
||||
export const systemConfigStub: Record<string, SystemConfigEntity[]> = {
|
||||
defaults: [],
|
||||
enabled: [
|
||||
{ key: SystemConfigKey.OAUTH_ENABLED, value: true },
|
||||
{ key: SystemConfigKey.OAUTH_AUTO_REGISTER, value: true },
|
||||
{ key: SystemConfigKey.OAUTH_AUTO_LAUNCH, value: false },
|
||||
{ key: SystemConfigKey.OAUTH_BUTTON_TEXT, value: 'OAuth' },
|
||||
],
|
||||
disabled: [{ key: SystemConfigKey.PASSWORD_LOGIN_ENABLED, value: false }],
|
||||
noAutoRegister: [
|
||||
{ key: SystemConfigKey.OAUTH_ENABLED, value: true },
|
||||
{ key: SystemConfigKey.OAUTH_AUTO_LAUNCH, value: false },
|
||||
{ key: SystemConfigKey.OAUTH_AUTO_REGISTER, value: false },
|
||||
{ key: SystemConfigKey.OAUTH_BUTTON_TEXT, value: 'OAuth' },
|
||||
],
|
||||
override: [
|
||||
{ key: SystemConfigKey.OAUTH_ENABLED, value: true },
|
||||
{ key: SystemConfigKey.OAUTH_AUTO_REGISTER, value: true },
|
||||
{ key: SystemConfigKey.OAUTH_MOBILE_OVERRIDE_ENABLED, value: true },
|
||||
{ key: SystemConfigKey.OAUTH_MOBILE_REDIRECT_URI, value: 'http://mobile-redirect' },
|
||||
{ key: SystemConfigKey.OAUTH_BUTTON_TEXT, value: 'OAuth' },
|
||||
],
|
||||
};
|
24
server/test/fixtures/tag.stub.ts
vendored
Normal file
24
server/test/fixtures/tag.stub.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
import { TagResponseDto } from '@app/domain';
|
||||
import { TagEntity, TagType } from '@app/infra/entities';
|
||||
import { userStub } from './user.stub';
|
||||
|
||||
export const tagStub = {
|
||||
tag1: Object.freeze<TagEntity>({
|
||||
id: 'tag-1',
|
||||
name: 'Tag1',
|
||||
type: TagType.CUSTOM,
|
||||
userId: userStub.admin.id,
|
||||
user: userStub.admin,
|
||||
renameTagId: null,
|
||||
assets: [],
|
||||
}),
|
||||
};
|
||||
|
||||
export const tagResponseStub = {
|
||||
tag1: Object.freeze<TagResponseDto>({
|
||||
id: 'tag-1',
|
||||
name: 'Tag1',
|
||||
type: 'CUSTOM',
|
||||
userId: 'admin_id',
|
||||
}),
|
||||
};
|
25
server/test/fixtures/user-token.stub.ts
vendored
Normal file
25
server/test/fixtures/user-token.stub.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
import { UserTokenEntity } from '@app/infra/entities';
|
||||
import { userStub } from './user.stub';
|
||||
|
||||
export const userTokenStub = {
|
||||
userToken: Object.freeze<UserTokenEntity>({
|
||||
id: 'token-id',
|
||||
token: 'auth_token',
|
||||
userId: userStub.user1.id,
|
||||
user: userStub.user1,
|
||||
createdAt: new Date('2021-01-01'),
|
||||
updatedAt: new Date(),
|
||||
deviceType: '',
|
||||
deviceOS: '',
|
||||
}),
|
||||
inactiveToken: Object.freeze<UserTokenEntity>({
|
||||
id: 'not_active',
|
||||
token: 'auth_token',
|
||||
userId: userStub.user1.id,
|
||||
user: userStub.user1,
|
||||
createdAt: new Date('2021-01-01'),
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
deviceType: 'Mobile',
|
||||
deviceOS: 'Android',
|
||||
}),
|
||||
};
|
69
server/test/fixtures/user.stub.ts
vendored
Normal file
69
server/test/fixtures/user.stub.ts
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
import { UserEntity } from '@app/infra/entities';
|
||||
import { authStub } from './auth.stub';
|
||||
|
||||
export const userStub = {
|
||||
admin: Object.freeze<UserEntity>({
|
||||
...authStub.admin,
|
||||
password: 'admin_password',
|
||||
firstName: 'admin_first_name',
|
||||
lastName: 'admin_last_name',
|
||||
storageLabel: 'admin',
|
||||
externalPath: null,
|
||||
oauthId: '',
|
||||
shouldChangePassword: false,
|
||||
profileImagePath: '',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
tags: [],
|
||||
assets: [],
|
||||
}),
|
||||
user1: Object.freeze<UserEntity>({
|
||||
...authStub.user1,
|
||||
password: 'immich_password',
|
||||
firstName: 'immich_first_name',
|
||||
lastName: 'immich_last_name',
|
||||
storageLabel: null,
|
||||
externalPath: null,
|
||||
oauthId: '',
|
||||
shouldChangePassword: false,
|
||||
profileImagePath: '',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
tags: [],
|
||||
assets: [],
|
||||
}),
|
||||
user2: Object.freeze<UserEntity>({
|
||||
...authStub.user2,
|
||||
password: 'immich_password',
|
||||
firstName: 'immich_first_name',
|
||||
lastName: 'immich_last_name',
|
||||
storageLabel: null,
|
||||
externalPath: null,
|
||||
oauthId: '',
|
||||
shouldChangePassword: false,
|
||||
profileImagePath: '',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
tags: [],
|
||||
assets: [],
|
||||
}),
|
||||
storageLabel: Object.freeze<UserEntity>({
|
||||
...authStub.user1,
|
||||
password: 'immich_password',
|
||||
firstName: 'immich_first_name',
|
||||
lastName: 'immich_last_name',
|
||||
storageLabel: 'label-1',
|
||||
externalPath: null,
|
||||
oauthId: '',
|
||||
shouldChangePassword: false,
|
||||
profileImagePath: '',
|
||||
createdAt: new Date('2021-01-01'),
|
||||
deletedAt: null,
|
||||
updatedAt: new Date('2021-01-01'),
|
||||
tags: [],
|
||||
assets: [],
|
||||
}),
|
||||
};
|
Loading…
Reference in New Issue
Block a user