mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
refactor: test mocks (#16008)
This commit is contained in:
parent
8794c84e9d
commit
735f8d661e
74 changed files with 3820 additions and 4043 deletions
|
|
@ -12,13 +12,11 @@ import {
|
|||
VideoCodec,
|
||||
VideoContainer,
|
||||
} from 'src/enum';
|
||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||
import { QueueName } from 'src/interfaces/job.interface';
|
||||
import { SystemConfigService } from 'src/services/system-config.service';
|
||||
import { DeepPartial, IConfigRepository, ILoggingRepository, ISystemMetadataRepository } from 'src/types';
|
||||
import { DeepPartial } from 'src/types';
|
||||
import { mockEnvData } from 'test/repositories/config.repository.mock';
|
||||
import { newTestService } from 'test/utils';
|
||||
import { Mocked } from 'vitest';
|
||||
import { newTestService, ServiceMocks } from 'test/utils';
|
||||
|
||||
const partialConfig = {
|
||||
ffmpeg: { crf: 30 },
|
||||
|
|
@ -198,14 +196,10 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
|||
|
||||
describe(SystemConfigService.name, () => {
|
||||
let sut: SystemConfigService;
|
||||
|
||||
let configMock: Mocked<IConfigRepository>;
|
||||
let eventMock: Mocked<IEventRepository>;
|
||||
let loggerMock: Mocked<ILoggingRepository>;
|
||||
let systemMock: Mocked<ISystemMetadataRepository>;
|
||||
let mocks: ServiceMocks;
|
||||
|
||||
beforeEach(() => {
|
||||
({ sut, configMock, eventMock, loggerMock, systemMock } = newTestService(SystemConfigService));
|
||||
({ sut, mocks } = newTestService(SystemConfigService));
|
||||
});
|
||||
|
||||
it('should work', () => {
|
||||
|
|
@ -214,22 +208,22 @@ describe(SystemConfigService.name, () => {
|
|||
|
||||
describe('getDefaults', () => {
|
||||
it('should return the default config', () => {
|
||||
systemMock.get.mockResolvedValue(partialConfig);
|
||||
mocks.systemMetadata.get.mockResolvedValue(partialConfig);
|
||||
|
||||
expect(sut.getDefaults()).toEqual(defaults);
|
||||
expect(systemMock.get).not.toHaveBeenCalled();
|
||||
expect(mocks.systemMetadata.get).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConfig', () => {
|
||||
it('should return the default config', async () => {
|
||||
systemMock.get.mockResolvedValue({});
|
||||
mocks.systemMetadata.get.mockResolvedValue({});
|
||||
|
||||
await expect(sut.getSystemConfig()).resolves.toEqual(defaults);
|
||||
});
|
||||
|
||||
it('should merge the overrides', async () => {
|
||||
systemMock.get.mockResolvedValue({
|
||||
mocks.systemMetadata.get.mockResolvedValue({
|
||||
ffmpeg: { crf: 30 },
|
||||
oauth: { autoLaunch: true },
|
||||
trash: { days: 10 },
|
||||
|
|
@ -240,17 +234,17 @@ describe(SystemConfigService.name, () => {
|
|||
});
|
||||
|
||||
it('should load the config from a json file', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify(partialConfig));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(partialConfig));
|
||||
|
||||
await expect(sut.getSystemConfig()).resolves.toEqual(updatedConfig);
|
||||
|
||||
expect(systemMock.readFile).toHaveBeenCalledWith('immich-config.json');
|
||||
expect(mocks.systemMetadata.readFile).toHaveBeenCalledWith('immich-config.json');
|
||||
});
|
||||
|
||||
it('should transform booleans', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify({ ffmpeg: { twoPass: 'false' } }));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({ ffmpeg: { twoPass: 'false' } }));
|
||||
|
||||
await expect(sut.getSystemConfig()).resolves.toMatchObject({
|
||||
ffmpeg: expect.objectContaining({ twoPass: false }),
|
||||
|
|
@ -258,8 +252,8 @@ describe(SystemConfigService.name, () => {
|
|||
});
|
||||
|
||||
it('should transform numbers', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify({ ffmpeg: { threads: '42' } }));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({ ffmpeg: { threads: '42' } }));
|
||||
|
||||
await expect(sut.getSystemConfig()).resolves.toMatchObject({
|
||||
ffmpeg: expect.objectContaining({ threads: 42 }),
|
||||
|
|
@ -267,8 +261,10 @@ describe(SystemConfigService.name, () => {
|
|||
});
|
||||
|
||||
it('should accept valid cron expressions', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify({ library: { scan: { cronExpression: '0 0 * * *' } } }));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(
|
||||
JSON.stringify({ library: { scan: { cronExpression: '0 0 * * *' } } }),
|
||||
);
|
||||
|
||||
await expect(sut.getSystemConfig()).resolves.toMatchObject({
|
||||
library: {
|
||||
|
|
@ -281,8 +277,8 @@ describe(SystemConfigService.name, () => {
|
|||
});
|
||||
|
||||
it('should reject invalid cron expressions', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify({ library: { scan: { cronExpression: 'foo' } } }));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({ library: { scan: { cronExpression: 'foo' } } }));
|
||||
|
||||
await expect(sut.getSystemConfig()).rejects.toThrow(
|
||||
'library.scan.cronExpression has failed the following constraints: cronValidator',
|
||||
|
|
@ -290,22 +286,22 @@ describe(SystemConfigService.name, () => {
|
|||
});
|
||||
|
||||
it('should log errors with the config file', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
|
||||
systemMock.readFile.mockResolvedValue(`{ "ffmpeg2": true, "ffmpeg2": true }`);
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(`{ "ffmpeg2": true, "ffmpeg2": true }`);
|
||||
|
||||
await expect(sut.getSystemConfig()).rejects.toBeInstanceOf(Error);
|
||||
|
||||
expect(systemMock.readFile).toHaveBeenCalledWith('immich-config.json');
|
||||
expect(loggerMock.error).toHaveBeenCalledTimes(2);
|
||||
expect(loggerMock.error.mock.calls[0][0]).toEqual('Unable to load configuration file: immich-config.json');
|
||||
expect(loggerMock.error.mock.calls[1][0].toString()).toEqual(
|
||||
expect(mocks.systemMetadata.readFile).toHaveBeenCalledWith('immich-config.json');
|
||||
expect(mocks.logger.error).toHaveBeenCalledTimes(2);
|
||||
expect(mocks.logger.error.mock.calls[0][0]).toEqual('Unable to load configuration file: immich-config.json');
|
||||
expect(mocks.logger.error.mock.calls[1][0].toString()).toEqual(
|
||||
expect.stringContaining('YAMLException: duplicated mapping key (1:20)'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should load the config from a yaml file', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.yaml' }));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.yaml' }));
|
||||
const partialConfig = `
|
||||
ffmpeg:
|
||||
crf: 30
|
||||
|
|
@ -316,26 +312,26 @@ describe(SystemConfigService.name, () => {
|
|||
user:
|
||||
deleteDelay: 15
|
||||
`;
|
||||
systemMock.readFile.mockResolvedValue(partialConfig);
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(partialConfig);
|
||||
|
||||
await expect(sut.getSystemConfig()).resolves.toEqual(updatedConfig);
|
||||
|
||||
expect(systemMock.readFile).toHaveBeenCalledWith('immich-config.yaml');
|
||||
expect(mocks.systemMetadata.readFile).toHaveBeenCalledWith('immich-config.yaml');
|
||||
});
|
||||
|
||||
it('should accept an empty configuration file', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify({}));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({}));
|
||||
|
||||
await expect(sut.getSystemConfig()).resolves.toEqual(defaults);
|
||||
|
||||
expect(systemMock.readFile).toHaveBeenCalledWith('immich-config.json');
|
||||
expect(mocks.systemMetadata.readFile).toHaveBeenCalledWith('immich-config.json');
|
||||
});
|
||||
|
||||
it('should allow underscores in the machine learning url', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
const partialConfig = { machineLearning: { urls: ['immich_machine_learning'] } };
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify(partialConfig));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(partialConfig));
|
||||
|
||||
const config = await sut.getSystemConfig();
|
||||
expect(config.machineLearning.urls).toEqual(['immich_machine_learning']);
|
||||
|
|
@ -349,9 +345,9 @@ describe(SystemConfigService.name, () => {
|
|||
|
||||
for (const { should, externalDomain, result } of externalDomainTests) {
|
||||
it(`should normalize an external domain ${should}`, async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
const partialConfig = { server: { externalDomain } };
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify(partialConfig));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(partialConfig));
|
||||
|
||||
const config = await sut.getSystemConfig();
|
||||
expect(config.server.externalDomain).toEqual(result ?? 'https://demo.immich.app');
|
||||
|
|
@ -359,14 +355,14 @@ describe(SystemConfigService.name, () => {
|
|||
}
|
||||
|
||||
it('should warn for unknown options in yaml', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.yaml' }));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.yaml' }));
|
||||
const partialConfig = `
|
||||
unknownOption: true
|
||||
`;
|
||||
systemMock.readFile.mockResolvedValue(partialConfig);
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(partialConfig);
|
||||
|
||||
await sut.getSystemConfig();
|
||||
expect(loggerMock.warn).toHaveBeenCalled();
|
||||
expect(mocks.logger.warn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const tests = [
|
||||
|
|
@ -380,12 +376,12 @@ describe(SystemConfigService.name, () => {
|
|||
|
||||
for (const test of tests) {
|
||||
it(`should ${test.should}`, async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify(test.config));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(test.config));
|
||||
|
||||
if (test.warn) {
|
||||
await sut.getSystemConfig();
|
||||
expect(loggerMock.warn).toHaveBeenCalled();
|
||||
expect(mocks.logger.warn).toHaveBeenCalled();
|
||||
} else {
|
||||
await expect(sut.getSystemConfig()).rejects.toBeInstanceOf(Error);
|
||||
}
|
||||
|
|
@ -395,19 +391,19 @@ describe(SystemConfigService.name, () => {
|
|||
|
||||
describe('updateConfig', () => {
|
||||
it('should update the config and emit an event', async () => {
|
||||
systemMock.get.mockResolvedValue(partialConfig);
|
||||
mocks.systemMetadata.get.mockResolvedValue(partialConfig);
|
||||
await expect(sut.updateSystemConfig(updatedConfig)).resolves.toEqual(updatedConfig);
|
||||
expect(eventMock.emit).toHaveBeenCalledWith(
|
||||
expect(mocks.event.emit).toHaveBeenCalledWith(
|
||||
'config.update',
|
||||
expect.objectContaining({ oldConfig: expect.any(Object), newConfig: updatedConfig }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if a config file is in use', async () => {
|
||||
configMock.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
systemMock.readFile.mockResolvedValue(JSON.stringify({}));
|
||||
mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' }));
|
||||
mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({}));
|
||||
await expect(sut.updateSystemConfig(defaults)).rejects.toBeInstanceOf(BadRequestException);
|
||||
expect(systemMock.set).not.toHaveBeenCalled();
|
||||
expect(mocks.systemMetadata.set).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue