mirror of
https://github.com/immich-app/immich.git
synced 2025-03-01 15:11:21 +01:00
refactor(server): test fixtures (#3491)
This commit is contained in:
parent
5f9dfa9493
commit
9e085c1071
32 changed files with 1545 additions and 1538 deletions
|
@ -8,7 +8,7 @@ import {
|
||||||
newAssetRepositoryMock,
|
newAssetRepositoryMock,
|
||||||
newJobRepositoryMock,
|
newJobRepositoryMock,
|
||||||
newUserRepositoryMock,
|
newUserRepositoryMock,
|
||||||
userEntityStub,
|
userStub,
|
||||||
} from '@test';
|
} from '@test';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { IAssetRepository } from '../asset';
|
import { IAssetRepository } from '../asset';
|
||||||
|
@ -326,12 +326,12 @@ describe(AlbumService.name, () => {
|
||||||
accessMock.album.hasOwnerAccess.mockResolvedValue(true);
|
accessMock.album.hasOwnerAccess.mockResolvedValue(true);
|
||||||
albumMock.getByIds.mockResolvedValue([_.cloneDeep(albumStub.sharedWithAdmin)]);
|
albumMock.getByIds.mockResolvedValue([_.cloneDeep(albumStub.sharedWithAdmin)]);
|
||||||
albumMock.update.mockResolvedValue(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] });
|
await sut.addUsers(authStub.user1, albumStub.sharedWithAdmin.id, { sharedUserIds: [authStub.user2.id] });
|
||||||
expect(albumMock.update).toHaveBeenCalledWith({
|
expect(albumMock.update).toHaveBeenCalledWith({
|
||||||
id: albumStub.sharedWithAdmin.id,
|
id: albumStub.sharedWithAdmin.id,
|
||||||
updatedAt: expect.any(Date),
|
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]);
|
albumMock.getByIds.mockResolvedValue([albumStub.sharedWithUser]);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
sut.removeUser(authStub.admin, albumStub.sharedWithUser.id, userEntityStub.user1.id),
|
sut.removeUser(authStub.admin, albumStub.sharedWithUser.id, userStub.user1.id),
|
||||||
).resolves.toBeUndefined();
|
).resolves.toBeUndefined();
|
||||||
|
|
||||||
expect(albumMock.update).toHaveBeenCalledTimes(1);
|
expect(albumMock.update).toHaveBeenCalledTimes(1);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { AssetType } from '@app/infra/entities';
|
import { AssetType } from '@app/infra/entities';
|
||||||
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
|
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
authStub,
|
authStub,
|
||||||
IAccessRepositoryMock,
|
IAccessRepositoryMock,
|
||||||
newAccessRepositoryMock,
|
newAccessRepositoryMock,
|
||||||
|
@ -246,7 +246,7 @@ describe(AssetService.name, () => {
|
||||||
describe('getMapMarkers', () => {
|
describe('getMapMarkers', () => {
|
||||||
it('should get geo information of assets', async () => {
|
it('should get geo information of assets', async () => {
|
||||||
assetMock.getMapMarkers.mockResolvedValue(
|
assetMock.getMapMarkers.mockResolvedValue(
|
||||||
[assetEntityStub.withLocation].map((asset) => ({
|
[assetStub.withLocation].map((asset) => ({
|
||||||
id: asset.id,
|
id: asset.id,
|
||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
||||||
|
@ -261,7 +261,7 @@ describe(AssetService.name, () => {
|
||||||
|
|
||||||
expect(markers).toHaveLength(1);
|
expect(markers).toHaveLength(1);
|
||||||
expect(markers[0]).toEqual({
|
expect(markers[0]).toEqual({
|
||||||
id: assetEntityStub.withLocation.id,
|
id: assetStub.withLocation.id,
|
||||||
lat: 100,
|
lat: 100,
|
||||||
lon: 100,
|
lon: 100,
|
||||||
});
|
});
|
||||||
|
@ -308,14 +308,14 @@ describe(AssetService.name, () => {
|
||||||
it('should set the title correctly', async () => {
|
it('should set the title correctly', async () => {
|
||||||
when(assetMock.getByDate)
|
when(assetMock.getByDate)
|
||||||
.calledWith(authStub.admin.id, new Date('2022-06-15T00:00:00.000Z'))
|
.calledWith(authStub.admin.id, new Date('2022-06-15T00:00:00.000Z'))
|
||||||
.mockResolvedValue([assetEntityStub.image]);
|
.mockResolvedValue([assetStub.image]);
|
||||||
when(assetMock.getByDate)
|
when(assetMock.getByDate)
|
||||||
.calledWith(authStub.admin.id, new Date('2021-06-15T00:00:00.000Z'))
|
.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([
|
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: '1 year since...', assets: [mapAsset(assetStub.image)] },
|
||||||
{ title: '2 years since...', assets: [mapAsset(assetEntityStub.video)] },
|
{ title: '2 years since...', assets: [mapAsset(assetStub.video)] },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(assetMock.getByDate).toHaveBeenCalledTimes(2);
|
expect(assetMock.getByDate).toHaveBeenCalledTimes(2);
|
||||||
|
@ -352,12 +352,12 @@ describe(AssetService.name, () => {
|
||||||
const stream = new Readable();
|
const stream = new Readable();
|
||||||
|
|
||||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
storageMock.createReadStream.mockResolvedValue({ stream });
|
storageMock.createReadStream.mockResolvedValue({ stream });
|
||||||
|
|
||||||
await expect(sut.downloadFile(authStub.admin, 'asset-1')).resolves.toEqual({ 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 () => {
|
it('should download an archive', async () => {
|
||||||
|
@ -368,7 +368,7 @@ describe(AssetService.name, () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath, assetEntityStub.noWebpPath]);
|
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath, assetStub.noWebpPath]);
|
||||||
storageMock.createZipStream.mockReturnValue(archiveMock);
|
storageMock.createZipStream.mockReturnValue(archiveMock);
|
||||||
|
|
||||||
await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1', 'asset-2'] })).resolves.toEqual({
|
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);
|
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath, assetEntityStub.noResizePath]);
|
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath, assetStub.noResizePath]);
|
||||||
storageMock.createZipStream.mockReturnValue(archiveMock);
|
storageMock.createZipStream.mockReturnValue(archiveMock);
|
||||||
|
|
||||||
await expect(sut.downloadArchive(authStub.admin, { assetIds: ['asset-1', 'asset-2'] })).resolves.toEqual({
|
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 () => {
|
it('should return a list of archives (assetIds)', async () => {
|
||||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
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'];
|
const assetIds = ['asset-1', 'asset-2'];
|
||||||
await expect(sut.getDownloadInfo(authStub.admin, { assetIds })).resolves.toEqual(downloadResponse);
|
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 () => {
|
it('should return a list of archives (albumId)', async () => {
|
||||||
accessMock.album.hasOwnerAccess.mockResolvedValue(true);
|
accessMock.album.hasOwnerAccess.mockResolvedValue(true);
|
||||||
assetMock.getByAlbumId.mockResolvedValue({
|
assetMock.getByAlbumId.mockResolvedValue({
|
||||||
items: [assetEntityStub.image, assetEntityStub.video],
|
items: [assetStub.image, assetStub.video],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -431,7 +431,7 @@ describe(AssetService.name, () => {
|
||||||
|
|
||||||
it('should return a list of archives (userId)', async () => {
|
it('should return a list of archives (userId)', async () => {
|
||||||
assetMock.getByUserId.mockResolvedValue({
|
assetMock.getByUserId.mockResolvedValue({
|
||||||
items: [assetEntityStub.image, assetEntityStub.video],
|
items: [assetStub.image, assetStub.video],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -445,10 +445,10 @@ describe(AssetService.name, () => {
|
||||||
it('should split archives by size', async () => {
|
it('should split archives by size', async () => {
|
||||||
assetMock.getByUserId.mockResolvedValue({
|
assetMock.getByUserId.mockResolvedValue({
|
||||||
items: [
|
items: [
|
||||||
{ ...assetEntityStub.image, id: 'asset-1' },
|
{ ...assetStub.image, id: 'asset-1' },
|
||||||
{ ...assetEntityStub.video, id: 'asset-2' },
|
{ ...assetStub.video, id: 'asset-2' },
|
||||||
{ ...assetEntityStub.withLocation, id: 'asset-3' },
|
{ ...assetStub.withLocation, id: 'asset-3' },
|
||||||
{ ...assetEntityStub.noWebpPath, id: 'asset-4' },
|
{ ...assetStub.noWebpPath, id: 'asset-4' },
|
||||||
],
|
],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
@ -470,18 +470,18 @@ describe(AssetService.name, () => {
|
||||||
it('should include the video portion of a live photo', async () => {
|
it('should include the video portion of a live photo', async () => {
|
||||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||||
when(assetMock.getByIds)
|
when(assetMock.getByIds)
|
||||||
.calledWith([assetEntityStub.livePhotoStillAsset.id])
|
.calledWith([assetStub.livePhotoStillAsset.id])
|
||||||
.mockResolvedValue([assetEntityStub.livePhotoStillAsset]);
|
.mockResolvedValue([assetStub.livePhotoStillAsset]);
|
||||||
when(assetMock.getByIds)
|
when(assetMock.getByIds)
|
||||||
.calledWith([assetEntityStub.livePhotoMotionAsset.id])
|
.calledWith([assetStub.livePhotoMotionAsset.id])
|
||||||
.mockResolvedValue([assetEntityStub.livePhotoMotionAsset]);
|
.mockResolvedValue([assetStub.livePhotoMotionAsset]);
|
||||||
|
|
||||||
const assetIds = [assetEntityStub.livePhotoStillAsset.id];
|
const assetIds = [assetStub.livePhotoStillAsset.id];
|
||||||
await expect(sut.getDownloadInfo(authStub.admin, { assetIds })).resolves.toEqual({
|
await expect(sut.getDownloadInfo(authStub.admin, { assetIds })).resolves.toEqual({
|
||||||
totalSize: 125_000,
|
totalSize: 125_000,
|
||||||
archives: [
|
archives: [
|
||||||
{
|
{
|
||||||
assetIds: [assetEntityStub.livePhotoStillAsset.id, assetEntityStub.livePhotoMotionAsset.id],
|
assetIds: [assetStub.livePhotoStillAsset.id, assetStub.livePhotoMotionAsset.id],
|
||||||
size: 125_000,
|
size: 125_000,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -12,8 +12,8 @@ import {
|
||||||
newUserTokenRepositoryMock,
|
newUserTokenRepositoryMock,
|
||||||
sharedLinkStub,
|
sharedLinkStub,
|
||||||
systemConfigStub,
|
systemConfigStub,
|
||||||
userEntityStub,
|
userStub,
|
||||||
userTokenEntityStub,
|
userTokenStub,
|
||||||
} from '@test';
|
} from '@test';
|
||||||
import { IncomingHttpHeaders } from 'http';
|
import { IncomingHttpHeaders } from 'http';
|
||||||
import { generators, Issuer } from 'openid-client';
|
import { generators, Issuer } from 'openid-client';
|
||||||
|
@ -112,15 +112,15 @@ describe('AuthService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should successfully log the user in', async () => {
|
it('should successfully log the user in', async () => {
|
||||||
userMock.getByEmail.mockResolvedValue(userEntityStub.user1);
|
userMock.getByEmail.mockResolvedValue(userStub.user1);
|
||||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||||
await expect(sut.login(fixtures.login, loginDetails)).resolves.toEqual(loginResponseStub.user1password);
|
await expect(sut.login(fixtures.login, loginDetails)).resolves.toEqual(loginResponseStub.user1password);
|
||||||
expect(userMock.getByEmail).toHaveBeenCalledTimes(1);
|
expect(userMock.getByEmail).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate the cookie headers (insecure)', async () => {
|
it('should generate the cookie headers (insecure)', async () => {
|
||||||
userMock.getByEmail.mockResolvedValue(userEntityStub.user1);
|
userMock.getByEmail.mockResolvedValue(userStub.user1);
|
||||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||||
await expect(
|
await expect(
|
||||||
sut.login(fixtures.login, {
|
sut.login(fixtures.login, {
|
||||||
clientIp: '127.0.0.1',
|
clientIp: '127.0.0.1',
|
||||||
|
@ -246,10 +246,10 @@ describe('AuthService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should validate using authorization header', async () => {
|
it('should validate using authorization header', async () => {
|
||||||
userMock.get.mockResolvedValue(userEntityStub.user1);
|
userMock.get.mockResolvedValue(userStub.user1);
|
||||||
userTokenMock.getByToken.mockResolvedValue(userTokenEntityStub.userToken);
|
userTokenMock.getByToken.mockResolvedValue(userTokenStub.userToken);
|
||||||
const client = { request: { headers: { authorization: 'Bearer auth_token' } } };
|
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 () => {
|
it('should accept a base64url key', async () => {
|
||||||
shareMock.getByKey.mockResolvedValue(sharedLinkStub.valid);
|
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') };
|
const headers: IncomingHttpHeaders = { 'x-immich-share-key': sharedLinkStub.valid.key.toString('base64url') };
|
||||||
await expect(sut.validate(headers, {})).resolves.toEqual(authStub.adminSharedLink);
|
await expect(sut.validate(headers, {})).resolves.toEqual(authStub.adminSharedLink);
|
||||||
expect(shareMock.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key);
|
expect(shareMock.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key);
|
||||||
|
@ -283,7 +283,7 @@ describe('AuthService', () => {
|
||||||
|
|
||||||
it('should accept a hex key', async () => {
|
it('should accept a hex key', async () => {
|
||||||
shareMock.getByKey.mockResolvedValue(sharedLinkStub.valid);
|
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') };
|
const headers: IncomingHttpHeaders = { 'x-immich-share-key': sharedLinkStub.valid.key.toString('hex') };
|
||||||
await expect(sut.validate(headers, {})).resolves.toEqual(authStub.adminSharedLink);
|
await expect(sut.validate(headers, {})).resolves.toEqual(authStub.adminSharedLink);
|
||||||
expect(shareMock.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key);
|
expect(shareMock.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key);
|
||||||
|
@ -298,16 +298,16 @@ describe('AuthService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an auth dto', async () => {
|
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' };
|
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 () => {
|
it('should update when access time exceeds an hour', async () => {
|
||||||
userTokenMock.getByToken.mockResolvedValue(userTokenEntityStub.inactiveToken);
|
userTokenMock.getByToken.mockResolvedValue(userTokenStub.inactiveToken);
|
||||||
userTokenMock.save.mockResolvedValue(userTokenEntityStub.userToken);
|
userTokenMock.save.mockResolvedValue(userTokenStub.userToken);
|
||||||
const headers: IncomingHttpHeaders = { cookie: 'immich_access_token=auth_token' };
|
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({
|
expect(userTokenMock.save.mock.calls[0][0]).toMatchObject({
|
||||||
id: 'not_active',
|
id: 'not_active',
|
||||||
token: 'auth_token',
|
token: 'auth_token',
|
||||||
|
@ -338,7 +338,7 @@ describe('AuthService', () => {
|
||||||
|
|
||||||
describe('getDevices', () => {
|
describe('getDevices', () => {
|
||||||
it('should get the devices', async () => {
|
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([
|
await expect(sut.getDevices(authStub.user1)).resolves.toEqual([
|
||||||
{
|
{
|
||||||
createdAt: '2021-01-01T00:00:00.000Z',
|
createdAt: '2021-01-01T00:00:00.000Z',
|
||||||
|
@ -364,7 +364,7 @@ describe('AuthService', () => {
|
||||||
|
|
||||||
describe('logoutDevices', () => {
|
describe('logoutDevices', () => {
|
||||||
it('should logout all devices', async () => {
|
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);
|
await sut.logoutDevices(authStub.user1);
|
||||||
|
|
||||||
|
@ -429,24 +429,24 @@ describe('AuthService', () => {
|
||||||
|
|
||||||
it('should link an existing user', async () => {
|
it('should link an existing user', async () => {
|
||||||
configMock.load.mockResolvedValue(systemConfigStub.noAutoRegister);
|
configMock.load.mockResolvedValue(systemConfigStub.noAutoRegister);
|
||||||
userMock.getByEmail.mockResolvedValue(userEntityStub.user1);
|
userMock.getByEmail.mockResolvedValue(userStub.user1);
|
||||||
userMock.update.mockResolvedValue(userEntityStub.user1);
|
userMock.update.mockResolvedValue(userStub.user1);
|
||||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||||
|
|
||||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||||
loginResponseStub.user1oauth,
|
loginResponseStub.user1oauth,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(userMock.getByEmail).toHaveBeenCalledTimes(1);
|
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 () => {
|
it('should allow auto registering by default', async () => {
|
||||||
configMock.load.mockResolvedValue(systemConfigStub.enabled);
|
configMock.load.mockResolvedValue(systemConfigStub.enabled);
|
||||||
userMock.getByEmail.mockResolvedValue(null);
|
userMock.getByEmail.mockResolvedValue(null);
|
||||||
userMock.getAdmin.mockResolvedValue(userEntityStub.user1);
|
userMock.getAdmin.mockResolvedValue(userStub.user1);
|
||||||
userMock.create.mockResolvedValue(userEntityStub.user1);
|
userMock.create.mockResolvedValue(userStub.user1);
|
||||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||||
|
|
||||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||||
loginResponseStub.user1oauth,
|
loginResponseStub.user1oauth,
|
||||||
|
@ -458,8 +458,8 @@ describe('AuthService', () => {
|
||||||
|
|
||||||
it('should use the mobile redirect override', async () => {
|
it('should use the mobile redirect override', async () => {
|
||||||
configMock.load.mockResolvedValue(systemConfigStub.override);
|
configMock.load.mockResolvedValue(systemConfigStub.override);
|
||||||
userMock.getByOAuthId.mockResolvedValue(userEntityStub.user1);
|
userMock.getByOAuthId.mockResolvedValue(userStub.user1);
|
||||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||||
|
|
||||||
await sut.callback({ url: `app.immich:/?code=abc123` }, loginDetails);
|
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 () => {
|
it('should use the mobile redirect override for ios urls with multiple slashes', async () => {
|
||||||
configMock.load.mockResolvedValue(systemConfigStub.override);
|
configMock.load.mockResolvedValue(systemConfigStub.override);
|
||||||
userMock.getByOAuthId.mockResolvedValue(userEntityStub.user1);
|
userMock.getByOAuthId.mockResolvedValue(userStub.user1);
|
||||||
userTokenMock.create.mockResolvedValue(userTokenEntityStub.userToken);
|
userTokenMock.create.mockResolvedValue(userTokenStub.userToken);
|
||||||
|
|
||||||
await sut.callback({ url: `app.immich:///?code=abc123` }, loginDetails);
|
await sut.callback({ url: `app.immich:///?code=abc123` }, loginDetails);
|
||||||
|
|
||||||
|
@ -480,7 +480,7 @@ describe('AuthService', () => {
|
||||||
describe('link', () => {
|
describe('link', () => {
|
||||||
it('should link an account', async () => {
|
it('should link an account', async () => {
|
||||||
configMock.load.mockResolvedValue(systemConfigStub.enabled);
|
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' });
|
await sut.link(authStub.user1, { url: 'http://immich/user-settings?code=abc123' });
|
||||||
|
|
||||||
|
@ -502,7 +502,7 @@ describe('AuthService', () => {
|
||||||
describe('unlink', () => {
|
describe('unlink', () => {
|
||||||
it('should unlink an account', async () => {
|
it('should unlink an account', async () => {
|
||||||
configMock.load.mockResolvedValue(systemConfigStub.enabled);
|
configMock.load.mockResolvedValue(systemConfigStub.enabled);
|
||||||
userMock.update.mockResolvedValue(userEntityStub.user1);
|
userMock.update.mockResolvedValue(userStub.user1);
|
||||||
|
|
||||||
await sut.unlink(authStub.user1);
|
await sut.unlink(authStub.user1);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {
|
import {
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
faceStub,
|
faceStub,
|
||||||
newAssetRepositoryMock,
|
newAssetRepositoryMock,
|
||||||
newFaceRepositoryMock,
|
newFaceRepositoryMock,
|
||||||
|
@ -133,7 +133,7 @@ describe(FacialRecognitionService.name, () => {
|
||||||
describe('handleQueueRecognizeFaces', () => {
|
describe('handleQueueRecognizeFaces', () => {
|
||||||
it('should queue missing assets', async () => {
|
it('should queue missing assets', async () => {
|
||||||
assetMock.getWithout.mockResolvedValue({
|
assetMock.getWithout.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
await sut.handleQueueRecognizeFaces({});
|
await sut.handleQueueRecognizeFaces({});
|
||||||
|
@ -141,13 +141,13 @@ describe(FacialRecognitionService.name, () => {
|
||||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.FACES);
|
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.FACES);
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.RECOGNIZE_FACES,
|
name: JobName.RECOGNIZE_FACES,
|
||||||
data: { id: assetEntityStub.image.id },
|
data: { id: assetStub.image.id },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should queue all assets', async () => {
|
it('should queue all assets', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
personMock.deleteAll.mockResolvedValue(5);
|
personMock.deleteAll.mockResolvedValue(5);
|
||||||
|
@ -158,24 +158,24 @@ describe(FacialRecognitionService.name, () => {
|
||||||
expect(assetMock.getAll).toHaveBeenCalled();
|
expect(assetMock.getAll).toHaveBeenCalled();
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.RECOGNIZE_FACES,
|
name: JobName.RECOGNIZE_FACES,
|
||||||
data: { id: assetEntityStub.image.id },
|
data: { id: assetStub.image.id },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('handleRecognizeFaces', () => {
|
describe('handleRecognizeFaces', () => {
|
||||||
it('should skip when no resize path', async () => {
|
it('should skip when no resize path', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath]);
|
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
||||||
await sut.handleRecognizeFaces({ id: assetEntityStub.noResizePath.id });
|
await sut.handleRecognizeFaces({ id: assetStub.noResizePath.id });
|
||||||
expect(machineLearningMock.detectFaces).not.toHaveBeenCalled();
|
expect(machineLearningMock.detectFaces).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle no results', async () => {
|
it('should handle no results', async () => {
|
||||||
machineLearningMock.detectFaces.mockResolvedValue([]);
|
machineLearningMock.detectFaces.mockResolvedValue([]);
|
||||||
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(machineLearningMock.detectFaces).toHaveBeenCalledWith({
|
expect(machineLearningMock.detectFaces).toHaveBeenCalledWith({
|
||||||
imagePath: assetEntityStub.image.resizePath,
|
imagePath: assetStub.image.resizePath,
|
||||||
});
|
});
|
||||||
expect(faceMock.create).not.toHaveBeenCalled();
|
expect(faceMock.create).not.toHaveBeenCalled();
|
||||||
expect(jobMock.queue).not.toHaveBeenCalled();
|
expect(jobMock.queue).not.toHaveBeenCalled();
|
||||||
|
@ -184,8 +184,8 @@ describe(FacialRecognitionService.name, () => {
|
||||||
it('should match existing people', async () => {
|
it('should match existing people', async () => {
|
||||||
machineLearningMock.detectFaces.mockResolvedValue([face.middle]);
|
machineLearningMock.detectFaces.mockResolvedValue([face.middle]);
|
||||||
searchMock.searchFaces.mockResolvedValue(faceSearch.oneMatch);
|
searchMock.searchFaces.mockResolvedValue(faceSearch.oneMatch);
|
||||||
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(faceMock.create).toHaveBeenCalledWith({
|
expect(faceMock.create).toHaveBeenCalledWith({
|
||||||
personId: 'person-1',
|
personId: 'person-1',
|
||||||
|
@ -204,11 +204,11 @@ describe(FacialRecognitionService.name, () => {
|
||||||
machineLearningMock.detectFaces.mockResolvedValue([face.middle]);
|
machineLearningMock.detectFaces.mockResolvedValue([face.middle]);
|
||||||
searchMock.searchFaces.mockResolvedValue(faceSearch.oneRemoteMatch);
|
searchMock.searchFaces.mockResolvedValue(faceSearch.oneRemoteMatch);
|
||||||
personMock.create.mockResolvedValue(personStub.noName);
|
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({
|
expect(faceMock.create).toHaveBeenCalledWith({
|
||||||
personId: 'person-1',
|
personId: 'person-1',
|
||||||
assetId: 'asset-id',
|
assetId: 'asset-id',
|
||||||
|
@ -254,7 +254,7 @@ describe(FacialRecognitionService.name, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip an asset without a thumbnail', async () => {
|
it('should skip an asset without a thumbnail', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath]);
|
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
||||||
|
|
||||||
await sut.handleGenerateFaceThumbnail(face.middle);
|
await sut.handleGenerateFaceThumbnail(face.middle);
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ describe(FacialRecognitionService.name, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate a thumbnail', async () => {
|
it('should generate a thumbnail', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
|
|
||||||
await sut.handleGenerateFaceThumbnail(face.middle);
|
await sut.handleGenerateFaceThumbnail(face.middle);
|
||||||
|
|
||||||
|
@ -285,7 +285,7 @@ describe(FacialRecognitionService.name, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate a thumbnail without going negative', async () => {
|
it('should generate a thumbnail without going negative', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
|
|
||||||
await sut.handleGenerateFaceThumbnail(face.start);
|
await sut.handleGenerateFaceThumbnail(face.start);
|
||||||
|
|
||||||
|
@ -302,7 +302,7 @@ describe(FacialRecognitionService.name, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate a thumbnail without overflowing', async () => {
|
it('should generate a thumbnail without overflowing', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
|
|
||||||
await sut.handleGenerateFaceThumbnail(face.end);
|
await sut.handleGenerateFaceThumbnail(face.end);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { SystemConfig } from '@app/infra/entities';
|
import { SystemConfig } from '@app/infra/entities';
|
||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
asyncTick,
|
asyncTick,
|
||||||
newAssetRepositoryMock,
|
newAssetRepositoryMock,
|
||||||
newCommunicationRepositoryMock,
|
newCommunicationRepositoryMock,
|
||||||
|
@ -300,7 +300,7 @@ describe(JobService.name, () => {
|
||||||
for (const { item, jobs } of tests) {
|
for (const { item, jobs } of tests) {
|
||||||
it(`should queue ${jobs.length} jobs when a ${item.name} job finishes successfully`, async () => {
|
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') {
|
if (item.name === JobName.GENERATE_JPEG_THUMBNAIL && item.data.source === 'upload') {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.livePhotoMotionAsset]);
|
assetMock.getByIds.mockResolvedValue([assetStub.livePhotoMotionAsset]);
|
||||||
} else {
|
} else {
|
||||||
assetMock.getByIds.mockResolvedValue([]);
|
assetMock.getByIds.mockResolvedValue([]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { AssetType, SystemConfigKey, TranscodePolicy, VideoCodec } from '@app/infra/entities';
|
import { AssetType, SystemConfigKey, TranscodePolicy, VideoCodec } from '@app/infra/entities';
|
||||||
import {
|
import {
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
newAssetRepositoryMock,
|
newAssetRepositoryMock,
|
||||||
newJobRepositoryMock,
|
newJobRepositoryMock,
|
||||||
newMediaRepositoryMock,
|
newMediaRepositoryMock,
|
||||||
|
@ -40,7 +40,7 @@ describe(MediaService.name, () => {
|
||||||
describe('handleQueueGenerateThumbnails', () => {
|
describe('handleQueueGenerateThumbnails', () => {
|
||||||
it('should queue all assets', async () => {
|
it('should queue all assets', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -50,13 +50,13 @@ describe(MediaService.name, () => {
|
||||||
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.GENERATE_JPEG_THUMBNAIL,
|
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 () => {
|
it('should queue all assets with missing resize path', async () => {
|
||||||
assetMock.getWithout.mockResolvedValue({
|
assetMock.getWithout.mockResolvedValue({
|
||||||
items: [assetEntityStub.noResizePath],
|
items: [assetStub.noResizePath],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,13 +66,13 @@ describe(MediaService.name, () => {
|
||||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.GENERATE_JPEG_THUMBNAIL,
|
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 () => {
|
it('should queue all assets with missing webp path', async () => {
|
||||||
assetMock.getWithout.mockResolvedValue({
|
assetMock.getWithout.mockResolvedValue({
|
||||||
items: [assetEntityStub.noWebpPath],
|
items: [assetStub.noWebpPath],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -82,13 +82,13 @@ describe(MediaService.name, () => {
|
||||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.GENERATE_WEBP_THUMBNAIL,
|
name: JobName.GENERATE_WEBP_THUMBNAIL,
|
||||||
data: { id: assetEntityStub.image.id },
|
data: { id: assetStub.image.id },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should queue all assets with missing thumbhash', async () => {
|
it('should queue all assets with missing thumbhash', async () => {
|
||||||
assetMock.getWithout.mockResolvedValue({
|
assetMock.getWithout.mockResolvedValue({
|
||||||
items: [assetEntityStub.noThumbhash],
|
items: [assetStub.noThumbhash],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ describe(MediaService.name, () => {
|
||||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.GENERATE_THUMBHASH_THUMBNAIL,
|
name: JobName.GENERATE_THUMBHASH_THUMBNAIL,
|
||||||
data: { id: assetEntityStub.image.id },
|
data: { id: assetStub.image.id },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -106,14 +106,14 @@ describe(MediaService.name, () => {
|
||||||
describe('handleGenerateJpegThumbnail', () => {
|
describe('handleGenerateJpegThumbnail', () => {
|
||||||
it('should skip thumbnail generation if asset not found', async () => {
|
it('should skip thumbnail generation if asset not found', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([]);
|
assetMock.getByIds.mockResolvedValue([]);
|
||||||
await sut.handleGenerateJpegThumbnail({ id: assetEntityStub.image.id });
|
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||||
expect(mediaMock.resize).not.toHaveBeenCalled();
|
expect(mediaMock.resize).not.toHaveBeenCalled();
|
||||||
expect(assetMock.save).not.toHaveBeenCalledWith();
|
expect(assetMock.save).not.toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate a thumbnail for an image', async () => {
|
it('should generate a thumbnail for an image', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
await sut.handleGenerateJpegThumbnail({ id: assetEntityStub.image.id });
|
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||||
|
|
||||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id');
|
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id');
|
||||||
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/asset-id.jpeg', {
|
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 () => {
|
it('should generate a thumbnail for a video', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleGenerateJpegThumbnail({ id: assetEntityStub.video.id });
|
await sut.handleGenerateJpegThumbnail({ id: assetStub.video.id });
|
||||||
|
|
||||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id');
|
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id');
|
||||||
expect(mediaMock.extractVideoThumbnail).toHaveBeenCalledWith(
|
expect(mediaMock.extractVideoThumbnail).toHaveBeenCalledWith(
|
||||||
|
@ -143,28 +143,28 @@ describe(MediaService.name, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should run successfully', async () => {
|
it('should run successfully', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
await sut.handleGenerateJpegThumbnail({ id: assetEntityStub.image.id });
|
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('handleGenerateWebpThumbnail', () => {
|
describe('handleGenerateWebpThumbnail', () => {
|
||||||
it('should skip thumbnail generation if asset not found', async () => {
|
it('should skip thumbnail generation if asset not found', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([]);
|
assetMock.getByIds.mockResolvedValue([]);
|
||||||
await sut.handleGenerateWebpThumbnail({ id: assetEntityStub.image.id });
|
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
|
||||||
expect(mediaMock.resize).not.toHaveBeenCalled();
|
expect(mediaMock.resize).not.toHaveBeenCalled();
|
||||||
expect(assetMock.save).not.toHaveBeenCalledWith();
|
expect(assetMock.save).not.toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip thumbnail generate if resize path is missing', async () => {
|
it('should skip thumbnail generate if resize path is missing', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath]);
|
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
||||||
await sut.handleGenerateWebpThumbnail({ id: assetEntityStub.noResizePath.id });
|
await sut.handleGenerateWebpThumbnail({ id: assetStub.noResizePath.id });
|
||||||
expect(mediaMock.resize).not.toHaveBeenCalled();
|
expect(mediaMock.resize).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate a thumbnail', async () => {
|
it('should generate a thumbnail', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
await sut.handleGenerateWebpThumbnail({ id: assetEntityStub.image.id });
|
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
|
||||||
|
|
||||||
expect(mediaMock.resize).toHaveBeenCalledWith(
|
expect(mediaMock.resize).toHaveBeenCalledWith(
|
||||||
'/uploads/user-id/thumbs/path.jpg',
|
'/uploads/user-id/thumbs/path.jpg',
|
||||||
|
@ -178,22 +178,22 @@ describe(MediaService.name, () => {
|
||||||
describe('handleGenerateThumbhashThumbnail', () => {
|
describe('handleGenerateThumbhashThumbnail', () => {
|
||||||
it('should skip thumbhash generation if asset not found', async () => {
|
it('should skip thumbhash generation if asset not found', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([]);
|
assetMock.getByIds.mockResolvedValue([]);
|
||||||
await sut.handleGenerateThumbhashThumbnail({ id: assetEntityStub.image.id });
|
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.image.id });
|
||||||
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
|
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip thumbhash generation if resize path is missing', async () => {
|
it('should skip thumbhash generation if resize path is missing', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.noResizePath]);
|
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
||||||
await sut.handleGenerateThumbhashThumbnail({ id: assetEntityStub.noResizePath.id });
|
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.noResizePath.id });
|
||||||
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
|
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate a thumbhash', async () => {
|
it('should generate a thumbhash', async () => {
|
||||||
const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8');
|
const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8');
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
mediaMock.generateThumbhash.mockResolvedValue(thumbhashBuffer);
|
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(mediaMock.generateThumbhash).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.jpg');
|
||||||
expect(assetMock.save).toHaveBeenCalledWith({ id: 'asset-id', thumbhash: thumbhashBuffer });
|
expect(assetMock.save).toHaveBeenCalledWith({ id: 'asset-id', thumbhash: thumbhashBuffer });
|
||||||
|
@ -203,7 +203,7 @@ describe(MediaService.name, () => {
|
||||||
describe('handleQueueVideoConversion', () => {
|
describe('handleQueueVideoConversion', () => {
|
||||||
it('should queue all video assets', async () => {
|
it('should queue all video assets', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.video],
|
items: [assetStub.video],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -213,13 +213,13 @@ describe(MediaService.name, () => {
|
||||||
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.VIDEO_CONVERSION,
|
name: JobName.VIDEO_CONVERSION,
|
||||||
data: { id: assetEntityStub.video.id },
|
data: { id: assetStub.video.id },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should queue all video assets without encoded videos', async () => {
|
it('should queue all video assets without encoded videos', async () => {
|
||||||
assetMock.getWithout.mockResolvedValue({
|
assetMock.getWithout.mockResolvedValue({
|
||||||
items: [assetEntityStub.video],
|
items: [assetStub.video],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -229,35 +229,35 @@ describe(MediaService.name, () => {
|
||||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.ENCODED_VIDEO);
|
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.ENCODED_VIDEO);
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.VIDEO_CONVERSION,
|
name: JobName.VIDEO_CONVERSION,
|
||||||
data: { id: assetEntityStub.video.id },
|
data: { id: assetStub.video.id },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('handleVideoConversion', () => {
|
describe('handleVideoConversion', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip transcoding if asset not found', async () => {
|
it('should skip transcoding if asset not found', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([]);
|
assetMock.getByIds.mockResolvedValue([]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.probe).not.toHaveBeenCalled();
|
expect(mediaMock.probe).not.toHaveBeenCalled();
|
||||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip transcoding if non-video asset', async () => {
|
it('should skip transcoding if non-video asset', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.image.id });
|
await sut.handleVideoConversion({ id: assetStub.image.id });
|
||||||
expect(mediaMock.probe).not.toHaveBeenCalled();
|
expect(mediaMock.probe).not.toHaveBeenCalled();
|
||||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should transcode the longest stream', async () => {
|
it('should transcode the longest stream', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.multipleVideoStreams);
|
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(mediaMock.probe).toHaveBeenCalledWith('/original/path.ext');
|
||||||
expect(configMock.load).toHaveBeenCalled();
|
expect(configMock.load).toHaveBeenCalled();
|
||||||
|
@ -282,23 +282,23 @@ describe(MediaService.name, () => {
|
||||||
|
|
||||||
it('should skip a video without any streams', async () => {
|
it('should skip a video without any streams', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.noVideoStreams);
|
mediaMock.probe.mockResolvedValue(probeStub.noVideoStreams);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip a video without any height', async () => {
|
it('should skip a video without any height', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.noHeight);
|
mediaMock.probe.mockResolvedValue(probeStub.noHeight);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should transcode when set to all', async () => {
|
it('should transcode when set to all', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.multipleVideoStreams);
|
mediaMock.probe.mockResolvedValue(probeStub.multipleVideoStreams);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.ALL }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.ALL }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'upload/encoded-video/user-id/asset-id.mp4',
|
||||||
|
@ -320,7 +320,7 @@ describe(MediaService.name, () => {
|
||||||
it('should transcode when optimal and too big', async () => {
|
it('should transcode when optimal and too big', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
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(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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_TRANSCODE, value: TranscodePolicy.ALL },
|
||||||
{ key: SystemConfigKey.FFMPEG_TARGET_RESOLUTION, value: 'original' },
|
{ 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(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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 () => {
|
it('should transcode with alternate scaling video is vertical', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.videoStreamVertical2160p);
|
mediaMock.probe.mockResolvedValue(probeStub.videoStreamVertical2160p);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'upload/encoded-video/user-id/asset-id.mp4',
|
||||||
|
@ -392,8 +392,8 @@ describe(MediaService.name, () => {
|
||||||
it('should transcode when audio doesnt match target', async () => {
|
it('should transcode when audio doesnt match target', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.audioStreamMp3);
|
mediaMock.probe.mockResolvedValue(probeStub.audioStreamMp3);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'upload/encoded-video/user-id/asset-id.mp4',
|
||||||
|
@ -416,8 +416,8 @@ describe(MediaService.name, () => {
|
||||||
it('should transcode when container doesnt match target', async () => {
|
it('should transcode when container doesnt match target', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'upload/encoded-video/user-id/asset-id.mp4',
|
||||||
|
@ -440,32 +440,32 @@ describe(MediaService.name, () => {
|
||||||
it('should not transcode an invalid transcode value', async () => {
|
it('should not transcode an invalid transcode value', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: 'invalid' }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: 'invalid' }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not transcode if transcoding is disabled', async () => {
|
it('should not transcode if transcoding is disabled', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.DISABLED }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.DISABLED }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not transcode if target codec is invalid', async () => {
|
it('should not transcode if target codec is invalid', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: 'invalid' }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: 'invalid' }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
expect(mediaMock.transcode).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set max bitrate if above 0', async () => {
|
it('should set max bitrate if above 0', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '4500k' }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '4500k' }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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_MAX_BITRATE, value: '4500k' },
|
||||||
{ key: SystemConfigKey.FFMPEG_TWO_PASS, value: true },
|
{ key: SystemConfigKey.FFMPEG_TWO_PASS, value: true },
|
||||||
]);
|
]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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 () => {
|
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);
|
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TWO_PASS, value: true }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TWO_PASS, value: true }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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_TWO_PASS, value: true },
|
||||||
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.VP9 },
|
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.VP9 },
|
||||||
]);
|
]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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_TARGET_VIDEO_CODEC, value: VideoCodec.VP9 },
|
||||||
{ key: SystemConfigKey.FFMPEG_PRESET, value: 'slow' },
|
{ key: SystemConfigKey.FFMPEG_PRESET, value: 'slow' },
|
||||||
]);
|
]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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_TARGET_VIDEO_CODEC, value: VideoCodec.VP9 },
|
||||||
{ key: SystemConfigKey.FFMPEG_PRESET, value: 'invalid' },
|
{ key: SystemConfigKey.FFMPEG_PRESET, value: 'invalid' },
|
||||||
]);
|
]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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_TARGET_VIDEO_CODEC, value: VideoCodec.VP9 },
|
||||||
{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 },
|
{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 },
|
||||||
]);
|
]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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 () => {
|
it('should disable thread pooling for h264 if thread limit is above 0', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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 () => {
|
it('should omit thread flags for h264 if thread limit is at or below 0', async () => {
|
||||||
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
|
||||||
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_THREADS, value: 0 }]);
|
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_THREADS, value: 0 }]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'upload/encoded-video/user-id/asset-id.mp4',
|
||||||
|
@ -715,8 +715,8 @@ describe(MediaService.name, () => {
|
||||||
{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 },
|
{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 },
|
||||||
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC },
|
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC },
|
||||||
]);
|
]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'upload/encoded-video/user-id/asset-id.mp4',
|
||||||
|
@ -745,8 +745,8 @@ describe(MediaService.name, () => {
|
||||||
{ key: SystemConfigKey.FFMPEG_THREADS, value: 0 },
|
{ key: SystemConfigKey.FFMPEG_THREADS, value: 0 },
|
||||||
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC },
|
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC },
|
||||||
]);
|
]);
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
await sut.handleVideoConversion({ id: assetEntityStub.video.id });
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||||
'/original/path.ext',
|
'/original/path.ext',
|
||||||
'upload/encoded-video/user-id/asset-id.mp4',
|
'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 { constants } from 'fs/promises';
|
||||||
import { IAssetRepository, WithoutProperty, WithProperty } from '../asset';
|
import { IAssetRepository, WithoutProperty, WithProperty } from '../asset';
|
||||||
import { IJobRepository, JobName } from '../job';
|
import { IJobRepository, JobName } from '../job';
|
||||||
|
@ -25,7 +25,7 @@ describe(MetadataService.name, () => {
|
||||||
|
|
||||||
describe('handleQueueSidecar', () => {
|
describe('handleQueueSidecar', () => {
|
||||||
it('should queue assets with sidecar files', async () => {
|
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 });
|
await sut.handleQueueSidecar({ force: true });
|
||||||
|
|
||||||
|
@ -33,12 +33,12 @@ describe(MetadataService.name, () => {
|
||||||
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.SIDECAR_SYNC,
|
name: JobName.SIDECAR_SYNC,
|
||||||
data: { id: assetEntityStub.sidecar.id },
|
data: { id: assetStub.sidecar.id },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should queue assets without sidecar files', async () => {
|
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 });
|
await sut.handleQueueSidecar({ force: false });
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ describe(MetadataService.name, () => {
|
||||||
expect(assetMock.getWith).not.toHaveBeenCalled();
|
expect(assetMock.getWith).not.toHaveBeenCalled();
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.SIDECAR_DISCOVERY,
|
name: JobName.SIDECAR_DISCOVERY,
|
||||||
data: { id: assetEntityStub.image.id },
|
data: { id: assetStub.image.id },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -59,44 +59,44 @@ describe(MetadataService.name, () => {
|
||||||
|
|
||||||
describe('handleSidecarDiscovery', () => {
|
describe('handleSidecarDiscovery', () => {
|
||||||
it('should skip hidden assets', async () => {
|
it('should skip hidden assets', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.livePhotoMotionAsset]);
|
assetMock.getByIds.mockResolvedValue([assetStub.livePhotoMotionAsset]);
|
||||||
await sut.handleSidecarDiscovery({ id: assetEntityStub.livePhotoMotionAsset.id });
|
await sut.handleSidecarDiscovery({ id: assetStub.livePhotoMotionAsset.id });
|
||||||
expect(storageMock.checkFileExists).not.toHaveBeenCalled();
|
expect(storageMock.checkFileExists).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should skip assets with a sidecar path', async () => {
|
it('should skip assets with a sidecar path', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.sidecar]);
|
assetMock.getByIds.mockResolvedValue([assetStub.sidecar]);
|
||||||
await sut.handleSidecarDiscovery({ id: assetEntityStub.sidecar.id });
|
await sut.handleSidecarDiscovery({ id: assetStub.sidecar.id });
|
||||||
expect(storageMock.checkFileExists).not.toHaveBeenCalled();
|
expect(storageMock.checkFileExists).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do nothing when a sidecar is not found ', async () => {
|
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);
|
storageMock.checkFileExists.mockResolvedValue(false);
|
||||||
await sut.handleSidecarDiscovery({ id: assetEntityStub.image.id });
|
await sut.handleSidecarDiscovery({ id: assetStub.image.id });
|
||||||
expect(assetMock.save).not.toHaveBeenCalled();
|
expect(assetMock.save).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update a image asset when a sidecar is found', async () => {
|
it('should update a image asset when a sidecar is found', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
assetMock.save.mockResolvedValue(assetStub.image);
|
||||||
storageMock.checkFileExists.mockResolvedValue(true);
|
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(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.jpg.xmp', constants.R_OK);
|
||||||
expect(assetMock.save).toHaveBeenCalledWith({
|
expect(assetMock.save).toHaveBeenCalledWith({
|
||||||
id: assetEntityStub.image.id,
|
id: assetStub.image.id,
|
||||||
sidecarPath: '/original/path.jpg.xmp',
|
sidecarPath: '/original/path.jpg.xmp',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update a video asset when a sidecar is found', async () => {
|
it('should update a video asset when a sidecar is found', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.video]);
|
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||||
assetMock.save.mockResolvedValue(assetEntityStub.video);
|
assetMock.save.mockResolvedValue(assetStub.video);
|
||||||
storageMock.checkFileExists.mockResolvedValue(true);
|
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(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.R_OK);
|
||||||
expect(assetMock.save).toHaveBeenCalledWith({
|
expect(assetMock.save).toHaveBeenCalledWith({
|
||||||
id: assetEntityStub.image.id,
|
id: assetStub.image.id,
|
||||||
sidecarPath: '/original/path.ext.xmp',
|
sidecarPath: '/original/path.ext.xmp',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
authStub,
|
authStub,
|
||||||
faceStub,
|
faceStub,
|
||||||
newJobRepositoryMock,
|
newJobRepositoryMock,
|
||||||
|
@ -112,7 +112,7 @@ describe(PersonService.name, () => {
|
||||||
|
|
||||||
describe('getAssets', () => {
|
describe('getAssets', () => {
|
||||||
it("should return a person's assets", async () => {
|
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');
|
await sut.getAssets(authStub.admin, 'person-1');
|
||||||
expect(personMock.getAssets).toHaveBeenCalledWith('admin_id', '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 () => {
|
it("should update a person's name", async () => {
|
||||||
personMock.getById.mockResolvedValue(personStub.noName);
|
personMock.getById.mockResolvedValue(personStub.noName);
|
||||||
personMock.update.mockResolvedValue(personStub.withName);
|
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);
|
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(personMock.update).toHaveBeenCalledWith({ id: 'person-1', name: 'Person 1' });
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.SEARCH_INDEX_ASSET,
|
name: JobName.SEARCH_INDEX_ASSET,
|
||||||
data: { ids: [assetEntityStub.image.id] },
|
data: { ids: [assetStub.image.id] },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update a person visibility', async () => {
|
it('should update a person visibility', async () => {
|
||||||
personMock.getById.mockResolvedValue(personStub.hidden);
|
personMock.getById.mockResolvedValue(personStub.hidden);
|
||||||
personMock.update.mockResolvedValue(personStub.withName);
|
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);
|
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(personMock.update).toHaveBeenCalledWith({ id: 'person-1', isHidden: false });
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.SEARCH_INDEX_ASSET,
|
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 () => {
|
it('should delete conflicting faces before merging', async () => {
|
||||||
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
||||||
personMock.getById.mockResolvedValue(personStub.mergePerson);
|
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([
|
await expect(sut.mergePerson(authStub.admin, 'person-1', { ids: ['person-2'] })).resolves.toEqual([
|
||||||
{ id: 'person-2', success: true },
|
{ id: 'person-2', success: true },
|
||||||
|
@ -252,7 +252,7 @@ describe(PersonService.name, () => {
|
||||||
|
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
name: JobName.SEARCH_REMOVE_FACE,
|
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 () => {
|
it('should handle an error reassigning faces', async () => {
|
||||||
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
personMock.getById.mockResolvedValue(personStub.primaryPerson);
|
||||||
personMock.getById.mockResolvedValue(personStub.mergePerson);
|
personMock.getById.mockResolvedValue(personStub.mergePerson);
|
||||||
personMock.prepareReassignFaces.mockResolvedValue([assetEntityStub.image.id]);
|
personMock.prepareReassignFaces.mockResolvedValue([assetStub.image.id]);
|
||||||
personMock.reassignFaces.mockRejectedValue(new Error('update failed'));
|
personMock.reassignFaces.mockRejectedValue(new Error('update failed'));
|
||||||
|
|
||||||
await expect(sut.mergePerson(authStub.admin, 'person-1', { ids: ['person-2'] })).resolves.toEqual([
|
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 { ConfigService } from '@nestjs/config';
|
||||||
import {
|
import {
|
||||||
albumStub,
|
albumStub,
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
asyncTick,
|
asyncTick,
|
||||||
authStub,
|
authStub,
|
||||||
faceStub,
|
faceStub,
|
||||||
|
@ -192,14 +192,14 @@ describe(SearchService.name, () => {
|
||||||
|
|
||||||
it('should index all the assets', async () => {
|
it('should index all the assets', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
await sut.handleIndexAssets();
|
await sut.handleIndexAssets();
|
||||||
|
|
||||||
expect(searchMock.importAssets.mock.calls).toEqual([
|
expect(searchMock.importAssets.mock.calls).toEqual([
|
||||||
[[assetEntityStub.image], false],
|
[[assetStub.image], false],
|
||||||
[[], true],
|
[[], true],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -217,11 +217,11 @@ describe(SearchService.name, () => {
|
||||||
describe('handleIndexAsset', () => {
|
describe('handleIndexAsset', () => {
|
||||||
it('should skip if search is disabled', () => {
|
it('should skip if search is disabled', () => {
|
||||||
const sut = makeSut('false');
|
const sut = makeSut('false');
|
||||||
sut.handleIndexAsset({ ids: [assetEntityStub.image.id] });
|
sut.handleIndexAsset({ ids: [assetStub.image.id] });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should index the asset', () => {
|
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 () => {
|
it('should flush queued asset updates', async () => {
|
||||||
assetMock.getByIds.mockResolvedValue([assetEntityStub.image]);
|
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||||
|
|
||||||
sut.handleIndexAsset({ ids: ['asset1'] });
|
sut.handleIndexAsset({ ids: ['asset1'] });
|
||||||
|
|
||||||
|
@ -376,7 +376,7 @@ describe(SearchService.name, () => {
|
||||||
await asyncTick(4);
|
await asyncTick(4);
|
||||||
|
|
||||||
expect(assetMock.getByIds).toHaveBeenCalledWith(['asset1']);
|
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 () => {
|
it('should flush queued asset deletes', async () => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
|
import { BadRequestException, ForbiddenException } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
albumStub,
|
albumStub,
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
authStub,
|
authStub,
|
||||||
IAccessRepositoryMock,
|
IAccessRepositoryMock,
|
||||||
newAccessRepositoryMock,
|
newAccessRepositoryMock,
|
||||||
|
@ -136,20 +136,20 @@ describe(SharedLinkService.name, () => {
|
||||||
|
|
||||||
await sut.create(authStub.admin, {
|
await sut.create(authStub.admin, {
|
||||||
type: SharedLinkType.INDIVIDUAL,
|
type: SharedLinkType.INDIVIDUAL,
|
||||||
assetIds: [assetEntityStub.image.id],
|
assetIds: [assetStub.image.id],
|
||||||
showExif: true,
|
showExif: true,
|
||||||
allowDownload: true,
|
allowDownload: true,
|
||||||
allowUpload: 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({
|
expect(shareMock.create).toHaveBeenCalledWith({
|
||||||
type: SharedLinkType.INDIVIDUAL,
|
type: SharedLinkType.INDIVIDUAL,
|
||||||
userId: authStub.admin.id,
|
userId: authStub.admin.id,
|
||||||
albumId: null,
|
albumId: null,
|
||||||
allowDownload: true,
|
allowDownload: true,
|
||||||
allowUpload: true,
|
allowUpload: true,
|
||||||
assets: [{ id: assetEntityStub.image.id }],
|
assets: [{ id: assetStub.image.id }],
|
||||||
description: null,
|
description: null,
|
||||||
expiresAt: null,
|
expiresAt: null,
|
||||||
showExif: true,
|
showExif: true,
|
||||||
|
@ -211,9 +211,9 @@ describe(SharedLinkService.name, () => {
|
||||||
when(accessMock.asset.hasOwnerAccess).calledWith(authStub.admin.id, 'asset-3').mockResolvedValue(true);
|
when(accessMock.asset.hasOwnerAccess).calledWith(authStub.admin.id, 'asset-3').mockResolvedValue(true);
|
||||||
|
|
||||||
await expect(
|
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([
|
).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-2', success: false, error: AssetIdErrorReason.NO_PERMISSION },
|
||||||
{ assetId: 'asset-3', success: true },
|
{ assetId: 'asset-3', success: true },
|
||||||
]);
|
]);
|
||||||
|
@ -221,7 +221,7 @@ describe(SharedLinkService.name, () => {
|
||||||
expect(accessMock.asset.hasOwnerAccess).toHaveBeenCalledTimes(2);
|
expect(accessMock.asset.hasOwnerAccess).toHaveBeenCalledTimes(2);
|
||||||
expect(shareMock.update).toHaveBeenCalledWith({
|
expect(shareMock.update).toHaveBeenCalledWith({
|
||||||
...sharedLinkStub.individual,
|
...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);
|
shareMock.create.mockResolvedValue(sharedLinkStub.individual);
|
||||||
|
|
||||||
await expect(
|
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([
|
).resolves.toEqual([
|
||||||
{ assetId: assetEntityStub.image.id, success: true },
|
{ assetId: assetStub.image.id, success: true },
|
||||||
{ assetId: 'asset-2', success: false, error: AssetIdErrorReason.NOT_FOUND },
|
{ assetId: 'asset-2', success: false, error: AssetIdErrorReason.NOT_FOUND },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { AssetEntity } from '@app/infra/entities';
|
import { AssetEntity } from '@app/infra/entities';
|
||||||
import {
|
import {
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
newAssetRepositoryMock,
|
newAssetRepositoryMock,
|
||||||
newJobRepositoryMock,
|
newJobRepositoryMock,
|
||||||
newMachineLearningRepositoryMock,
|
newMachineLearningRepositoryMock,
|
||||||
|
@ -41,29 +41,25 @@ describe(SmartInfoService.name, () => {
|
||||||
describe('handleQueueObjectTagging', () => {
|
describe('handleQueueObjectTagging', () => {
|
||||||
it('should queue the assets without tags', async () => {
|
it('should queue the assets without tags', async () => {
|
||||||
assetMock.getWithout.mockResolvedValue({
|
assetMock.getWithout.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
await sut.handleQueueObjectTagging({ force: false });
|
await sut.handleQueueObjectTagging({ force: false });
|
||||||
|
|
||||||
expect(jobMock.queue.mock.calls).toEqual([
|
expect(jobMock.queue.mock.calls).toEqual([[{ name: JobName.CLASSIFY_IMAGE, data: { id: assetStub.image.id } }]]);
|
||||||
[{ name: JobName.CLASSIFY_IMAGE, data: { id: assetEntityStub.image.id } }],
|
|
||||||
]);
|
|
||||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.OBJECT_TAGS);
|
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.OBJECT_TAGS);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should queue all the assets', async () => {
|
it('should queue all the assets', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
await sut.handleQueueObjectTagging({ force: true });
|
await sut.handleQueueObjectTagging({ force: true });
|
||||||
|
|
||||||
expect(jobMock.queue.mock.calls).toEqual([
|
expect(jobMock.queue.mock.calls).toEqual([[{ name: JobName.CLASSIFY_IMAGE, data: { id: assetStub.image.id } }]]);
|
||||||
[{ name: JobName.CLASSIFY_IMAGE, data: { id: assetEntityStub.image.id } }],
|
|
||||||
]);
|
|
||||||
expect(assetMock.getAll).toHaveBeenCalled();
|
expect(assetMock.getAll).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -104,25 +100,25 @@ describe(SmartInfoService.name, () => {
|
||||||
describe('handleQueueEncodeClip', () => {
|
describe('handleQueueEncodeClip', () => {
|
||||||
it('should queue the assets without clip embeddings', async () => {
|
it('should queue the assets without clip embeddings', async () => {
|
||||||
assetMock.getWithout.mockResolvedValue({
|
assetMock.getWithout.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
await sut.handleQueueEncodeClip({ force: 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);
|
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.CLIP_ENCODING);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should queue all the assets', async () => {
|
it('should queue all the assets', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
await sut.handleQueueEncodeClip({ force: true });
|
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();
|
expect(assetMock.getAll).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import {
|
import {
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
newAssetRepositoryMock,
|
newAssetRepositoryMock,
|
||||||
newStorageRepositoryMock,
|
newStorageRepositoryMock,
|
||||||
newSystemConfigRepositoryMock,
|
newSystemConfigRepositoryMock,
|
||||||
newUserRepositoryMock,
|
newUserRepositoryMock,
|
||||||
userEntityStub,
|
userStub,
|
||||||
} from '@test';
|
} from '@test';
|
||||||
import { when } from 'jest-when';
|
import { when } from 'jest-when';
|
||||||
import { StorageTemplateService } from '.';
|
import { StorageTemplateService } from '.';
|
||||||
|
@ -49,11 +49,11 @@ describe(StorageTemplateService.name, () => {
|
||||||
|
|
||||||
it('should handle an asset with a duplicate destination', async () => {
|
it('should handle an asset with a duplicate destination', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
assetMock.save.mockResolvedValue(assetStub.image);
|
||||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||||
|
|
||||||
when(storageMock.checkFileExists)
|
when(storageMock.checkFileExists)
|
||||||
.calledWith('upload/library/user-id/2023/2023-02-23/asset-id.jpg')
|
.calledWith('upload/library/user-id/2023/2023-02-23/asset-id.jpg')
|
||||||
|
@ -68,7 +68,7 @@ describe(StorageTemplateService.name, () => {
|
||||||
expect(assetMock.getAll).toHaveBeenCalled();
|
expect(assetMock.getAll).toHaveBeenCalled();
|
||||||
expect(storageMock.checkFileExists).toHaveBeenCalledTimes(2);
|
expect(storageMock.checkFileExists).toHaveBeenCalledTimes(2);
|
||||||
expect(assetMock.save).toHaveBeenCalledWith({
|
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',
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
||||||
});
|
});
|
||||||
expect(userMock.getList).toHaveBeenCalled();
|
expect(userMock.getList).toHaveBeenCalled();
|
||||||
|
@ -78,13 +78,13 @@ describe(StorageTemplateService.name, () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
...assetEntityStub.image,
|
...assetStub.image,
|
||||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||||
|
|
||||||
await sut.handleMigration();
|
await sut.handleMigration();
|
||||||
|
|
||||||
|
@ -98,13 +98,13 @@ describe(StorageTemplateService.name, () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
...assetEntityStub.image,
|
...assetStub.image,
|
||||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||||
|
|
||||||
await sut.handleMigration();
|
await sut.handleMigration();
|
||||||
|
|
||||||
|
@ -116,11 +116,11 @@ describe(StorageTemplateService.name, () => {
|
||||||
|
|
||||||
it('should move an asset', async () => {
|
it('should move an asset', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
assetMock.save.mockResolvedValue(assetStub.image);
|
||||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||||
|
|
||||||
await sut.handleMigration();
|
await sut.handleMigration();
|
||||||
|
|
||||||
|
@ -130,18 +130,18 @@ describe(StorageTemplateService.name, () => {
|
||||||
'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
||||||
);
|
);
|
||||||
expect(assetMock.save).toHaveBeenCalledWith({
|
expect(assetMock.save).toHaveBeenCalledWith({
|
||||||
id: assetEntityStub.image.id,
|
id: assetStub.image.id,
|
||||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use the user storage label', async () => {
|
it('should use the user storage label', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
assetMock.save.mockResolvedValue(assetStub.image);
|
||||||
userMock.getList.mockResolvedValue([userEntityStub.storageLabel]);
|
userMock.getList.mockResolvedValue([userStub.storageLabel]);
|
||||||
|
|
||||||
await sut.handleMigration();
|
await sut.handleMigration();
|
||||||
|
|
||||||
|
@ -151,18 +151,18 @@ describe(StorageTemplateService.name, () => {
|
||||||
'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
|
'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
|
||||||
);
|
);
|
||||||
expect(assetMock.save).toHaveBeenCalledWith({
|
expect(assetMock.save).toHaveBeenCalledWith({
|
||||||
id: assetEntityStub.image.id,
|
id: assetStub.image.id,
|
||||||
originalPath: 'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
|
originalPath: 'upload/library/label-1/2023/2023-02-23/asset-id.jpg',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not update the database if the move fails', async () => {
|
it('should not update the database if the move fails', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
storageMock.moveFile.mockRejectedValue(new Error('Read only system'));
|
storageMock.moveFile.mockRejectedValue(new Error('Read only system'));
|
||||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||||
|
|
||||||
await sut.handleMigration();
|
await sut.handleMigration();
|
||||||
|
|
||||||
|
@ -176,17 +176,17 @@ describe(StorageTemplateService.name, () => {
|
||||||
|
|
||||||
it('should move the asset back if the database fails', async () => {
|
it('should move the asset back if the database fails', async () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [assetEntityStub.image],
|
items: [assetStub.image],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
assetMock.save.mockRejectedValue('Connection Error!');
|
assetMock.save.mockRejectedValue('Connection Error!');
|
||||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||||
|
|
||||||
await sut.handleMigration();
|
await sut.handleMigration();
|
||||||
|
|
||||||
expect(assetMock.getAll).toHaveBeenCalled();
|
expect(assetMock.getAll).toHaveBeenCalled();
|
||||||
expect(assetMock.save).toHaveBeenCalledWith({
|
expect(assetMock.save).toHaveBeenCalledWith({
|
||||||
id: assetEntityStub.image.id,
|
id: assetStub.image.id,
|
||||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg',
|
||||||
});
|
});
|
||||||
expect(storageMock.moveFile.mock.calls).toEqual([
|
expect(storageMock.moveFile.mock.calls).toEqual([
|
||||||
|
@ -199,15 +199,15 @@ describe(StorageTemplateService.name, () => {
|
||||||
assetMock.getAll.mockResolvedValue({
|
assetMock.getAll.mockResolvedValue({
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
...assetEntityStub.image,
|
...assetStub.image,
|
||||||
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg',
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
});
|
});
|
||||||
assetMock.save.mockResolvedValue(assetEntityStub.image);
|
assetMock.save.mockResolvedValue(assetStub.image);
|
||||||
userMock.getList.mockResolvedValue([userEntityStub.user1]);
|
userMock.getList.mockResolvedValue([userStub.user1]);
|
||||||
|
|
||||||
await sut.handleMigration();
|
await sut.handleMigration();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { TagType } from '@app/infra/entities';
|
import { TagType } from '@app/infra/entities';
|
||||||
import { BadRequestException } from '@nestjs/common';
|
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 { when } from 'jest-when';
|
||||||
import { AssetIdErrorReason } from '../asset';
|
import { AssetIdErrorReason } from '../asset';
|
||||||
import { ITagRepository } from './tag.repository';
|
import { ITagRepository } from './tag.repository';
|
||||||
|
@ -107,7 +107,7 @@ describe(TagService.name, () => {
|
||||||
|
|
||||||
it('should get the assets for a tag', async () => {
|
it('should get the assets for a tag', async () => {
|
||||||
tagMock.getById.mockResolvedValue(tagStub.tag1);
|
tagMock.getById.mockResolvedValue(tagStub.tag1);
|
||||||
tagMock.getAssets.mockResolvedValue([assetEntityStub.image]);
|
tagMock.getAssets.mockResolvedValue([assetStub.image]);
|
||||||
await sut.getAssets(authStub.admin, 'tag-1');
|
await sut.getAssets(authStub.admin, 'tag-1');
|
||||||
expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1');
|
expect(tagMock.getById).toHaveBeenCalledWith(authStub.admin.id, 'tag-1');
|
||||||
expect(tagMock.getAssets).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 { AlbumResponseDto, AuthUserDto, mapUser } from '@app/domain';
|
||||||
import { AlbumEntity, UserEntity } from '@app/infra/entities';
|
import { AlbumEntity, UserEntity } from '@app/infra/entities';
|
||||||
import { ForbiddenException, NotFoundException } from '@nestjs/common';
|
import { ForbiddenException, NotFoundException } from '@nestjs/common';
|
||||||
import { userEntityStub } from '@test';
|
import { userStub } from '@test';
|
||||||
import { IAlbumRepository } from './album-repository';
|
import { IAlbumRepository } from './album-repository';
|
||||||
import { AlbumService } from './album.service';
|
import { AlbumService } from './album.service';
|
||||||
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
||||||
|
@ -61,11 +61,11 @@ describe('Album service', () => {
|
||||||
albumEntity.albumThumbnailAssetId = null;
|
albumEntity.albumThumbnailAssetId = null;
|
||||||
albumEntity.sharedUsers = [
|
albumEntity.sharedUsers = [
|
||||||
{
|
{
|
||||||
...userEntityStub.user1,
|
...userStub.user1,
|
||||||
id: authUser.id,
|
id: authUser.id,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...userEntityStub.user1,
|
...userStub.user1,
|
||||||
id: sharedAlbumSharedAlsoWithId,
|
id: sharedAlbumSharedAlsoWithId,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ICryptoRepository, IJobRepository, IStorageRepository, JobName } from '
|
||||||
import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
|
import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
|
||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
assetEntityStub,
|
assetStub,
|
||||||
authStub,
|
authStub,
|
||||||
fileStub,
|
fileStub,
|
||||||
IAccessRepositoryMock,
|
IAccessRepositoryMock,
|
||||||
|
@ -132,11 +132,11 @@ describe('AssetService', () => {
|
||||||
sut = new AssetService(accessMock, assetRepositoryMock, a, cryptoMock, jobMock, storageMock);
|
sut = new AssetService(accessMock, assetRepositoryMock, a, cryptoMock, jobMock, storageMock);
|
||||||
|
|
||||||
when(assetRepositoryMock.get)
|
when(assetRepositoryMock.get)
|
||||||
.calledWith(assetEntityStub.livePhotoStillAsset.id)
|
.calledWith(assetStub.livePhotoStillAsset.id)
|
||||||
.mockResolvedValue(assetEntityStub.livePhotoStillAsset);
|
.mockResolvedValue(assetStub.livePhotoStillAsset);
|
||||||
when(assetRepositoryMock.get)
|
when(assetRepositoryMock.get)
|
||||||
.calledWith(assetEntityStub.livePhotoMotionAsset.id)
|
.calledWith(assetStub.livePhotoMotionAsset.id)
|
||||||
.mockResolvedValue(assetEntityStub.livePhotoMotionAsset);
|
.mockResolvedValue(assetStub.livePhotoMotionAsset);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('uploadFile', () => {
|
describe('uploadFile', () => {
|
||||||
|
@ -185,8 +185,8 @@ describe('AssetService', () => {
|
||||||
const error = new QueryFailedError('', [], '');
|
const error = new QueryFailedError('', [], '');
|
||||||
(error as any).constraint = 'UQ_userid_checksum';
|
(error as any).constraint = 'UQ_userid_checksum';
|
||||||
|
|
||||||
assetRepositoryMock.create.mockResolvedValueOnce(assetEntityStub.livePhotoMotionAsset);
|
assetRepositoryMock.create.mockResolvedValueOnce(assetStub.livePhotoMotionAsset);
|
||||||
assetRepositoryMock.create.mockResolvedValueOnce(assetEntityStub.livePhotoStillAsset);
|
assetRepositoryMock.create.mockResolvedValueOnce(assetStub.livePhotoStillAsset);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
sut.uploadFile(authStub.user1, dto, fileStub.livePhotoStill, fileStub.livePhotoMotion),
|
sut.uploadFile(authStub.user1, dto, fileStub.livePhotoStill, fileStub.livePhotoMotion),
|
||||||
|
@ -199,10 +199,10 @@ describe('AssetService', () => {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: JobName.METADATA_EXTRACTION,
|
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 () => {
|
it('should delete a live photo', async () => {
|
||||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||||
|
|
||||||
await expect(sut.deleteAll(authStub.user1, { ids: [assetEntityStub.livePhotoStillAsset.id] })).resolves.toEqual([
|
await expect(sut.deleteAll(authStub.user1, { ids: [assetStub.livePhotoStillAsset.id] })).resolves.toEqual([
|
||||||
{ id: assetEntityStub.livePhotoStillAsset.id, status: 'SUCCESS' },
|
{ id: assetStub.livePhotoStillAsset.id, status: 'SUCCESS' },
|
||||||
{ id: assetEntityStub.livePhotoMotionAsset.id, status: 'SUCCESS' },
|
{ id: assetStub.livePhotoMotionAsset.id, status: 'SUCCESS' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||||
|
@ -373,7 +373,7 @@ describe('AssetService', () => {
|
||||||
|
|
||||||
describe('importFile', () => {
|
describe('importFile', () => {
|
||||||
it('should handle a file import', async () => {
|
it('should handle a file import', async () => {
|
||||||
assetRepositoryMock.create.mockResolvedValue(assetEntityStub.image);
|
assetRepositoryMock.create.mockResolvedValue(assetStub.image);
|
||||||
storageMock.checkFileExists.mockResolvedValue(true);
|
storageMock.checkFileExists.mockResolvedValue(true);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -392,7 +392,7 @@ describe('AssetService', () => {
|
||||||
(error as any).constraint = 'UQ_userid_checksum';
|
(error as any).constraint = 'UQ_userid_checksum';
|
||||||
|
|
||||||
assetRepositoryMock.create.mockRejectedValue(error);
|
assetRepositoryMock.create.mockRejectedValue(error);
|
||||||
assetRepositoryMock.getAssetsByChecksums.mockResolvedValue([assetEntityStub.image]);
|
assetRepositoryMock.getAssetsByChecksums.mockResolvedValue([assetStub.image]);
|
||||||
storageMock.checkFileExists.mockResolvedValue(true);
|
storageMock.checkFileExists.mockResolvedValue(true);
|
||||||
cryptoMock.hashFile.mockResolvedValue(Buffer.from('file hash', 'utf8'));
|
cryptoMock.hashFile.mockResolvedValue(Buffer.from('file hash', 'utf8'));
|
||||||
|
|
||||||
|
@ -411,36 +411,36 @@ describe('AssetService', () => {
|
||||||
describe('getAssetById', () => {
|
describe('getAssetById', () => {
|
||||||
it('should allow owner access', async () => {
|
it('should allow owner access', async () => {
|
||||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
accessMock.asset.hasOwnerAccess.mockResolvedValue(true);
|
||||||
assetRepositoryMock.getById.mockResolvedValue(assetEntityStub.image);
|
assetRepositoryMock.getById.mockResolvedValue(assetStub.image);
|
||||||
await sut.getAssetById(authStub.admin, assetEntityStub.image.id);
|
await sut.getAssetById(authStub.admin, assetStub.image.id);
|
||||||
expect(accessMock.asset.hasOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetEntityStub.image.id);
|
expect(accessMock.asset.hasOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetStub.image.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow shared link access', async () => {
|
it('should allow shared link access', async () => {
|
||||||
accessMock.asset.hasSharedLinkAccess.mockResolvedValue(true);
|
accessMock.asset.hasSharedLinkAccess.mockResolvedValue(true);
|
||||||
assetRepositoryMock.getById.mockResolvedValue(assetEntityStub.image);
|
assetRepositoryMock.getById.mockResolvedValue(assetStub.image);
|
||||||
await sut.getAssetById(authStub.adminSharedLink, assetEntityStub.image.id);
|
await sut.getAssetById(authStub.adminSharedLink, assetStub.image.id);
|
||||||
expect(accessMock.asset.hasSharedLinkAccess).toHaveBeenCalledWith(
|
expect(accessMock.asset.hasSharedLinkAccess).toHaveBeenCalledWith(
|
||||||
authStub.adminSharedLink.sharedLinkId,
|
authStub.adminSharedLink.sharedLinkId,
|
||||||
assetEntityStub.image.id,
|
assetStub.image.id,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow partner sharing access', async () => {
|
it('should allow partner sharing access', async () => {
|
||||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(false);
|
accessMock.asset.hasOwnerAccess.mockResolvedValue(false);
|
||||||
accessMock.asset.hasPartnerAccess.mockResolvedValue(true);
|
accessMock.asset.hasPartnerAccess.mockResolvedValue(true);
|
||||||
assetRepositoryMock.getById.mockResolvedValue(assetEntityStub.image);
|
assetRepositoryMock.getById.mockResolvedValue(assetStub.image);
|
||||||
await sut.getAssetById(authStub.admin, assetEntityStub.image.id);
|
await sut.getAssetById(authStub.admin, assetStub.image.id);
|
||||||
expect(accessMock.asset.hasPartnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetEntityStub.image.id);
|
expect(accessMock.asset.hasPartnerAccess).toHaveBeenCalledWith(authStub.admin.id, assetStub.image.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow shared album access', async () => {
|
it('should allow shared album access', async () => {
|
||||||
accessMock.asset.hasOwnerAccess.mockResolvedValue(false);
|
accessMock.asset.hasOwnerAccess.mockResolvedValue(false);
|
||||||
accessMock.asset.hasPartnerAccess.mockResolvedValue(false);
|
accessMock.asset.hasPartnerAccess.mockResolvedValue(false);
|
||||||
accessMock.asset.hasAlbumAccess.mockResolvedValue(true);
|
accessMock.asset.hasAlbumAccess.mockResolvedValue(true);
|
||||||
assetRepositoryMock.getById.mockResolvedValue(assetEntityStub.image);
|
assetRepositoryMock.getById.mockResolvedValue(assetStub.image);
|
||||||
await sut.getAssetById(authStub.admin, assetEntityStub.image.id);
|
await sut.getAssetById(authStub.admin, assetStub.image.id);
|
||||||
expect(accessMock.asset.hasAlbumAccess).toHaveBeenCalledWith(authStub.admin.id, assetEntityStub.image.id);
|
expect(accessMock.asset.hasAlbumAccess).toHaveBeenCalledWith(authStub.admin.id, assetStub.image.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error for no access', async () => {
|
it('should throw an error for no access', async () => {
|
||||||
|
@ -448,15 +448,13 @@ describe('AssetService', () => {
|
||||||
accessMock.asset.hasPartnerAccess.mockResolvedValue(false);
|
accessMock.asset.hasPartnerAccess.mockResolvedValue(false);
|
||||||
accessMock.asset.hasSharedLinkAccess.mockResolvedValue(false);
|
accessMock.asset.hasSharedLinkAccess.mockResolvedValue(false);
|
||||||
accessMock.asset.hasAlbumAccess.mockResolvedValue(false);
|
accessMock.asset.hasAlbumAccess.mockResolvedValue(false);
|
||||||
await expect(sut.getAssetById(authStub.admin, assetEntityStub.image.id)).rejects.toBeInstanceOf(
|
await expect(sut.getAssetById(authStub.admin, assetStub.image.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||||
BadRequestException,
|
|
||||||
);
|
|
||||||
expect(assetRepositoryMock.getById).not.toHaveBeenCalled();
|
expect(assetRepositoryMock.getById).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error for an invalid shared link', async () => {
|
it('should throw an error for an invalid shared link', async () => {
|
||||||
accessMock.asset.hasSharedLinkAccess.mockResolvedValue(false);
|
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,
|
BadRequestException,
|
||||||
);
|
);
|
||||||
expect(accessMock.asset.hasOwnerAccess).not.toHaveBeenCalled();
|
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…
Add table
Reference in a new issue