mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
refactor: test mocks (#16008)
This commit is contained in:
parent
8794c84e9d
commit
735f8d661e
74 changed files with 3820 additions and 4043 deletions
|
|
@ -4,19 +4,13 @@ import { SystemConfigDto } from 'src/dtos/system-config.dto';
|
|||
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
||||
import { AssetFileEntity } from 'src/entities/asset-files.entity';
|
||||
import { AssetFileType, UserMetadataKey } from 'src/enum';
|
||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||
import { IJobRepository, INotifyAlbumUpdateJob, JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||
import { INotifyAlbumUpdateJob, JobName, JobStatus } from 'src/interfaces/job.interface';
|
||||
import { EmailTemplate } from 'src/repositories/notification.repository';
|
||||
import { NotificationService } from 'src/services/notification.service';
|
||||
import { INotificationRepository, ISystemMetadataRepository } from 'src/types';
|
||||
import { albumStub } from 'test/fixtures/album.stub';
|
||||
import { assetStub } from 'test/fixtures/asset.stub';
|
||||
import { userStub } from 'test/fixtures/user.stub';
|
||||
import { newTestService } from 'test/utils';
|
||||
import { Mocked } from 'vitest';
|
||||
import { newTestService, ServiceMocks } from 'test/utils';
|
||||
|
||||
const configs = {
|
||||
smtpDisabled: Object.freeze<SystemConfig>({
|
||||
|
|
@ -57,18 +51,10 @@ const configs = {
|
|||
|
||||
describe(NotificationService.name, () => {
|
||||
let sut: NotificationService;
|
||||
|
||||
let albumMock: Mocked<IAlbumRepository>;
|
||||
let assetMock: Mocked<IAssetRepository>;
|
||||
let eventMock: Mocked<IEventRepository>;
|
||||
let jobMock: Mocked<IJobRepository>;
|
||||
let notificationMock: Mocked<INotificationRepository>;
|
||||
let systemMock: Mocked<ISystemMetadataRepository>;
|
||||
let userMock: Mocked<IUserRepository>;
|
||||
let mocks: ServiceMocks;
|
||||
|
||||
beforeEach(() => {
|
||||
({ sut, albumMock, assetMock, eventMock, jobMock, notificationMock, systemMock, userMock } =
|
||||
newTestService(NotificationService));
|
||||
({ sut, mocks } = newTestService(NotificationService));
|
||||
});
|
||||
|
||||
it('should work', () => {
|
||||
|
|
@ -79,8 +65,8 @@ describe(NotificationService.name, () => {
|
|||
it('should emit client and server events', () => {
|
||||
const update = { oldConfig: defaults, newConfig: defaults };
|
||||
expect(sut.onConfigUpdate(update)).toBeUndefined();
|
||||
expect(eventMock.clientBroadcast).toHaveBeenCalledWith('on_config_update');
|
||||
expect(eventMock.serverSend).toHaveBeenCalledWith('config.update', update);
|
||||
expect(mocks.event.clientBroadcast).toHaveBeenCalledWith('on_config_update');
|
||||
expect(mocks.event.serverSend).toHaveBeenCalledWith('config.update', update);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -89,18 +75,18 @@ describe(NotificationService.name, () => {
|
|||
const oldConfig = configs.smtpDisabled;
|
||||
const newConfig = configs.smtpEnabled;
|
||||
|
||||
notificationMock.verifySmtp.mockResolvedValue(true);
|
||||
mocks.notification.verifySmtp.mockResolvedValue(true);
|
||||
await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow();
|
||||
expect(notificationMock.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport);
|
||||
expect(mocks.notification.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport);
|
||||
});
|
||||
|
||||
it('validates smtp config when transport changes', async () => {
|
||||
const oldConfig = configs.smtpEnabled;
|
||||
const newConfig = configs.smtpTransport;
|
||||
|
||||
notificationMock.verifySmtp.mockResolvedValue(true);
|
||||
mocks.notification.verifySmtp.mockResolvedValue(true);
|
||||
await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow();
|
||||
expect(notificationMock.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport);
|
||||
expect(mocks.notification.verifySmtp).toHaveBeenCalledWith(newConfig.notifications.smtp.transport);
|
||||
});
|
||||
|
||||
it('skips smtp validation when there are no changes', async () => {
|
||||
|
|
@ -108,7 +94,7 @@ describe(NotificationService.name, () => {
|
|||
const newConfig = { ...configs.smtpEnabled };
|
||||
|
||||
await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow();
|
||||
expect(notificationMock.verifySmtp).not.toHaveBeenCalled();
|
||||
expect(mocks.notification.verifySmtp).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('skips smtp validation with DTO when there are no changes', async () => {
|
||||
|
|
@ -116,7 +102,7 @@ describe(NotificationService.name, () => {
|
|||
const newConfig = plainToInstance(SystemConfigDto, configs.smtpEnabled);
|
||||
|
||||
await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow();
|
||||
expect(notificationMock.verifySmtp).not.toHaveBeenCalled();
|
||||
expect(mocks.notification.verifySmtp).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('skips smtp validation when smtp is disabled', async () => {
|
||||
|
|
@ -124,14 +110,14 @@ describe(NotificationService.name, () => {
|
|||
const newConfig = { ...configs.smtpDisabled };
|
||||
|
||||
await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow();
|
||||
expect(notificationMock.verifySmtp).not.toHaveBeenCalled();
|
||||
expect(mocks.notification.verifySmtp).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fail if smtp configuration is invalid', async () => {
|
||||
const oldConfig = configs.smtpDisabled;
|
||||
const newConfig = configs.smtpEnabled;
|
||||
|
||||
notificationMock.verifySmtp.mockRejectedValue(new Error('Failed validating smtp'));
|
||||
mocks.notification.verifySmtp.mockRejectedValue(new Error('Failed validating smtp'));
|
||||
await expect(sut.onConfigValidate({ oldConfig, newConfig })).rejects.toBeInstanceOf(Error);
|
||||
});
|
||||
});
|
||||
|
|
@ -139,14 +125,14 @@ describe(NotificationService.name, () => {
|
|||
describe('onAssetHide', () => {
|
||||
it('should send connected clients an event', () => {
|
||||
sut.onAssetHide({ assetId: 'asset-id', userId: 'user-id' });
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_asset_hidden', 'user-id', 'asset-id');
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_asset_hidden', 'user-id', 'asset-id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onAssetShow', () => {
|
||||
it('should queue the generate thumbnail job', async () => {
|
||||
await sut.onAssetShow({ assetId: 'asset-id', userId: 'user-id' });
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.GENERATE_THUMBNAILS,
|
||||
data: { id: 'asset-id', notify: true },
|
||||
});
|
||||
|
|
@ -156,12 +142,12 @@ describe(NotificationService.name, () => {
|
|||
describe('onUserSignupEvent', () => {
|
||||
it('skips when notify is false', async () => {
|
||||
await sut.onUserSignup({ id: '', notify: false });
|
||||
expect(jobMock.queue).not.toHaveBeenCalled();
|
||||
expect(mocks.job.queue).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should queue notify signup event if notify is true', async () => {
|
||||
await sut.onUserSignup({ id: '', notify: true });
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.NOTIFY_SIGNUP,
|
||||
data: { id: '', tempPassword: undefined },
|
||||
});
|
||||
|
|
@ -171,7 +157,7 @@ describe(NotificationService.name, () => {
|
|||
describe('onAlbumUpdateEvent', () => {
|
||||
it('should queue notify album update event', async () => {
|
||||
await sut.onAlbumUpdate({ id: 'album', recipientIds: ['42'] });
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.NOTIFY_ALBUM_UPDATE,
|
||||
data: { id: 'album', recipientIds: ['42'], delay: 300_000 },
|
||||
});
|
||||
|
|
@ -181,7 +167,7 @@ describe(NotificationService.name, () => {
|
|||
describe('onAlbumInviteEvent', () => {
|
||||
it('should queue notify album invite event', async () => {
|
||||
await sut.onAlbumInvite({ id: '', userId: '42' });
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.NOTIFY_ALBUM_INVITE,
|
||||
data: { id: '', recipientId: '42' },
|
||||
});
|
||||
|
|
@ -192,67 +178,67 @@ describe(NotificationService.name, () => {
|
|||
it('should send a on_session_delete client event', () => {
|
||||
vi.useFakeTimers();
|
||||
sut.onSessionDelete({ sessionId: 'id' });
|
||||
expect(eventMock.clientSend).not.toHaveBeenCalled();
|
||||
expect(mocks.event.clientSend).not.toHaveBeenCalled();
|
||||
|
||||
vi.advanceTimersByTime(500);
|
||||
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_session_delete', 'id', 'id');
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_session_delete', 'id', 'id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onAssetTrash', () => {
|
||||
it('should send connected clients an event', () => {
|
||||
sut.onAssetTrash({ assetId: 'asset-id', userId: 'user-id' });
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_asset_trash', 'user-id', ['asset-id']);
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_asset_trash', 'user-id', ['asset-id']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onAssetDelete', () => {
|
||||
it('should send connected clients an event', () => {
|
||||
sut.onAssetDelete({ assetId: 'asset-id', userId: 'user-id' });
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_asset_delete', 'user-id', 'asset-id');
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_asset_delete', 'user-id', 'asset-id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onAssetsTrash', () => {
|
||||
it('should send connected clients an event', () => {
|
||||
sut.onAssetsTrash({ assetIds: ['asset-id'], userId: 'user-id' });
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_asset_trash', 'user-id', ['asset-id']);
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_asset_trash', 'user-id', ['asset-id']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onAssetsRestore', () => {
|
||||
it('should send connected clients an event', () => {
|
||||
sut.onAssetsRestore({ assetIds: ['asset-id'], userId: 'user-id' });
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_asset_restore', 'user-id', ['asset-id']);
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_asset_restore', 'user-id', ['asset-id']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onStackCreate', () => {
|
||||
it('should send connected clients an event', () => {
|
||||
sut.onStackCreate({ stackId: 'stack-id', userId: 'user-id' });
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_asset_stack_update', 'user-id');
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_asset_stack_update', 'user-id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onStackUpdate', () => {
|
||||
it('should send connected clients an event', () => {
|
||||
sut.onStackUpdate({ stackId: 'stack-id', userId: 'user-id' });
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_asset_stack_update', 'user-id');
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_asset_stack_update', 'user-id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onStackDelete', () => {
|
||||
it('should send connected clients an event', () => {
|
||||
sut.onStackDelete({ stackId: 'stack-id', userId: 'user-id' });
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_asset_stack_update', 'user-id');
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_asset_stack_update', 'user-id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onStacksDelete', () => {
|
||||
it('should send connected clients an event', () => {
|
||||
sut.onStacksDelete({ stackIds: ['stack-id'], userId: 'user-id' });
|
||||
expect(eventMock.clientSend).toHaveBeenCalledWith('on_asset_stack_update', 'user-id');
|
||||
expect(mocks.event.clientSend).toHaveBeenCalledWith('on_asset_stack_update', 'user-id');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -262,8 +248,8 @@ describe(NotificationService.name, () => {
|
|||
});
|
||||
|
||||
it('should throw error if smtp validation fails', async () => {
|
||||
userMock.get.mockResolvedValue(userStub.admin);
|
||||
notificationMock.verifySmtp.mockRejectedValue('');
|
||||
mocks.user.get.mockResolvedValue(userStub.admin);
|
||||
mocks.notification.verifySmtp.mockRejectedValue('');
|
||||
|
||||
await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).rejects.toThrow(
|
||||
'Failed to verify SMTP configuration',
|
||||
|
|
@ -271,16 +257,16 @@ describe(NotificationService.name, () => {
|
|||
});
|
||||
|
||||
it('should send email to default domain', async () => {
|
||||
userMock.get.mockResolvedValue(userStub.admin);
|
||||
notificationMock.verifySmtp.mockResolvedValue(true);
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.user.get.mockResolvedValue(userStub.admin);
|
||||
mocks.notification.verifySmtp.mockResolvedValue(true);
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
|
||||
await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).resolves.not.toThrow();
|
||||
expect(notificationMock.renderEmail).toHaveBeenCalledWith({
|
||||
expect(mocks.notification.renderEmail).toHaveBeenCalledWith({
|
||||
template: EmailTemplate.TEST_EMAIL,
|
||||
data: { baseUrl: 'http://localhost:2283', displayName: userStub.admin.name },
|
||||
});
|
||||
expect(notificationMock.sendEmail).toHaveBeenCalledWith(
|
||||
expect(mocks.notification.sendEmail).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
subject: 'Test email from Immich',
|
||||
smtp: configs.smtpTransport.notifications.smtp.transport,
|
||||
|
|
@ -289,17 +275,17 @@ describe(NotificationService.name, () => {
|
|||
});
|
||||
|
||||
it('should send email to external domain', async () => {
|
||||
userMock.get.mockResolvedValue(userStub.admin);
|
||||
notificationMock.verifySmtp.mockResolvedValue(true);
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
systemMock.get.mockResolvedValue({ server: { externalDomain: 'https://demo.immich.app' } });
|
||||
mocks.user.get.mockResolvedValue(userStub.admin);
|
||||
mocks.notification.verifySmtp.mockResolvedValue(true);
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.systemMetadata.get.mockResolvedValue({ server: { externalDomain: 'https://demo.immich.app' } });
|
||||
|
||||
await expect(sut.sendTestEmail('', configs.smtpTransport.notifications.smtp)).resolves.not.toThrow();
|
||||
expect(notificationMock.renderEmail).toHaveBeenCalledWith({
|
||||
expect(mocks.notification.renderEmail).toHaveBeenCalledWith({
|
||||
template: EmailTemplate.TEST_EMAIL,
|
||||
data: { baseUrl: 'https://demo.immich.app', displayName: userStub.admin.name },
|
||||
});
|
||||
expect(notificationMock.sendEmail).toHaveBeenCalledWith(
|
||||
expect(mocks.notification.sendEmail).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
subject: 'Test email from Immich',
|
||||
smtp: configs.smtpTransport.notifications.smtp.transport,
|
||||
|
|
@ -308,18 +294,18 @@ describe(NotificationService.name, () => {
|
|||
});
|
||||
|
||||
it('should send email with replyTo', async () => {
|
||||
userMock.get.mockResolvedValue(userStub.admin);
|
||||
notificationMock.verifySmtp.mockResolvedValue(true);
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.user.get.mockResolvedValue(userStub.admin);
|
||||
mocks.notification.verifySmtp.mockResolvedValue(true);
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
|
||||
await expect(
|
||||
sut.sendTestEmail('', { ...configs.smtpTransport.notifications.smtp, replyTo: 'demo@immich.app' }),
|
||||
).resolves.not.toThrow();
|
||||
expect(notificationMock.renderEmail).toHaveBeenCalledWith({
|
||||
expect(mocks.notification.renderEmail).toHaveBeenCalledWith({
|
||||
template: EmailTemplate.TEST_EMAIL,
|
||||
data: { baseUrl: 'http://localhost:2283', displayName: userStub.admin.name },
|
||||
});
|
||||
expect(notificationMock.sendEmail).toHaveBeenCalledWith(
|
||||
expect(mocks.notification.sendEmail).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
subject: 'Test email from Immich',
|
||||
smtp: configs.smtpTransport.notifications.smtp.transport,
|
||||
|
|
@ -335,12 +321,12 @@ describe(NotificationService.name, () => {
|
|||
});
|
||||
|
||||
it('should be successful', async () => {
|
||||
userMock.get.mockResolvedValue(userStub.admin);
|
||||
systemMock.get.mockResolvedValue({ server: {} });
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.user.get.mockResolvedValue(userStub.admin);
|
||||
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
|
||||
await expect(sut.handleUserSignup({ id: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SEND_EMAIL,
|
||||
data: expect.objectContaining({ subject: 'Welcome to Immich' }),
|
||||
});
|
||||
|
|
@ -350,19 +336,19 @@ describe(NotificationService.name, () => {
|
|||
describe('handleAlbumInvite', () => {
|
||||
it('should skip if album could not be found', async () => {
|
||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SKIPPED);
|
||||
expect(userMock.get).not.toHaveBeenCalled();
|
||||
expect(mocks.user.get).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip if recipient could not be found', async () => {
|
||||
albumMock.getById.mockResolvedValue(albumStub.empty);
|
||||
mocks.album.getById.mockResolvedValue(albumStub.empty);
|
||||
|
||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SKIPPED);
|
||||
expect(assetMock.getById).not.toHaveBeenCalled();
|
||||
expect(mocks.asset.getById).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip if the recipient has email notifications disabled', async () => {
|
||||
albumMock.getById.mockResolvedValue(albumStub.empty);
|
||||
userMock.get.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue(albumStub.empty);
|
||||
mocks.user.get.mockResolvedValue({
|
||||
...userStub.user1,
|
||||
metadata: [
|
||||
{
|
||||
|
|
@ -378,8 +364,8 @@ describe(NotificationService.name, () => {
|
|||
});
|
||||
|
||||
it('should skip if the recipient has email notifications for album invite disabled', async () => {
|
||||
albumMock.getById.mockResolvedValue(albumStub.empty);
|
||||
userMock.get.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue(albumStub.empty);
|
||||
mocks.user.get.mockResolvedValue({
|
||||
...userStub.user1,
|
||||
metadata: [
|
||||
{
|
||||
|
|
@ -395,8 +381,8 @@ describe(NotificationService.name, () => {
|
|||
});
|
||||
|
||||
it('should send invite email', async () => {
|
||||
albumMock.getById.mockResolvedValue(albumStub.empty);
|
||||
userMock.get.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue(albumStub.empty);
|
||||
mocks.user.get.mockResolvedValue({
|
||||
...userStub.user1,
|
||||
metadata: [
|
||||
{
|
||||
|
|
@ -407,19 +393,19 @@ describe(NotificationService.name, () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
systemMock.get.mockResolvedValue({ server: {} });
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
|
||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SEND_EMAIL,
|
||||
data: expect.objectContaining({ subject: expect.stringContaining('You have been added to a shared album') }),
|
||||
});
|
||||
});
|
||||
|
||||
it('should send invite email without album thumbnail if thumbnail asset does not exist', async () => {
|
||||
albumMock.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail);
|
||||
userMock.get.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail);
|
||||
mocks.user.get.mockResolvedValue({
|
||||
...userStub.user1,
|
||||
metadata: [
|
||||
{
|
||||
|
|
@ -430,14 +416,14 @@ describe(NotificationService.name, () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
systemMock.get.mockResolvedValue({ server: {} });
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
|
||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||
expect(assetMock.getById).toHaveBeenCalledWith(albumStub.emptyWithValidThumbnail.albumThumbnailAssetId, {
|
||||
expect(mocks.asset.getById).toHaveBeenCalledWith(albumStub.emptyWithValidThumbnail.albumThumbnailAssetId, {
|
||||
files: true,
|
||||
});
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SEND_EMAIL,
|
||||
data: expect.objectContaining({
|
||||
subject: expect.stringContaining('You have been added to a shared album'),
|
||||
|
|
@ -447,8 +433,8 @@ describe(NotificationService.name, () => {
|
|||
});
|
||||
|
||||
it('should send invite email with album thumbnail as jpeg', async () => {
|
||||
albumMock.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail);
|
||||
userMock.get.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail);
|
||||
mocks.user.get.mockResolvedValue({
|
||||
...userStub.user1,
|
||||
metadata: [
|
||||
{
|
||||
|
|
@ -459,18 +445,18 @@ describe(NotificationService.name, () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
systemMock.get.mockResolvedValue({ server: {} });
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
assetMock.getById.mockResolvedValue({
|
||||
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.asset.getById.mockResolvedValue({
|
||||
...assetStub.image,
|
||||
files: [{ assetId: 'asset-id', type: AssetFileType.THUMBNAIL, path: 'path-to-thumb.jpg' } as AssetFileEntity],
|
||||
});
|
||||
|
||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||
expect(assetMock.getById).toHaveBeenCalledWith(albumStub.emptyWithValidThumbnail.albumThumbnailAssetId, {
|
||||
expect(mocks.asset.getById).toHaveBeenCalledWith(albumStub.emptyWithValidThumbnail.albumThumbnailAssetId, {
|
||||
files: true,
|
||||
});
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SEND_EMAIL,
|
||||
data: expect.objectContaining({
|
||||
subject: expect.stringContaining('You have been added to a shared album'),
|
||||
|
|
@ -480,8 +466,8 @@ describe(NotificationService.name, () => {
|
|||
});
|
||||
|
||||
it('should send invite email with album thumbnail and arbitrary extension', async () => {
|
||||
albumMock.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail);
|
||||
userMock.get.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail);
|
||||
mocks.user.get.mockResolvedValue({
|
||||
...userStub.user1,
|
||||
metadata: [
|
||||
{
|
||||
|
|
@ -492,15 +478,15 @@ describe(NotificationService.name, () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
systemMock.get.mockResolvedValue({ server: {} });
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
assetMock.getById.mockResolvedValue(assetStub.image);
|
||||
mocks.systemMetadata.get.mockResolvedValue({ server: {} });
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.image);
|
||||
|
||||
await expect(sut.handleAlbumInvite({ id: '', recipientId: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||
expect(assetMock.getById).toHaveBeenCalledWith(albumStub.emptyWithValidThumbnail.albumThumbnailAssetId, {
|
||||
expect(mocks.asset.getById).toHaveBeenCalledWith(albumStub.emptyWithValidThumbnail.albumThumbnailAssetId, {
|
||||
files: true,
|
||||
});
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.SEND_EMAIL,
|
||||
data: expect.objectContaining({
|
||||
subject: expect.stringContaining('You have been added to a shared album'),
|
||||
|
|
@ -513,35 +499,35 @@ describe(NotificationService.name, () => {
|
|||
describe('handleAlbumUpdate', () => {
|
||||
it('should skip if album could not be found', async () => {
|
||||
await expect(sut.handleAlbumUpdate({ id: '', recipientIds: ['1'] })).resolves.toBe(JobStatus.SKIPPED);
|
||||
expect(userMock.get).not.toHaveBeenCalled();
|
||||
expect(mocks.user.get).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip if owner could not be found', async () => {
|
||||
albumMock.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail);
|
||||
mocks.album.getById.mockResolvedValue(albumStub.emptyWithValidThumbnail);
|
||||
|
||||
await expect(sut.handleAlbumUpdate({ id: '', recipientIds: ['1'] })).resolves.toBe(JobStatus.SKIPPED);
|
||||
expect(systemMock.get).not.toHaveBeenCalled();
|
||||
expect(mocks.systemMetadata.get).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip recipient that could not be looked up', async () => {
|
||||
albumMock.getById.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue({
|
||||
...albumStub.emptyWithValidThumbnail,
|
||||
albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUserEntity],
|
||||
});
|
||||
userMock.get.mockResolvedValueOnce(userStub.user1);
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.user.get.mockResolvedValueOnce(userStub.user1);
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
|
||||
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
||||
expect(userMock.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||
expect(notificationMock.renderEmail).not.toHaveBeenCalled();
|
||||
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||
expect(mocks.notification.renderEmail).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip recipient with disabled email notifications', async () => {
|
||||
albumMock.getById.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue({
|
||||
...albumStub.emptyWithValidThumbnail,
|
||||
albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUserEntity],
|
||||
});
|
||||
userMock.get.mockResolvedValue({
|
||||
mocks.user.get.mockResolvedValue({
|
||||
...userStub.user1,
|
||||
metadata: [
|
||||
{
|
||||
|
|
@ -552,19 +538,19 @@ describe(NotificationService.name, () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
|
||||
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
||||
expect(userMock.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||
expect(notificationMock.renderEmail).not.toHaveBeenCalled();
|
||||
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||
expect(mocks.notification.renderEmail).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip recipient with disabled email notifications for the album update event', async () => {
|
||||
albumMock.getById.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue({
|
||||
...albumStub.emptyWithValidThumbnail,
|
||||
albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUserEntity],
|
||||
});
|
||||
userMock.get.mockResolvedValue({
|
||||
mocks.user.get.mockResolvedValue({
|
||||
...userStub.user1,
|
||||
metadata: [
|
||||
{
|
||||
|
|
@ -575,31 +561,31 @@ describe(NotificationService.name, () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
|
||||
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
||||
expect(userMock.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||
expect(notificationMock.renderEmail).not.toHaveBeenCalled();
|
||||
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||
expect(mocks.notification.renderEmail).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should send email', async () => {
|
||||
albumMock.getById.mockResolvedValue({
|
||||
mocks.album.getById.mockResolvedValue({
|
||||
...albumStub.emptyWithValidThumbnail,
|
||||
albumUsers: [{ user: { id: userStub.user1.id } } as AlbumUserEntity],
|
||||
});
|
||||
userMock.get.mockResolvedValue(userStub.user1);
|
||||
notificationMock.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
mocks.user.get.mockResolvedValue(userStub.user1);
|
||||
mocks.notification.renderEmail.mockResolvedValue({ html: '', text: '' });
|
||||
|
||||
await sut.handleAlbumUpdate({ id: '', recipientIds: [userStub.user1.id] });
|
||||
expect(userMock.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||
expect(notificationMock.renderEmail).toHaveBeenCalled();
|
||||
expect(jobMock.queue).toHaveBeenCalled();
|
||||
expect(mocks.user.get).toHaveBeenCalledWith(userStub.user1.id, { withDeleted: false });
|
||||
expect(mocks.notification.renderEmail).toHaveBeenCalled();
|
||||
expect(mocks.job.queue).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should add new recipients for new images if job is already queued', async () => {
|
||||
jobMock.removeJob.mockResolvedValue({ id: '1', recipientIds: ['2', '3', '4'] } as INotifyAlbumUpdateJob);
|
||||
mocks.job.removeJob.mockResolvedValue({ id: '1', recipientIds: ['2', '3', '4'] } as INotifyAlbumUpdateJob);
|
||||
await sut.onAlbumUpdate({ id: '1', recipientIds: ['1', '2', '3'] } as INotifyAlbumUpdateJob);
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
expect(mocks.job.queue).toHaveBeenCalledWith({
|
||||
name: JobName.NOTIFY_ALBUM_UPDATE,
|
||||
data: {
|
||||
id: '1',
|
||||
|
|
@ -612,26 +598,32 @@ describe(NotificationService.name, () => {
|
|||
|
||||
describe('handleSendEmail', () => {
|
||||
it('should skip if smtp notifications are disabled', async () => {
|
||||
systemMock.get.mockResolvedValue({ notifications: { smtp: { enabled: false } } });
|
||||
mocks.systemMetadata.get.mockResolvedValue({ notifications: { smtp: { enabled: false } } });
|
||||
await expect(sut.handleSendEmail({ html: '', subject: '', text: '', to: '' })).resolves.toBe(JobStatus.SKIPPED);
|
||||
});
|
||||
|
||||
it('should send mail successfully', async () => {
|
||||
systemMock.get.mockResolvedValue({ notifications: { smtp: { enabled: true, from: 'test@immich.app' } } });
|
||||
notificationMock.sendEmail.mockResolvedValue({ messageId: '', response: '' });
|
||||
mocks.systemMetadata.get.mockResolvedValue({
|
||||
notifications: { smtp: { enabled: true, from: 'test@immich.app' } },
|
||||
});
|
||||
mocks.notification.sendEmail.mockResolvedValue({ messageId: '', response: '' });
|
||||
|
||||
await expect(sut.handleSendEmail({ html: '', subject: '', text: '', to: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||
expect(notificationMock.sendEmail).toHaveBeenCalledWith(expect.objectContaining({ replyTo: 'test@immich.app' }));
|
||||
expect(mocks.notification.sendEmail).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ replyTo: 'test@immich.app' }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should send mail with replyTo successfully', async () => {
|
||||
systemMock.get.mockResolvedValue({
|
||||
mocks.systemMetadata.get.mockResolvedValue({
|
||||
notifications: { smtp: { enabled: true, from: 'test@immich.app', replyTo: 'demo@immich.app' } },
|
||||
});
|
||||
notificationMock.sendEmail.mockResolvedValue({ messageId: '', response: '' });
|
||||
mocks.notification.sendEmail.mockResolvedValue({ messageId: '', response: '' });
|
||||
|
||||
await expect(sut.handleSendEmail({ html: '', subject: '', text: '', to: '' })).resolves.toBe(JobStatus.SUCCESS);
|
||||
expect(notificationMock.sendEmail).toHaveBeenCalledWith(expect.objectContaining({ replyTo: 'demo@immich.app' }));
|
||||
expect(mocks.notification.sendEmail).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ replyTo: 'demo@immich.app' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue