1
0
mirror of https://github.com/immich-app/immich.git synced 2025-08-09 23:17:29 +02:00

refactor: medium tests (#19537)

This commit is contained in:
Jason Rasmussen
2025-06-26 15:32:06 -04:00
committed by GitHub
parent b96c95beda
commit 3105094a3d
19 changed files with 1579 additions and 2253 deletions

View File

@@ -2,72 +2,65 @@ import { Kysely } from 'kysely';
import { DateTime } from 'luxon';
import { DB } from 'src/db';
import { ImmichEnvironment, JobName, JobStatus } from 'src/enum';
import { ConfigRepository } from 'src/repositories/config.repository';
import { CryptoRepository } from 'src/repositories/crypto.repository';
import { JobRepository } from 'src/repositories/job.repository';
import { LoggingRepository } from 'src/repositories/logging.repository';
import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository';
import { UserRepository } from 'src/repositories/user.repository';
import { UserService } from 'src/services/user.service';
import { mediumFactory, newMediumService } from 'test/medium.factory';
import { factory } from 'test/small.factory';
import { getKyselyDB } from 'test/utils';
describe(UserService.name, () => {
let defaultDatabase: Kysely<DB>;
let defaultDatabase: Kysely<DB>;
const createSut = (db?: Kysely<DB>) => {
process.env.IMMICH_ENV = ImmichEnvironment.TESTING;
const setup = (db?: Kysely<DB>) => {
process.env.IMMICH_ENV = ImmichEnvironment.TESTING;
return newMediumService(UserService, {
database: db || defaultDatabase,
repos: {
user: 'real',
crypto: 'real',
config: 'real',
job: 'mock',
systemMetadata: 'real',
},
});
};
beforeAll(async () => {
defaultDatabase = await getKyselyDB();
const { repos } = createSut();
await repos.user.create({ isAdmin: true, email: 'admin@immich.cloud' });
return newMediumService(UserService, {
database: db || defaultDatabase,
real: [CryptoRepository, ConfigRepository, SystemMetadataRepository, UserRepository],
mock: [LoggingRepository, JobRepository],
});
};
beforeAll(async () => {
defaultDatabase = await getKyselyDB();
const { ctx } = setup();
await ctx.newUser({ isAdmin: true, email: 'admin@immich.cloud' });
});
describe(UserService.name, () => {
describe('create', () => {
it('should create a user', async () => {
const { sut } = createSut();
const { sut } = setup();
const user = mediumFactory.userInsert();
await expect(sut.createUser({ name: user.name, email: user.email })).resolves.toEqual(
expect.objectContaining({ name: user.name, email: user.email }),
);
});
it('should reject user with duplicate email', async () => {
const { sut } = createSut();
const { sut } = setup();
const user = mediumFactory.userInsert();
await expect(sut.createUser({ email: user.email })).resolves.toMatchObject({ email: user.email });
await expect(sut.createUser({ email: user.email })).rejects.toThrow('User exists');
});
it('should not return password', async () => {
const { sut } = createSut();
const { sut } = setup();
const dto = mediumFactory.userInsert({ password: 'password' });
const user = await sut.createUser({ email: dto.email, password: 'password' });
expect((user as any).password).toBeUndefined();
});
});
describe('search', () => {
it('should get users', async () => {
const { sut, repos } = createSut();
const user1 = mediumFactory.userInsert();
const user2 = mediumFactory.userInsert();
await Promise.all([repos.user.create(user1), repos.user.create(user2)]);
const { sut, ctx } = setup();
const { user: user1 } = await ctx.newUser();
const { user: user2 } = await ctx.newUser();
const auth = factory.auth({ user: user1 });
await expect(sut.search(auth)).resolves.toEqual(
@@ -81,10 +74,8 @@ describe(UserService.name, () => {
describe('get', () => {
it('should get a user', async () => {
const { sut, repos } = createSut();
const user = mediumFactory.userInsert();
await repos.user.create(user);
const { sut, ctx } = setup();
const { user } = await ctx.newUser();
await expect(sut.get(user.id)).resolves.toEqual(
expect.objectContaining({
@@ -96,11 +87,8 @@ describe(UserService.name, () => {
});
it('should not return password', async () => {
const { sut, repos } = createSut();
const user = mediumFactory.userInsert();
await repos.user.create(user);
const { sut, ctx } = setup();
const { user } = await ctx.newUser();
const result = await sut.get(user.id);
expect((result as any).password).toBeUndefined();
@@ -109,10 +97,9 @@ describe(UserService.name, () => {
describe('updateMe', () => {
it('should update a user', async () => {
const { sut, repos: repositories } = createSut();
const before = await repositories.user.create(mediumFactory.userInsert());
const auth = factory.auth({ user: { id: before.id } });
const { sut, ctx } = setup();
const { user, result: before } = await ctx.newUser();
const auth = factory.auth({ user: { id: user.id } });
const after = await sut.updateMe(auth, { name: `${before.name} Updated` });
expect(before.updatedAt).toBeDefined();
@@ -128,17 +115,13 @@ describe(UserService.name, () => {
activationKey:
'KuX8KsktrBSiXpQMAH0zLgA5SpijXVr_PDkzLdWUlAogCTMBZ0I3KCHXK0eE9EEd7harxup8_EHMeqAWeHo5VQzol6LGECpFv585U9asXD4Zc-UXt3mhJr2uhazqipBIBwJA2YhmUCDy8hiyiGsukDQNu9Rg9C77UeoKuZBWVjWUBWG0mc1iRqfvF0faVM20w53czAzlhaMxzVGc3Oimbd7xi_CAMSujF_2y8QpA3X2fOVkQkzdcH9lV0COejl7IyH27zQQ9HrlrXv3Lai5Hw67kNkaSjmunVBxC5PS0TpKoc9SfBJMaAGWnaDbjhjYUrm-8nIDQnoeEAidDXVAdPw',
};
const { sut, repos } = createSut();
const user = mediumFactory.userInsert();
await repos.user.create(user);
const { sut, ctx } = setup();
const { user } = await ctx.newUser();
const auth = factory.auth({ user: { id: user.id } });
await expect(sut.getLicense(auth)).rejects.toThrowError();
const after = await sut.setLicense(auth, license);
expect(after.licenseKey).toEqual(license.licenseKey);
expect(after.activationKey).toEqual(license.activationKey);
const getResponse = await sut.getLicense(auth);
expect(getResponse).toEqual(after);
});
@@ -146,7 +129,7 @@ describe(UserService.name, () => {
describe.sequential('handleUserDeleteCheck', () => {
beforeEach(async () => {
const { sut } = createSut();
const { sut } = setup();
// These tests specifically have to be sequential otherwise we hit race conditions with config changes applying in incorrect tests
const config = await sut.getConfig({ withCache: false });
config.user.deleteDelay = 7;
@@ -154,52 +137,43 @@ describe(UserService.name, () => {
});
it('should work when there are no deleted users', async () => {
const { sut, mocks } = createSut();
mocks.job.queueAll.mockResolvedValue(void 0);
const { sut, ctx } = setup();
const jobMock = ctx.getMock(JobRepository);
jobMock.queueAll.mockResolvedValue(void 0);
await expect(sut.handleUserDeleteCheck()).resolves.toEqual(JobStatus.SUCCESS);
expect(mocks.job.queueAll).toHaveBeenCalledExactlyOnceWith([]);
expect(jobMock.queueAll).toHaveBeenCalledExactlyOnceWith([]);
});
it('should work when there is a user to delete', async () => {
const { sut, repos, mocks } = createSut(await getKyselyDB());
mocks.job.queueAll.mockResolvedValue(void 0);
const user = mediumFactory.userInsert({ deletedAt: DateTime.now().minus({ days: 60 }).toJSDate() });
await repos.user.create(user);
const { sut, ctx } = setup(await getKyselyDB());
const jobMock = ctx.getMock(JobRepository);
const { user } = await ctx.newUser({ deletedAt: DateTime.now().minus({ days: 60 }).toJSDate() });
jobMock.queueAll.mockResolvedValue(void 0);
await expect(sut.handleUserDeleteCheck()).resolves.toEqual(JobStatus.SUCCESS);
expect(mocks.job.queueAll).toHaveBeenCalledExactlyOnceWith([
expect(jobMock.queueAll).toHaveBeenCalledExactlyOnceWith([
{ name: JobName.USER_DELETION, data: { id: user.id } },
]);
});
it('should skip a recently deleted user', async () => {
const { sut, repos, mocks } = createSut(await getKyselyDB());
mocks.job.queueAll.mockResolvedValue(void 0);
const user = mediumFactory.userInsert({ deletedAt: DateTime.now().minus({ days: 5 }).toJSDate() });
await repos.user.create(user);
const { sut, ctx } = setup(await getKyselyDB());
const jobMock = ctx.getMock(JobRepository);
await ctx.newUser({ deletedAt: DateTime.now().minus({ days: 5 }).toJSDate() });
jobMock.queueAll.mockResolvedValue(void 0);
await expect(sut.handleUserDeleteCheck()).resolves.toEqual(JobStatus.SUCCESS);
expect(mocks.job.queueAll).toHaveBeenCalledExactlyOnceWith([]);
expect(jobMock.queueAll).toHaveBeenCalledExactlyOnceWith([]);
});
it('should respect a custom user delete delay', async () => {
const { sut, repos, mocks } = createSut(await getKyselyDB());
mocks.job.queueAll.mockResolvedValue(void 0);
const user = mediumFactory.userInsert({ deletedAt: DateTime.now().minus({ days: 25 }).toJSDate() });
await repos.user.create(user);
const { sut, ctx } = setup(await getKyselyDB());
const jobMock = ctx.getMock(JobRepository);
await ctx.newUser({ deletedAt: DateTime.now().minus({ days: 25 }).toJSDate() });
jobMock.queueAll.mockResolvedValue(void 0);
const config = await sut.getConfig({ withCache: false });
config.user.deleteDelay = 30;
await sut.updateConfig(config);
await expect(sut.handleUserDeleteCheck()).resolves.toEqual(JobStatus.SUCCESS);
expect(mocks.job.queueAll).toHaveBeenCalledExactlyOnceWith([]);
expect(jobMock.queueAll).toHaveBeenCalledExactlyOnceWith([]);
});
});
});