mirror of
https://github.com/immich-app/immich.git
synced 2025-12-20 17:25:35 +03:00
refactor: repository mocks (#16785)
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { EmailRenderRequest, EmailTemplate, NotificationRepository } from 'src/repositories/notification.repository';
|
||||
import { newFakeLoggingRepository } from 'test/repositories/logger.repository.mock';
|
||||
import { automock } from 'test/utils';
|
||||
|
||||
describe(NotificationRepository.name, () => {
|
||||
let sut: NotificationRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
sut = new NotificationRepository(newFakeLoggingRepository());
|
||||
sut = new NotificationRepository(automock(LoggingRepository, { args: [, { getEnv: () => ({}) }], strict: false }));
|
||||
});
|
||||
|
||||
describe('renderEmail', () => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import mockfs from 'mock-fs';
|
||||
import { CrawlOptionsDto } from 'src/dtos/library.dto';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { StorageRepository } from 'src/repositories/storage.repository';
|
||||
import { newFakeLoggingRepository } from 'test/repositories/logger.repository.mock';
|
||||
import { automock } from 'test/utils';
|
||||
|
||||
interface Test {
|
||||
test: string;
|
||||
@@ -182,7 +183,7 @@ describe(StorageRepository.name, () => {
|
||||
let sut: StorageRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
sut = new StorageRepository(newFakeLoggingRepository());
|
||||
sut = new StorageRepository(automock(LoggingRepository, { args: [, { getEnv: () => ({}) }], strict: false }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -146,6 +146,7 @@ describe(ActivityService.name, () => {
|
||||
const activity = factory.activity();
|
||||
|
||||
mocks.access.activity.checkOwnerAccess.mockResolvedValue(new Set([activity.id]));
|
||||
mocks.activity.delete.mockResolvedValue();
|
||||
|
||||
await sut.delete(factory.auth(), activity.id);
|
||||
|
||||
@@ -156,6 +157,7 @@ describe(ActivityService.name, () => {
|
||||
const activity = factory.activity();
|
||||
|
||||
mocks.access.activity.checkAlbumOwnerAccess.mockResolvedValue(new Set([activity.id]));
|
||||
mocks.activity.delete.mockResolvedValue();
|
||||
|
||||
await sut.delete(factory.auth(), activity.id);
|
||||
|
||||
|
||||
@@ -347,6 +347,7 @@ describe(AlbumService.name, () => {
|
||||
it('should remove a shared user from an owned album', async () => {
|
||||
mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set([albumStub.sharedWithUser.id]));
|
||||
mocks.album.getById.mockResolvedValue(albumStub.sharedWithUser);
|
||||
mocks.albumUser.delete.mockResolvedValue();
|
||||
|
||||
await expect(
|
||||
sut.removeUser(authStub.admin, albumStub.sharedWithUser.id, userStub.user1.id),
|
||||
@@ -376,6 +377,7 @@ describe(AlbumService.name, () => {
|
||||
|
||||
it('should allow a shared user to remove themselves', async () => {
|
||||
mocks.album.getById.mockResolvedValue(albumStub.sharedWithUser);
|
||||
mocks.albumUser.delete.mockResolvedValue();
|
||||
|
||||
await sut.removeUser(authStub.user1, albumStub.sharedWithUser.id, authStub.user1.user.id);
|
||||
|
||||
@@ -388,6 +390,7 @@ describe(AlbumService.name, () => {
|
||||
|
||||
it('should allow a shared user to remove themselves using "me"', async () => {
|
||||
mocks.album.getById.mockResolvedValue(albumStub.sharedWithUser);
|
||||
mocks.albumUser.delete.mockResolvedValue();
|
||||
|
||||
await sut.removeUser(authStub.user1, albumStub.sharedWithUser.id, 'me');
|
||||
|
||||
@@ -422,6 +425,8 @@ describe(AlbumService.name, () => {
|
||||
describe('updateUser', () => {
|
||||
it('should update user role', async () => {
|
||||
mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set([albumStub.sharedWithAdmin.id]));
|
||||
mocks.albumUser.update.mockResolvedValue(null as any);
|
||||
|
||||
await sut.updateUser(authStub.user1, albumStub.sharedWithAdmin.id, userStub.admin.id, {
|
||||
role: AlbumUserRole.EDITOR,
|
||||
});
|
||||
|
||||
@@ -67,6 +67,8 @@ describe(ApiKeyService.name, () => {
|
||||
const id = newUuid();
|
||||
const auth = factory.auth();
|
||||
|
||||
mocks.apiKey.getById.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.update(auth, id, { name: 'New Name' })).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(mocks.apiKey.update).not.toHaveBeenCalledWith(id);
|
||||
@@ -91,6 +93,8 @@ describe(ApiKeyService.name, () => {
|
||||
const auth = factory.auth();
|
||||
const id = newUuid();
|
||||
|
||||
mocks.apiKey.getById.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.delete(auth, id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(mocks.apiKey.delete).not.toHaveBeenCalledWith(id);
|
||||
@@ -101,6 +105,7 @@ describe(ApiKeyService.name, () => {
|
||||
const apiKey = factory.apiKey({ userId: auth.user.id });
|
||||
|
||||
mocks.apiKey.getById.mockResolvedValue(apiKey);
|
||||
mocks.apiKey.delete.mockResolvedValue();
|
||||
|
||||
await sut.delete(auth, apiKey.id);
|
||||
|
||||
@@ -113,6 +118,8 @@ describe(ApiKeyService.name, () => {
|
||||
const auth = factory.auth();
|
||||
const id = newUuid();
|
||||
|
||||
mocks.apiKey.getById.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.getById(auth, id)).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(mocks.apiKey.getById).toHaveBeenCalledWith(auth.user.id, id);
|
||||
|
||||
@@ -127,8 +127,11 @@ describe(AssetService.name, () => {
|
||||
|
||||
describe('getRandom', () => {
|
||||
it('should get own random assets', async () => {
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
mocks.asset.getRandom.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await sut.getRandom(authStub.admin, 1);
|
||||
|
||||
expect(mocks.asset.getRandom).toHaveBeenCalledWith([authStub.admin.user.id], 1);
|
||||
});
|
||||
|
||||
@@ -531,6 +534,7 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should update stack primary asset if deleted asset was primary asset in a stack', async () => {
|
||||
mocks.stack.update.mockResolvedValue(factory.stack() as unknown as any);
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.primaryImage as AssetEntity);
|
||||
|
||||
await sut.handleAssetDeletion({ id: assetStub.primaryImage.id, deleteOnDisk: true });
|
||||
@@ -542,6 +546,7 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should delete the entire stack if deleted asset was the primary asset and the stack would only contain one asset afterwards', async () => {
|
||||
mocks.stack.delete.mockResolvedValue();
|
||||
mocks.asset.getById.mockResolvedValue({
|
||||
...assetStub.primaryImage,
|
||||
stack: { ...assetStub.primaryImage.stack, assets: assetStub.primaryImage.stack!.assets.slice(0, 2) },
|
||||
|
||||
@@ -18,7 +18,10 @@ describe(AuditService.name, () => {
|
||||
|
||||
describe('handleCleanup', () => {
|
||||
it('should delete old audit entries', async () => {
|
||||
mocks.audit.removeBefore.mockResolvedValue();
|
||||
|
||||
await expect(sut.handleCleanup()).resolves.toBe(JobStatus.SUCCESS);
|
||||
|
||||
expect(mocks.audit.removeBefore).toHaveBeenCalledWith(expect.any(Date));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -65,7 +65,10 @@ describe('AuthService', () => {
|
||||
|
||||
describe('onBootstrap', () => {
|
||||
it('should init the repo', () => {
|
||||
mocks.oauth.init.mockResolvedValue();
|
||||
|
||||
sut.onBootstrap();
|
||||
|
||||
expect(mocks.oauth.init).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -73,24 +76,30 @@ describe('AuthService', () => {
|
||||
describe('login', () => {
|
||||
it('should throw an error if password login is disabled', async () => {
|
||||
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.disabled);
|
||||
|
||||
await expect(sut.login(fixtures.login, loginDetails)).rejects.toBeInstanceOf(UnauthorizedException);
|
||||
});
|
||||
|
||||
it('should check the user exists', async () => {
|
||||
mocks.user.getByEmail.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.login(fixtures.login, loginDetails)).rejects.toBeInstanceOf(UnauthorizedException);
|
||||
|
||||
expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should check the user has a password', async () => {
|
||||
mocks.user.getByEmail.mockResolvedValue({} as UserEntity);
|
||||
|
||||
await expect(sut.login(fixtures.login, loginDetails)).rejects.toBeInstanceOf(UnauthorizedException);
|
||||
|
||||
expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should successfully log the user in', async () => {
|
||||
mocks.user.getByEmail.mockResolvedValue(userStub.user1);
|
||||
mocks.session.create.mockResolvedValue(sessionStub.valid);
|
||||
|
||||
await expect(sut.login(fixtures.login, loginDetails)).resolves.toEqual({
|
||||
accessToken: 'cmFuZG9tLWJ5dGVz',
|
||||
userId: 'user-id',
|
||||
@@ -100,6 +109,7 @@ describe('AuthService', () => {
|
||||
isAdmin: false,
|
||||
shouldChangePassword: false,
|
||||
});
|
||||
|
||||
expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -159,8 +169,10 @@ describe('AuthService', () => {
|
||||
|
||||
describe('logout', () => {
|
||||
it('should return the end session endpoint', async () => {
|
||||
const auth = factory.auth();
|
||||
|
||||
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.enabled);
|
||||
const auth = { user: { id: '123' } } as AuthDto;
|
||||
|
||||
await expect(sut.logout(auth, AuthType.OAUTH)).resolves.toEqual({
|
||||
successful: true,
|
||||
redirectUri: 'http://end-session-endpoint',
|
||||
@@ -168,7 +180,7 @@ describe('AuthService', () => {
|
||||
});
|
||||
|
||||
it('should return the default redirect', async () => {
|
||||
const auth = { user: { id: '123' } } as AuthDto;
|
||||
const auth = factory.auth();
|
||||
|
||||
await expect(sut.logout(auth, AuthType.PASSWORD)).resolves.toEqual({
|
||||
successful: true,
|
||||
@@ -178,6 +190,7 @@ describe('AuthService', () => {
|
||||
|
||||
it('should delete the access token', async () => {
|
||||
const auth = { user: { id: '123' }, session: { id: 'token123' } } as AuthDto;
|
||||
mocks.session.delete.mockResolvedValue();
|
||||
|
||||
await expect(sut.logout(auth, AuthType.PASSWORD)).resolves.toEqual({
|
||||
successful: true,
|
||||
@@ -203,7 +216,9 @@ describe('AuthService', () => {
|
||||
|
||||
it('should only allow one admin', async () => {
|
||||
mocks.user.getAdmin.mockResolvedValue({} as UserEntity);
|
||||
|
||||
await expect(sut.adminSignUp(dto)).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(mocks.user.getAdmin).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -215,6 +230,7 @@ describe('AuthService', () => {
|
||||
createdAt: new Date('2021-01-01'),
|
||||
metadata: [] as UserMetadataEntity[],
|
||||
} as UserEntity);
|
||||
|
||||
await expect(sut.adminSignUp(dto)).resolves.toMatchObject({
|
||||
avatarColor: expect.any(String),
|
||||
id: 'admin',
|
||||
@@ -222,6 +238,7 @@ describe('AuthService', () => {
|
||||
email: 'test@immich.com',
|
||||
name: 'immich admin',
|
||||
});
|
||||
|
||||
expect(mocks.user.getAdmin).toHaveBeenCalled();
|
||||
expect(mocks.user.create).toHaveBeenCalled();
|
||||
});
|
||||
@@ -241,6 +258,7 @@ describe('AuthService', () => {
|
||||
it('should validate using authorization header', async () => {
|
||||
mocks.user.get.mockResolvedValue(userStub.user1);
|
||||
mocks.session.getByToken.mockResolvedValue(sessionStub.valid as any);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { authorization: 'Bearer auth_token' },
|
||||
@@ -256,6 +274,8 @@ describe('AuthService', () => {
|
||||
|
||||
describe('validate - shared key', () => {
|
||||
it('should not accept a non-existent key', async () => {
|
||||
mocks.sharedLink.getByKey.mockResolvedValue(void 0);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { 'x-immich-share-key': 'key' },
|
||||
@@ -267,6 +287,7 @@ describe('AuthService', () => {
|
||||
|
||||
it('should not accept an expired key', async () => {
|
||||
mocks.sharedLink.getByKey.mockResolvedValue(sharedLinkStub.expired);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { 'x-immich-share-key': 'key' },
|
||||
@@ -278,6 +299,7 @@ describe('AuthService', () => {
|
||||
|
||||
it('should not accept a key on a non-shared route', async () => {
|
||||
mocks.sharedLink.getByKey.mockResolvedValue(sharedLinkStub.valid);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { 'x-immich-share-key': 'key' },
|
||||
@@ -290,6 +312,7 @@ describe('AuthService', () => {
|
||||
it('should not accept a key without a user', async () => {
|
||||
mocks.sharedLink.getByKey.mockResolvedValue(sharedLinkStub.expired);
|
||||
mocks.user.get.mockResolvedValue(void 0);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { 'x-immich-share-key': 'key' },
|
||||
@@ -302,6 +325,7 @@ describe('AuthService', () => {
|
||||
it('should accept a base64url key', async () => {
|
||||
mocks.sharedLink.getByKey.mockResolvedValue(sharedLinkStub.valid);
|
||||
mocks.user.get.mockResolvedValue(userStub.admin);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { 'x-immich-share-key': sharedLinkStub.valid.key.toString('base64url') },
|
||||
@@ -318,6 +342,7 @@ describe('AuthService', () => {
|
||||
it('should accept a hex key', async () => {
|
||||
mocks.sharedLink.getByKey.mockResolvedValue(sharedLinkStub.valid);
|
||||
mocks.user.get.mockResolvedValue(userStub.admin);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { 'x-immich-share-key': sharedLinkStub.valid.key.toString('hex') },
|
||||
@@ -335,6 +360,7 @@ describe('AuthService', () => {
|
||||
describe('validate - user token', () => {
|
||||
it('should throw if no token is found', async () => {
|
||||
mocks.session.getByToken.mockResolvedValue(void 0);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { 'x-immich-user-token': 'auth_token' },
|
||||
@@ -346,6 +372,7 @@ describe('AuthService', () => {
|
||||
|
||||
it('should return an auth dto', async () => {
|
||||
mocks.session.getByToken.mockResolvedValue(sessionStub.valid as any);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { cookie: 'immich_access_token=auth_token' },
|
||||
@@ -360,6 +387,7 @@ describe('AuthService', () => {
|
||||
|
||||
it('should throw if admin route and not an admin', async () => {
|
||||
mocks.session.getByToken.mockResolvedValue(sessionStub.valid as any);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { cookie: 'immich_access_token=auth_token' },
|
||||
@@ -372,6 +400,7 @@ describe('AuthService', () => {
|
||||
it('should update when access time exceeds an hour', async () => {
|
||||
mocks.session.getByToken.mockResolvedValue(sessionStub.inactive as any);
|
||||
mocks.session.update.mockResolvedValue(sessionStub.valid);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { cookie: 'immich_access_token=auth_token' },
|
||||
@@ -386,6 +415,7 @@ describe('AuthService', () => {
|
||||
describe('validate - api key', () => {
|
||||
it('should throw an error if no api key is found', async () => {
|
||||
mocks.apiKey.getKey.mockResolvedValue(void 0);
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { 'x-api-key': 'auth_token' },
|
||||
@@ -401,6 +431,7 @@ describe('AuthService', () => {
|
||||
const authApiKey = factory.authApiKey({ permissions: [] });
|
||||
|
||||
mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser });
|
||||
|
||||
await expect(
|
||||
sut.authenticate({
|
||||
headers: { 'x-api-key': 'auth_token' },
|
||||
@@ -442,6 +473,7 @@ describe('AuthService', () => {
|
||||
describe('authorize', () => {
|
||||
it('should fail if oauth is disabled', async () => {
|
||||
mocks.systemMetadata.get.mockResolvedValue({ oauth: { enabled: false } });
|
||||
|
||||
await expect(sut.authorize({ redirectUri: 'https://demo.immich.app' })).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
@@ -449,6 +481,7 @@ describe('AuthService', () => {
|
||||
|
||||
it('should authorize the user', async () => {
|
||||
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithMobileOverride);
|
||||
|
||||
await sut.authorize({ redirectUri: 'https://demo.immich.app' });
|
||||
});
|
||||
});
|
||||
@@ -461,9 +494,11 @@ describe('AuthService', () => {
|
||||
it('should not allow auto registering', async () => {
|
||||
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthEnabled);
|
||||
mocks.user.getByEmail.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toBeInstanceOf(
|
||||
BadRequestException,
|
||||
);
|
||||
|
||||
expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -540,6 +575,7 @@ describe('AuthService', () => {
|
||||
mocks.session.create.mockResolvedValue(sessionStub.valid);
|
||||
|
||||
await sut.callback({ url }, loginDetails);
|
||||
|
||||
expect(mocks.oauth.getProfile).toHaveBeenCalledWith(expect.objectContaining({}), url, 'http://mobile-redirect');
|
||||
});
|
||||
}
|
||||
@@ -549,6 +585,7 @@ describe('AuthService', () => {
|
||||
mocks.user.getByEmail.mockResolvedValue(void 0);
|
||||
mocks.user.getAdmin.mockResolvedValue(userStub.user1);
|
||||
mocks.user.create.mockResolvedValue(userStub.user1);
|
||||
mocks.session.create.mockResolvedValue(factory.session());
|
||||
|
||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||
oauthResponse,
|
||||
@@ -563,6 +600,7 @@ describe('AuthService', () => {
|
||||
mocks.user.getAdmin.mockResolvedValue(userStub.user1);
|
||||
mocks.user.create.mockResolvedValue(userStub.user1);
|
||||
mocks.oauth.getProfile.mockResolvedValue({ sub, email, immich_quota: 'abc' });
|
||||
mocks.session.create.mockResolvedValue(factory.session());
|
||||
|
||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||
oauthResponse,
|
||||
@@ -577,6 +615,7 @@ describe('AuthService', () => {
|
||||
mocks.user.getAdmin.mockResolvedValue(userStub.user1);
|
||||
mocks.user.create.mockResolvedValue(userStub.user1);
|
||||
mocks.oauth.getProfile.mockResolvedValue({ sub, email, immich_quota: -5 });
|
||||
mocks.session.create.mockResolvedValue(factory.session());
|
||||
|
||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||
oauthResponse,
|
||||
@@ -591,6 +630,7 @@ describe('AuthService', () => {
|
||||
mocks.user.getAdmin.mockResolvedValue(userStub.user1);
|
||||
mocks.user.create.mockResolvedValue(userStub.user1);
|
||||
mocks.oauth.getProfile.mockResolvedValue({ sub, email, immich_quota: 0 });
|
||||
mocks.session.create.mockResolvedValue(factory.session());
|
||||
|
||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||
oauthResponse,
|
||||
@@ -611,6 +651,7 @@ describe('AuthService', () => {
|
||||
mocks.user.getAdmin.mockResolvedValue(userStub.user1);
|
||||
mocks.user.create.mockResolvedValue(userStub.user1);
|
||||
mocks.oauth.getProfile.mockResolvedValue({ sub, email, immich_quota: 5 });
|
||||
mocks.session.create.mockResolvedValue(factory.session());
|
||||
|
||||
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
|
||||
oauthResponse,
|
||||
|
||||
@@ -22,6 +22,7 @@ describe(BackupService.name, () => {
|
||||
describe('onBootstrapEvent', () => {
|
||||
it('should init cron job and handle config changes', async () => {
|
||||
mocks.database.tryLock.mockResolvedValue(true);
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: systemConfigStub.backupEnabled as SystemConfig });
|
||||
|
||||
@@ -47,10 +48,14 @@ describe(BackupService.name, () => {
|
||||
describe('onConfigUpdateEvent', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.database.tryLock.mockResolvedValue(true);
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: defaults });
|
||||
});
|
||||
|
||||
it('should update cron job if backup is enabled', () => {
|
||||
mocks.cron.update.mockResolvedValue();
|
||||
|
||||
sut.onConfigUpdate({
|
||||
oldConfig: defaults,
|
||||
newConfig: {
|
||||
|
||||
@@ -31,6 +31,8 @@ describe(CliService.name, () => {
|
||||
|
||||
it('should default to a random password', async () => {
|
||||
mocks.user.getAdmin.mockResolvedValue(userStub.admin);
|
||||
mocks.user.update.mockResolvedValue(userStub.admin);
|
||||
|
||||
const ask = vitest.fn().mockImplementation(() => {});
|
||||
|
||||
const response = await sut.resetAdminPassword(ask);
|
||||
@@ -45,6 +47,8 @@ describe(CliService.name, () => {
|
||||
|
||||
it('should use the supplied password', async () => {
|
||||
mocks.user.getAdmin.mockResolvedValue(userStub.admin);
|
||||
mocks.user.update.mockResolvedValue(userStub.admin);
|
||||
|
||||
const ask = vitest.fn().mockResolvedValue('new-password');
|
||||
|
||||
const response = await sut.resetAdminPassword(ask);
|
||||
|
||||
@@ -173,6 +173,7 @@ describe(DownloadService.name, () => {
|
||||
it('should return a list of archives (assetIds)', async () => {
|
||||
const assetIds = ['asset-1', 'asset-2'];
|
||||
|
||||
mocks.user.getMetadata.mockResolvedValue([]);
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(assetIds));
|
||||
mocks.downloadRepository.downloadAssetIds.mockReturnValue(
|
||||
makeStream([
|
||||
@@ -187,6 +188,7 @@ describe(DownloadService.name, () => {
|
||||
});
|
||||
|
||||
it('should return a list of archives (albumId)', async () => {
|
||||
mocks.user.getMetadata.mockResolvedValue([]);
|
||||
mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set(['album-1']));
|
||||
mocks.downloadRepository.downloadAlbumId.mockReturnValue(
|
||||
makeStream([
|
||||
@@ -202,6 +204,7 @@ describe(DownloadService.name, () => {
|
||||
});
|
||||
|
||||
it('should return a list of archives (userId)', async () => {
|
||||
mocks.user.getMetadata.mockResolvedValue([]);
|
||||
mocks.downloadRepository.downloadUserId.mockReturnValue(
|
||||
makeStream([
|
||||
{ id: 'asset-1', livePhotoVideoId: null, size: 100_000 },
|
||||
@@ -217,6 +220,7 @@ describe(DownloadService.name, () => {
|
||||
});
|
||||
|
||||
it('should split archives by size', async () => {
|
||||
mocks.user.getMetadata.mockResolvedValue([]);
|
||||
mocks.downloadRepository.downloadUserId.mockReturnValue(
|
||||
makeStream([
|
||||
{ id: 'asset-1', livePhotoVideoId: null, size: 5000 },
|
||||
@@ -244,13 +248,13 @@ describe(DownloadService.name, () => {
|
||||
const assetIds = ['asset-1', 'asset-2'];
|
||||
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(assetIds));
|
||||
mocks.user.getMetadata.mockResolvedValue([]);
|
||||
mocks.downloadRepository.downloadAssetIds.mockReturnValue(
|
||||
makeStream([
|
||||
{ id: 'asset-1', livePhotoVideoId: 'asset-3', size: 5000 },
|
||||
{ id: 'asset-2', livePhotoVideoId: 'asset-4', size: 100_000 },
|
||||
]),
|
||||
);
|
||||
|
||||
mocks.downloadRepository.downloadMotionAssetIds.mockReturnValue(
|
||||
makeStream([
|
||||
{ id: 'asset-3', livePhotoVideoId: null, size: 23_456, originalPath: '/path/to/file.mp4' },
|
||||
@@ -271,11 +275,10 @@ describe(DownloadService.name, () => {
|
||||
const assetIds = ['asset-1'];
|
||||
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(assetIds));
|
||||
|
||||
mocks.user.getMetadata.mockResolvedValue([]);
|
||||
mocks.downloadRepository.downloadAssetIds.mockReturnValue(
|
||||
makeStream([{ id: 'asset-1', livePhotoVideoId: 'asset-3', size: 5000 }]),
|
||||
);
|
||||
|
||||
mocks.downloadRepository.downloadMotionAssetIds.mockReturnValue(
|
||||
makeStream([
|
||||
{ id: 'asset-2', livePhotoVideoId: null, size: 23_456, originalPath: 'upload/encoded-video/uuid-MP.mp4' },
|
||||
|
||||
@@ -36,6 +36,9 @@ describe(LibraryService.name, () => {
|
||||
|
||||
describe('onConfigInit', () => {
|
||||
it('should init cron job and handle config changes', async () => {
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
mocks.cron.update.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: defaults });
|
||||
|
||||
expect(mocks.cron.create).toHaveBeenCalled();
|
||||
@@ -65,6 +68,7 @@ describe(LibraryService.name, () => {
|
||||
mocks.library.get.mockImplementation((id) =>
|
||||
Promise.resolve([library1, library2].find((library) => library.id === id)),
|
||||
);
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: systemConfigStub.libraryWatchEnabled as SystemConfig });
|
||||
|
||||
@@ -74,6 +78,8 @@ describe(LibraryService.name, () => {
|
||||
});
|
||||
|
||||
it('should not initialize watcher when watching is disabled', async () => {
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: systemConfigStub.libraryWatchDisabled as SystemConfig });
|
||||
|
||||
expect(mocks.storage.watch).not.toHaveBeenCalled();
|
||||
@@ -99,6 +105,8 @@ describe(LibraryService.name, () => {
|
||||
describe('onConfigUpdateEvent', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.database.tryLock.mockResolvedValue(true);
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: defaults });
|
||||
});
|
||||
|
||||
@@ -111,6 +119,9 @@ describe(LibraryService.name, () => {
|
||||
|
||||
it('should update cron job and enable watching', async () => {
|
||||
mocks.library.getAll.mockResolvedValue([]);
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
mocks.cron.update.mockResolvedValue();
|
||||
|
||||
await sut.onConfigUpdate({
|
||||
newConfig: systemConfigStub.libraryScanAndWatch as SystemConfig,
|
||||
oldConfig: defaults,
|
||||
@@ -125,6 +136,9 @@ describe(LibraryService.name, () => {
|
||||
|
||||
it('should update cron job and disable watching', async () => {
|
||||
mocks.library.getAll.mockResolvedValue([]);
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
mocks.cron.update.mockResolvedValue();
|
||||
|
||||
await sut.onConfigUpdate({
|
||||
newConfig: systemConfigStub.libraryScanAndWatch as SystemConfig,
|
||||
oldConfig: defaults,
|
||||
@@ -620,6 +634,7 @@ describe(LibraryService.name, () => {
|
||||
|
||||
const mockClose = vitest.fn();
|
||||
mocks.storage.watch.mockImplementation(makeMockWatcher({ close: mockClose }));
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: systemConfigStub.libraryWatchEnabled as SystemConfig });
|
||||
await sut.delete(library.id);
|
||||
@@ -765,6 +780,7 @@ describe(LibraryService.name, () => {
|
||||
mocks.library.create.mockResolvedValue(library);
|
||||
mocks.library.get.mockResolvedValue(library);
|
||||
mocks.library.getAll.mockResolvedValue([]);
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: systemConfigStub.libraryWatchEnabled as SystemConfig });
|
||||
await sut.create({ ownerId: authStub.admin.user.id, importPaths: library.importPaths });
|
||||
@@ -832,6 +848,7 @@ describe(LibraryService.name, () => {
|
||||
describe('update', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.library.getAll.mockResolvedValue([]);
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: systemConfigStub.libraryWatchEnabled as SystemConfig });
|
||||
});
|
||||
@@ -878,6 +895,8 @@ describe(LibraryService.name, () => {
|
||||
|
||||
describe('watching disabled', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: systemConfigStub.libraryWatchDisabled as SystemConfig });
|
||||
});
|
||||
|
||||
@@ -895,6 +914,8 @@ describe(LibraryService.name, () => {
|
||||
describe('watching enabled', () => {
|
||||
beforeEach(async () => {
|
||||
mocks.library.getAll.mockResolvedValue([]);
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: systemConfigStub.libraryWatchEnabled as SystemConfig });
|
||||
});
|
||||
|
||||
@@ -1067,6 +1088,7 @@ describe(LibraryService.name, () => {
|
||||
|
||||
const mockClose = vitest.fn();
|
||||
mocks.storage.watch.mockImplementation(makeMockWatcher({ close: mockClose }));
|
||||
mocks.cron.create.mockResolvedValue();
|
||||
|
||||
await sut.onConfigInit({ newConfig: systemConfigStub.libraryWatchEnabled as SystemConfig });
|
||||
await sut.onShutdown();
|
||||
|
||||
@@ -33,6 +33,8 @@ describe(MemoryService.name, () => {
|
||||
});
|
||||
|
||||
it('should map ', async () => {
|
||||
mocks.memory.search.mockResolvedValue([]);
|
||||
|
||||
await expect(sut.search(factory.auth(), {})).resolves.toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -46,6 +48,7 @@ describe(MemoryService.name, () => {
|
||||
const [memoryId] = newUuids();
|
||||
|
||||
mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memoryId]));
|
||||
mocks.memory.get.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.get(factory.auth(), memoryId)).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
@@ -159,6 +162,7 @@ describe(MemoryService.name, () => {
|
||||
const memoryId = newUuid();
|
||||
|
||||
mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memoryId]));
|
||||
mocks.memory.delete.mockResolvedValue();
|
||||
|
||||
await expect(sut.remove(factory.auth(), memoryId)).resolves.toBeUndefined();
|
||||
|
||||
@@ -183,6 +187,7 @@ describe(MemoryService.name, () => {
|
||||
|
||||
mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id]));
|
||||
mocks.memory.get.mockResolvedValue(memory);
|
||||
mocks.memory.getAssetIds.mockResolvedValue(new Set());
|
||||
|
||||
await expect(sut.addAssets(factory.auth(), memory.id, { ids: [assetId] })).resolves.toEqual([
|
||||
{ error: 'no_permission', id: assetId, success: false },
|
||||
@@ -213,6 +218,9 @@ describe(MemoryService.name, () => {
|
||||
mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id]));
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId]));
|
||||
mocks.memory.get.mockResolvedValue(memory);
|
||||
mocks.memory.update.mockResolvedValue(memory);
|
||||
mocks.memory.getAssetIds.mockResolvedValue(new Set());
|
||||
mocks.memory.addAssetIds.mockResolvedValue();
|
||||
|
||||
await expect(sut.addAssets(factory.auth(), memory.id, { ids: [assetId] })).resolves.toEqual([
|
||||
{ id: assetId, success: true },
|
||||
@@ -233,6 +241,7 @@ describe(MemoryService.name, () => {
|
||||
|
||||
it('should skip assets not in the memory', async () => {
|
||||
mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set(['memory1']));
|
||||
mocks.memory.getAssetIds.mockResolvedValue(new Set());
|
||||
|
||||
await expect(sut.removeAssets(factory.auth(), 'memory1', { ids: ['not-found'] })).resolves.toEqual([
|
||||
{ error: 'not_found', id: 'not-found', success: false },
|
||||
@@ -242,15 +251,20 @@ describe(MemoryService.name, () => {
|
||||
});
|
||||
|
||||
it('should remove assets', async () => {
|
||||
mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set(['memory1']));
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset1']));
|
||||
mocks.memory.getAssetIds.mockResolvedValue(new Set(['asset1']));
|
||||
const memory = factory.memory();
|
||||
const asset = factory.asset();
|
||||
|
||||
await expect(sut.removeAssets(factory.auth(), 'memory1', { ids: ['asset1'] })).resolves.toEqual([
|
||||
{ id: 'asset1', success: true },
|
||||
mocks.access.memory.checkOwnerAccess.mockResolvedValue(new Set([memory.id]));
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.memory.getAssetIds.mockResolvedValue(new Set([asset.id]));
|
||||
mocks.memory.removeAssetIds.mockResolvedValue();
|
||||
mocks.memory.update.mockResolvedValue(memory);
|
||||
|
||||
await expect(sut.removeAssets(factory.auth(), memory.id, { ids: [asset.id] })).resolves.toEqual([
|
||||
{ id: asset.id, success: true },
|
||||
]);
|
||||
|
||||
expect(mocks.memory.removeAssetIds).toHaveBeenCalledWith('memory1', ['asset1']);
|
||||
expect(mocks.memory.removeAssetIds).toHaveBeenCalledWith(memory.id, [asset.id]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,6 +52,10 @@ describe(MetadataService.name, () => {
|
||||
|
||||
describe('onBootstrapEvent', () => {
|
||||
it('should pause and resume queue during init', async () => {
|
||||
mocks.job.pause.mockResolvedValue();
|
||||
mocks.map.init.mockResolvedValue();
|
||||
mocks.job.resume.mockResolvedValue();
|
||||
|
||||
await sut.onBootstrap();
|
||||
|
||||
expect(mocks.job.pause).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -260,6 +260,7 @@ describe(NotificationService.name, () => {
|
||||
mocks.user.get.mockResolvedValue(userStub.admin);
|
||||
mocks.notification.verifySmtp.mockResolvedValue(true);
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.notification.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' });
|
||||
|
||||
await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).resolves.not.toThrow();
|
||||
expect(mocks.notification.renderEmail).toHaveBeenCalledWith({
|
||||
@@ -279,6 +280,7 @@ describe(NotificationService.name, () => {
|
||||
mocks.notification.verifySmtp.mockResolvedValue(true);
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.systemMetadata.get.mockResolvedValue({ server: { externalDomain: 'https://demo.immich.app' } });
|
||||
mocks.notification.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' });
|
||||
|
||||
await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).resolves.not.toThrow();
|
||||
expect(mocks.notification.renderEmail).toHaveBeenCalledWith({
|
||||
@@ -297,6 +299,7 @@ describe(NotificationService.name, () => {
|
||||
mocks.user.get.mockResolvedValue(userStub.admin);
|
||||
mocks.notification.verifySmtp.mockResolvedValue(true);
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.notification.sendEmail.mockResolvedValue({ messageId: 'message-1', response: '' });
|
||||
|
||||
await expect(
|
||||
sut.sendTestEmail('', { ...configs.smtpTransport.notifications.smtp, replyTo: 'demo@immich.app' }),
|
||||
|
||||
@@ -324,6 +324,10 @@ describe(PersonService.name, () => {
|
||||
mocks.person.getFacesByIds.mockResolvedValue([faceStub.face1]);
|
||||
mocks.person.reassignFace.mockResolvedValue(1);
|
||||
mocks.person.getRandomFace.mockResolvedValue(faceStub.primaryFace1);
|
||||
mocks.person.refreshFaces.mockResolvedValue();
|
||||
mocks.person.reassignFace.mockResolvedValue(5);
|
||||
mocks.person.update.mockResolvedValue(personStub.noName);
|
||||
|
||||
await expect(
|
||||
sut.reassignFaces(authStub.admin, personStub.noName.id, {
|
||||
data: [{ personId: personStub.withName.id, assetId: assetStub.image.id }],
|
||||
@@ -515,6 +519,7 @@ describe(PersonService.name, () => {
|
||||
hasNextPage: false,
|
||||
});
|
||||
mocks.person.getAllWithoutFaces.mockResolvedValue([personStub.randomPerson]);
|
||||
mocks.person.deleteFaces.mockResolvedValue();
|
||||
|
||||
await sut.handleQueueDetectFaces({ force: true });
|
||||
|
||||
@@ -633,6 +638,7 @@ describe(PersonService.name, () => {
|
||||
mocks.person.getAll.mockReturnValue(makeStream());
|
||||
mocks.person.getAllFaces.mockReturnValue(makeStream([faceStub.face1]));
|
||||
mocks.person.getAllWithoutFaces.mockResolvedValue([]);
|
||||
mocks.person.unassignFaces.mockResolvedValue();
|
||||
|
||||
await sut.handleQueueRecognizeFaces({ force: true, nightly: true });
|
||||
|
||||
@@ -679,6 +685,7 @@ describe(PersonService.name, () => {
|
||||
mocks.person.getAll.mockReturnValue(makeStream([faceStub.face1.person, personStub.randomPerson]));
|
||||
mocks.person.getAllFaces.mockReturnValue(makeStream([faceStub.face1]));
|
||||
mocks.person.getAllWithoutFaces.mockResolvedValue([personStub.randomPerson]);
|
||||
mocks.person.unassignFaces.mockResolvedValue();
|
||||
|
||||
await sut.handleQueueRecognizeFaces({ force: true });
|
||||
|
||||
@@ -757,6 +764,7 @@ describe(PersonService.name, () => {
|
||||
mocks.machineLearning.detectFaces.mockResolvedValue(detectFaceMock);
|
||||
mocks.search.searchFaces.mockResolvedValue([{ ...faceStub.face1, distance: 0.7 }]);
|
||||
mocks.asset.getByIds.mockResolvedValue([assetStub.image]);
|
||||
mocks.person.refreshFaces.mockResolvedValue();
|
||||
|
||||
await sut.handleDetectFaces({ id: assetStub.image.id });
|
||||
|
||||
@@ -784,6 +792,7 @@ describe(PersonService.name, () => {
|
||||
it('should add new face and delete an existing face not among the new detected faces', async () => {
|
||||
mocks.machineLearning.detectFaces.mockResolvedValue(detectFaceMock);
|
||||
mocks.asset.getByIds.mockResolvedValue([{ ...assetStub.image, faces: [faceStub.primaryFace1] }]);
|
||||
mocks.person.refreshFaces.mockResolvedValue();
|
||||
|
||||
await sut.handleDetectFaces({ id: assetStub.image.id });
|
||||
|
||||
@@ -799,6 +808,7 @@ describe(PersonService.name, () => {
|
||||
it('should add embedding to matching metadata face', async () => {
|
||||
mocks.machineLearning.detectFaces.mockResolvedValue(detectFaceMock);
|
||||
mocks.asset.getByIds.mockResolvedValue([{ ...assetStub.image, faces: [faceStub.fromExif1] }]);
|
||||
mocks.person.refreshFaces.mockResolvedValue();
|
||||
|
||||
await sut.handleDetectFaces({ id: assetStub.image.id });
|
||||
|
||||
@@ -1006,6 +1016,7 @@ describe(PersonService.name, () => {
|
||||
mocks.person.getById.mockResolvedValue({ ...personStub.primaryPerson, faceAssetId: faceStub.middle.assetId });
|
||||
mocks.person.getFaceByIdWithAssets.mockResolvedValue(faceStub.middle);
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.primaryImage);
|
||||
mocks.media.generateThumbnail.mockResolvedValue();
|
||||
|
||||
await sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id });
|
||||
|
||||
@@ -1038,6 +1049,7 @@ describe(PersonService.name, () => {
|
||||
mocks.person.getById.mockResolvedValue({ ...personStub.primaryPerson, faceAssetId: faceStub.start.assetId });
|
||||
mocks.person.getFaceByIdWithAssets.mockResolvedValue(faceStub.start);
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
mocks.media.generateThumbnail.mockResolvedValue();
|
||||
|
||||
await sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id });
|
||||
|
||||
@@ -1063,7 +1075,9 @@ describe(PersonService.name, () => {
|
||||
it('should generate a thumbnail without overflowing', async () => {
|
||||
mocks.person.getById.mockResolvedValue({ ...personStub.primaryPerson, faceAssetId: faceStub.end.assetId });
|
||||
mocks.person.getFaceByIdWithAssets.mockResolvedValue(faceStub.end);
|
||||
mocks.person.update.mockResolvedValue(personStub.primaryPerson);
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.primaryImage);
|
||||
mocks.media.generateThumbnail.mockResolvedValue();
|
||||
|
||||
await sut.handleGeneratePersonThumbnail({ id: personStub.primaryPerson.id });
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ describe(SearchService.name, () => {
|
||||
describe('getSearchSuggestions', () => {
|
||||
it('should return search suggestions for country', async () => {
|
||||
mocks.search.getCountries.mockResolvedValue(['USA']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.COUNTRY }),
|
||||
).resolves.toEqual(['USA']);
|
||||
@@ -65,6 +67,8 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should return search suggestions for country (including null)', async () => {
|
||||
mocks.search.getCountries.mockResolvedValue(['USA']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.COUNTRY }),
|
||||
).resolves.toEqual(['USA', null]);
|
||||
@@ -73,6 +77,8 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should return search suggestions for state', async () => {
|
||||
mocks.search.getStates.mockResolvedValue(['California']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.STATE }),
|
||||
).resolves.toEqual(['California']);
|
||||
@@ -81,6 +87,8 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should return search suggestions for state (including null)', async () => {
|
||||
mocks.search.getStates.mockResolvedValue(['California']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.STATE }),
|
||||
).resolves.toEqual(['California', null]);
|
||||
@@ -89,6 +97,8 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should return search suggestions for city', async () => {
|
||||
mocks.search.getCities.mockResolvedValue(['Denver']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.CITY }),
|
||||
).resolves.toEqual(['Denver']);
|
||||
@@ -97,6 +107,8 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should return search suggestions for city (including null)', async () => {
|
||||
mocks.search.getCities.mockResolvedValue(['Denver']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.CITY }),
|
||||
).resolves.toEqual(['Denver', null]);
|
||||
@@ -105,6 +117,8 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should return search suggestions for camera make', async () => {
|
||||
mocks.search.getCameraMakes.mockResolvedValue(['Nikon']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.CAMERA_MAKE }),
|
||||
).resolves.toEqual(['Nikon']);
|
||||
@@ -113,6 +127,8 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should return search suggestions for camera make (including null)', async () => {
|
||||
mocks.search.getCameraMakes.mockResolvedValue(['Nikon']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.CAMERA_MAKE }),
|
||||
).resolves.toEqual(['Nikon', null]);
|
||||
@@ -121,6 +137,8 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should return search suggestions for camera model', async () => {
|
||||
mocks.search.getCameraModels.mockResolvedValue(['Fujifilm X100VI']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.CAMERA_MODEL }),
|
||||
).resolves.toEqual(['Fujifilm X100VI']);
|
||||
@@ -129,6 +147,8 @@ describe(SearchService.name, () => {
|
||||
|
||||
it('should return search suggestions for camera model (including null)', async () => {
|
||||
mocks.search.getCameraModels.mockResolvedValue(['Fujifilm X100VI']);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.CAMERA_MODEL }),
|
||||
).resolves.toEqual(['Fujifilm X100VI', null]);
|
||||
|
||||
@@ -36,6 +36,7 @@ describe('SessionService', () => {
|
||||
updateId: 'uuid-v7',
|
||||
},
|
||||
]);
|
||||
mocks.session.delete.mockResolvedValue();
|
||||
|
||||
await expect(sut.handleCleanup()).resolves.toEqual(JobStatus.SUCCESS);
|
||||
expect(mocks.session.delete).toHaveBeenCalledWith('123');
|
||||
@@ -71,6 +72,7 @@ describe('SessionService', () => {
|
||||
describe('logoutDevices', () => {
|
||||
it('should logout all devices', async () => {
|
||||
mocks.session.getByUserId.mockResolvedValue([sessionStub.inactive, sessionStub.valid] as any[]);
|
||||
mocks.session.delete.mockResolvedValue();
|
||||
|
||||
await sut.deleteAll(authStub.user1);
|
||||
|
||||
@@ -83,6 +85,7 @@ describe('SessionService', () => {
|
||||
describe('logoutDevice', () => {
|
||||
it('should logout the device', async () => {
|
||||
mocks.access.authDevice.checkOwnerAccess.mockResolvedValue(new Set(['token-1']));
|
||||
mocks.session.delete.mockResolvedValue();
|
||||
|
||||
await sut.delete(authStub.user1, 'token-1');
|
||||
|
||||
|
||||
@@ -71,7 +71,10 @@ describe(SharedLinkService.name, () => {
|
||||
|
||||
describe('get', () => {
|
||||
it('should throw an error for an invalid shared link', async () => {
|
||||
mocks.sharedLink.get.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.get(authStub.user1, 'missing-id')).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, 'missing-id');
|
||||
expect(mocks.sharedLink.update).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -194,7 +197,10 @@ describe(SharedLinkService.name, () => {
|
||||
|
||||
describe('update', () => {
|
||||
it('should throw an error for an invalid shared link', async () => {
|
||||
mocks.sharedLink.get.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.update(authStub.user1, 'missing-id', {})).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, 'missing-id');
|
||||
expect(mocks.sharedLink.update).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -214,14 +220,20 @@ describe(SharedLinkService.name, () => {
|
||||
|
||||
describe('remove', () => {
|
||||
it('should throw an error for an invalid shared link', async () => {
|
||||
mocks.sharedLink.get.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.remove(authStub.user1, 'missing-id')).rejects.toBeInstanceOf(BadRequestException);
|
||||
|
||||
expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, 'missing-id');
|
||||
expect(mocks.sharedLink.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should remove a key', async () => {
|
||||
mocks.sharedLink.get.mockResolvedValue(sharedLinkStub.valid);
|
||||
mocks.sharedLink.remove.mockResolvedValue();
|
||||
|
||||
await sut.remove(authStub.user1, sharedLinkStub.valid.id);
|
||||
|
||||
expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, sharedLinkStub.valid.id);
|
||||
expect(mocks.sharedLink.remove).toHaveBeenCalledWith(sharedLinkStub.valid);
|
||||
});
|
||||
@@ -238,6 +250,7 @@ describe(SharedLinkService.name, () => {
|
||||
it('should add assets to a shared link', async () => {
|
||||
mocks.sharedLink.get.mockResolvedValue(_.cloneDeep(sharedLinkStub.individual));
|
||||
mocks.sharedLink.create.mockResolvedValue(sharedLinkStub.individual);
|
||||
mocks.sharedLink.update.mockResolvedValue(sharedLinkStub.individual);
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-3']));
|
||||
|
||||
await expect(
|
||||
@@ -268,6 +281,7 @@ describe(SharedLinkService.name, () => {
|
||||
it('should remove assets from a shared link', async () => {
|
||||
mocks.sharedLink.get.mockResolvedValue(_.cloneDeep(sharedLinkStub.individual));
|
||||
mocks.sharedLink.create.mockResolvedValue(sharedLinkStub.individual);
|
||||
mocks.sharedLink.update.mockResolvedValue(sharedLinkStub.individual);
|
||||
|
||||
await expect(
|
||||
sut.removeAssets(authStub.admin, 'link-1', { assetIds: [assetStub.image.id, 'asset-2'] }),
|
||||
|
||||
@@ -155,6 +155,7 @@ describe(StackService.name, () => {
|
||||
|
||||
it('should delete stack', async () => {
|
||||
mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
|
||||
mocks.stack.delete.mockResolvedValue();
|
||||
|
||||
await sut.delete(authStub.admin, 'stack-id');
|
||||
|
||||
@@ -176,6 +177,7 @@ describe(StackService.name, () => {
|
||||
|
||||
it('should delete all stacks', async () => {
|
||||
mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
|
||||
mocks.stack.deleteAll.mockResolvedValue();
|
||||
|
||||
await sut.deleteAll(authStub.admin, { ids: ['stack-id'] });
|
||||
|
||||
|
||||
@@ -93,7 +93,9 @@ describe(StorageTemplateService.name, () => {
|
||||
describe('handleMigrationSingle', () => {
|
||||
it('should skip when storage template is disabled', async () => {
|
||||
mocks.systemMetadata.get.mockResolvedValue({ storageTemplate: { enabled: false } });
|
||||
|
||||
await expect(sut.handleMigrationSingle({ id: testAsset.id })).resolves.toBe(JobStatus.SKIPPED);
|
||||
|
||||
expect(mocks.asset.getByIds).not.toHaveBeenCalled();
|
||||
expect(mocks.storage.checkFileExists).not.toHaveBeenCalled();
|
||||
expect(mocks.storage.rename).not.toHaveBeenCalled();
|
||||
|
||||
@@ -87,9 +87,12 @@ describe(TagService.name, () => {
|
||||
|
||||
it('should create a new tag with optional color', async () => {
|
||||
mocks.tag.create.mockResolvedValue(tagStub.colorCreate);
|
||||
mocks.tag.getByValue.mockResolvedValue(void 0);
|
||||
|
||||
await expect(sut.create(authStub.admin, { name: 'tag-1', color: '#000000' })).resolves.toEqual(
|
||||
tagResponseStub.color1,
|
||||
);
|
||||
|
||||
expect(mocks.tag.create).toHaveBeenCalledWith({
|
||||
userId: authStub.admin.user.id,
|
||||
value: 'tag-1',
|
||||
@@ -168,6 +171,8 @@ describe(TagService.name, () => {
|
||||
|
||||
it('should remove a tag', async () => {
|
||||
mocks.tag.get.mockResolvedValue(tagStub.tag);
|
||||
mocks.tag.delete.mockResolvedValue();
|
||||
|
||||
await sut.remove(authStub.admin, 'tag-1');
|
||||
expect(mocks.tag.delete).toHaveBeenCalledWith('tag-1');
|
||||
});
|
||||
@@ -223,6 +228,7 @@ describe(TagService.name, () => {
|
||||
it('should accept accept ids that are new and reject the rest', async () => {
|
||||
mocks.tag.get.mockResolvedValue(tagStub.tag);
|
||||
mocks.tag.getAssetIds.mockResolvedValue(new Set(['asset-1']));
|
||||
mocks.tag.addAssetIds.mockResolvedValue();
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-2']));
|
||||
|
||||
await expect(
|
||||
@@ -242,6 +248,8 @@ describe(TagService.name, () => {
|
||||
describe('removeAssets', () => {
|
||||
it('should throw an error for an invalid id', async () => {
|
||||
mocks.tag.getAssetIds.mockResolvedValue(new Set());
|
||||
mocks.tag.removeAssetIds.mockResolvedValue();
|
||||
|
||||
await expect(sut.removeAssets(authStub.admin, 'tag-1', { ids: ['asset-1'] })).resolves.toEqual([
|
||||
{ id: 'asset-1', success: false, error: 'not_found' },
|
||||
]);
|
||||
@@ -250,6 +258,7 @@ describe(TagService.name, () => {
|
||||
it('should accept accept ids that are tagged and reject the rest', async () => {
|
||||
mocks.tag.get.mockResolvedValue(tagStub.tag);
|
||||
mocks.tag.getAssetIds.mockResolvedValue(new Set(['asset-1']));
|
||||
mocks.tag.removeAssetIds.mockResolvedValue();
|
||||
|
||||
await expect(
|
||||
sut.removeAssets(authStub.admin, 'tag-1', {
|
||||
@@ -267,7 +276,10 @@ describe(TagService.name, () => {
|
||||
|
||||
describe('handleTagCleanup', () => {
|
||||
it('should delete empty tags', async () => {
|
||||
mocks.tag.deleteEmptyTags.mockResolvedValue();
|
||||
|
||||
await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.SUCCESS);
|
||||
|
||||
expect(mocks.tag.deleteEmptyTags).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,6 +70,7 @@ describe(TimelineService.name, () => {
|
||||
|
||||
it('should include partner shared assets', async () => {
|
||||
mocks.asset.getTimeBucket.mockResolvedValue([assetStub.image]);
|
||||
mocks.partner.getAll.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
sut.getTimeBucket(authStub.admin, {
|
||||
|
||||
@@ -39,6 +39,7 @@ describe(TrashService.name, () => {
|
||||
|
||||
it('should restore a batch of assets', async () => {
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset1', 'asset2']));
|
||||
mocks.trash.restoreAll.mockResolvedValue(0);
|
||||
|
||||
await sut.restoreAssets(authStub.user1, { ids: ['asset1', 'asset2'] });
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { serverVersion } from 'src/constants';
|
||||
import { ImmichEnvironment, JobName, JobStatus, SystemMetadataKey } from 'src/enum';
|
||||
import { VersionService } from 'src/services/version.service';
|
||||
import { mockEnvData } from 'test/repositories/config.repository.mock';
|
||||
import { factory } from 'test/small.factory';
|
||||
import { newTestService, ServiceMocks } from 'test/utils';
|
||||
|
||||
const mockRelease = (version: string) => ({
|
||||
@@ -30,7 +31,12 @@ describe(VersionService.name, () => {
|
||||
|
||||
describe('onBootstrap', () => {
|
||||
it('should record a new version', async () => {
|
||||
mocks.versionHistory.getAll.mockResolvedValue([]);
|
||||
mocks.versionHistory.getLatest.mockResolvedValue(void 0);
|
||||
mocks.versionHistory.create.mockResolvedValue(factory.versionHistory());
|
||||
|
||||
await expect(sut.onBootstrap()).resolves.toBeUndefined();
|
||||
|
||||
expect(mocks.versionHistory.create).toHaveBeenCalledWith({ version: expect.any(String) });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user