Messaging-unit-tests (#11467)

# Unit test on the Messaging Module

Initially the issue was to create integration test for the messaging
module but after speaking with the core team, we decided to go for an
easier implementation: unit test only,

We decided to focus our test on three main components of the module :  
- message list
- message import
- message save & create contact

Fixes https://github.com/twentyhq/core-team-issues/issues/56
This commit is contained in:
Guillim
2025-04-09 17:57:43 +02:00
committed by GitHub
parent e23688cb41
commit 9f4e8c046f
5 changed files with 851 additions and 3 deletions

View File

@ -157,7 +157,6 @@ export class MessagingMessageListFetchJob {
await this.messagingFullMessageListFetchService.processMessageListFetch(
messageChannel,
messageChannel.connectedAccount,
workspaceId,
);

View File

@ -0,0 +1,239 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import { MessageFolderWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-folder.workspace-entity';
import { MessagingMessageCleanerService } from 'src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service';
import { MessagingCursorService } from 'src/modules/messaging/message-import-manager/services/messaging-cursor.service';
import { MessagingFullMessageListFetchService } from 'src/modules/messaging/message-import-manager/services/messaging-full-message-list-fetch.service';
import { MessagingGetMessageListService } from 'src/modules/messaging/message-import-manager/services/messaging-get-message-list.service';
import { MessageImportExceptionHandlerService } from 'src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service';
describe('MessagingFullMessageListFetchService', () => {
let messagingFullMessageListFetchService: MessagingFullMessageListFetchService;
let messagingGetMessageListService: MessagingGetMessageListService;
let messageChannelSyncStatusService: MessageChannelSyncStatusService;
let twentyORMManager: TwentyORMManager;
let messagingCursorService: MessagingCursorService;
let mockMicrosoftMessageChannel: MessageChannelWorkspaceEntity;
let mockGoogleMessageChannel: MessageChannelWorkspaceEntity;
const workspaceId = 'workspace-id';
beforeAll(() => {
mockMicrosoftMessageChannel = {
id: 'microsoft-message-channel-id',
connectedAccount: {
id: 'microsoft-connected-account-id',
provider: ConnectedAccountProvider.MICROSOFT,
handle: 'test@microsoft.com',
refreshToken: 'refresh-token',
handleAliases: '',
},
messageFolders: [
{
id: 'inbox-folder-id',
name: 'inbox',
syncCursor: 'inbox-sync-cursor',
messageChannelId: 'microsoft-message-channel-id',
} as MessageFolderWorkspaceEntity,
],
} as MessageChannelWorkspaceEntity;
mockGoogleMessageChannel = {
id: 'google-message-channel-id',
connectedAccount: {
id: 'google-connected-account-id',
provider: ConnectedAccountProvider.GOOGLE,
handle: 'test@gmail.com',
refreshToken: 'google-refresh-token',
handleAliases: '',
},
syncCursor: 'google-sync-cursor',
} as MessageChannelWorkspaceEntity;
});
beforeEach(async () => {
const mockMessageChannelMessageAssociationRepository = {
find: jest
.fn()
.mockResolvedValue([
{ messageExternalId: 'external-id-existing-message-1' },
{ messageExternalId: 'external-id-existing-message-2' },
]),
delete: jest.fn().mockResolvedValue(undefined),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
MessagingFullMessageListFetchService,
{
provide: CacheStorageNamespace.ModuleMessaging,
useValue: {
setAdd: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: MessagingGetMessageListService,
useValue: {
getFullMessageLists: jest
.fn()
.mockImplementation((messageChannel) => {
if (
messageChannel.connectedAccount.provider ===
ConnectedAccountProvider.GOOGLE
) {
return [
{
messageExternalIds: [
'external-id-existing-message-1',
'external-id-google-message-1',
'external-id-google-message-2',
],
nextSyncCursor: 'new-google-history-id',
folderId: undefined,
},
];
} else {
return [
{
messageExternalIds: [
'external-id-existing-message-1',
'external-id-new-message-1',
'external-id-new-message-2',
],
nextSyncCursor: 'new-sync-cursor',
folderId: 'inbox-folder-id',
},
];
}
}),
},
},
{
provide: MessageChannelSyncStatusService,
useValue: {
markAsMessagesListFetchOngoing: jest
.fn()
.mockResolvedValue(undefined),
scheduleMessagesImport: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: TwentyORMManager,
useValue: {
getRepository: jest
.fn()
.mockResolvedValue(
mockMessageChannelMessageAssociationRepository,
),
},
},
{
provide: MessagingCursorService,
useValue: {
updateCursor: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: CacheStorageService,
useValue: {
setAdd: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: MessageImportExceptionHandlerService,
useValue: {
handleDriverException: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: MessagingMessageCleanerService,
useValue: {
cleanWorkspaceThreads: jest.fn().mockResolvedValue(undefined),
},
},
],
}).compile();
messagingFullMessageListFetchService =
module.get<MessagingFullMessageListFetchService>(
MessagingFullMessageListFetchService,
);
messagingGetMessageListService = module.get<MessagingGetMessageListService>(
MessagingGetMessageListService,
);
messageChannelSyncStatusService =
module.get<MessageChannelSyncStatusService>(
MessageChannelSyncStatusService,
);
twentyORMManager = module.get<TwentyORMManager>(TwentyORMManager);
messagingCursorService = module.get<MessagingCursorService>(
MessagingCursorService,
);
});
it('should process Microsoft message list fetch correctly', async () => {
await messagingFullMessageListFetchService.processMessageListFetch(
mockMicrosoftMessageChannel,
workspaceId,
);
expect(
messageChannelSyncStatusService.markAsMessagesListFetchOngoing,
).toHaveBeenCalledWith([mockMicrosoftMessageChannel.id]);
expect(
messagingGetMessageListService.getFullMessageLists,
).toHaveBeenCalledWith(mockMicrosoftMessageChannel);
expect(twentyORMManager.getRepository).toHaveBeenCalledWith(
'messageChannelMessageAssociation',
);
expect(messagingCursorService.updateCursor).toHaveBeenCalledWith(
mockMicrosoftMessageChannel,
'new-sync-cursor',
'inbox-folder-id',
);
expect(
messageChannelSyncStatusService.scheduleMessagesImport,
).toHaveBeenCalledWith([mockMicrosoftMessageChannel.id]);
});
it('should process Google message list fetch correctly', async () => {
await messagingFullMessageListFetchService.processMessageListFetch(
mockGoogleMessageChannel,
workspaceId,
);
expect(
messageChannelSyncStatusService.markAsMessagesListFetchOngoing,
).toHaveBeenCalledWith([mockGoogleMessageChannel.id]);
expect(
messagingGetMessageListService.getFullMessageLists,
).toHaveBeenCalledWith(mockGoogleMessageChannel);
expect(twentyORMManager.getRepository).toHaveBeenCalledWith(
'messageChannelMessageAssociation',
);
expect(messagingCursorService.updateCursor).toHaveBeenCalledWith(
mockGoogleMessageChannel,
'new-google-history-id',
undefined,
);
expect(
messageChannelSyncStatusService.scheduleMessagesImport,
).toHaveBeenCalledWith([mockGoogleMessageChannel.id]);
});
});

View File

@ -6,7 +6,6 @@ import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decora
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
import { MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
@ -32,7 +31,6 @@ export class MessagingFullMessageListFetchService {
public async processMessageListFetch(
messageChannel: MessageChannelWorkspaceEntity,
connectedAccount: ConnectedAccountWorkspaceEntity,
workspaceId: string,
) {
try {

View File

@ -0,0 +1,284 @@
import { Logger, Provider } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { ConnectedAccountProvider } from 'twenty-shared/types';
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { BlocklistRepository } from 'src/modules/blocklist/repositories/blocklist.repository';
import { EmailAliasManagerService } from 'src/modules/connected-account/email-alias-manager/services/email-alias-manager.service';
import { ConnectedAccountRefreshTokensService } from 'src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { MessageChannelSyncStatusService } from 'src/modules/messaging/common/services/message-channel-sync-status.service';
import {
MessageChannelSyncStage,
MessageChannelWorkspaceEntity,
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import { MESSAGING_GMAIL_USERS_MESSAGES_GET_BATCH_SIZE } from 'src/modules/messaging/message-import-manager/drivers/gmail/constants/messaging-gmail-users-messages-get-batch-size.constant';
import { MessagingGetMessagesService } from 'src/modules/messaging/message-import-manager/services/messaging-get-messages.service';
import { MessageImportExceptionHandlerService } from 'src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service';
import { MessagingMessagesImportService } from 'src/modules/messaging/message-import-manager/services/messaging-messages-import.service';
import { MessagingSaveMessagesAndEnqueueContactCreationService } from 'src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service';
import { MessagingTelemetryService } from 'src/modules/messaging/monitoring/services/messaging-telemetry.service';
describe('MessagingMessagesImportService', () => {
let service: MessagingMessagesImportService;
let messageChannelSyncStatusService: MessageChannelSyncStatusService;
let connectedAccountRefreshTokensService: ConnectedAccountRefreshTokensService;
let emailAliasManagerService: EmailAliasManagerService;
let messagingGetMessagesService: MessagingGetMessagesService;
let saveMessagesService: MessagingSaveMessagesAndEnqueueContactCreationService;
const workspaceId = 'workspace-id';
let mockMessageChannel: MessageChannelWorkspaceEntity;
let mockConnectedAccount: ConnectedAccountWorkspaceEntity;
let providersBase: Provider[];
beforeEach(async () => {
mockConnectedAccount = {
id: 'connected-account-id',
provider: ConnectedAccountProvider.GOOGLE,
handle: 'test@gmail.com',
refreshToken: 'refresh-token',
accessToken: 'old-access-token',
accountOwnerId: 'account-owner-id',
handleAliases: 'alias1@gmail.com,alias2@gmail.com',
} as ConnectedAccountWorkspaceEntity;
mockMessageChannel = {
id: 'message-channel-id',
syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_PENDING,
connectedAccountId: mockConnectedAccount.id,
handle: 'test@gmail.com',
} as MessageChannelWorkspaceEntity;
providersBase = [
MessagingMessagesImportService,
{
provide: CacheStorageService,
useValue: {
setAdd: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: MessageChannelSyncStatusService,
useValue: {
markAsMessagesImportOngoing: jest.fn().mockResolvedValue(undefined),
markAsCompletedAndSchedulePartialMessageListFetch: jest
.fn()
.mockResolvedValue(undefined),
scheduleMessagesImport: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: ConnectedAccountRefreshTokensService,
useValue: {
refreshAndSaveTokens: jest.fn().mockResolvedValue('new-access-token'),
},
},
{
provide: MessagingTelemetryService,
useValue: {
track: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: 'BlocklistRepository',
useValue: {
getByWorkspaceMemberId: jest.fn().mockResolvedValue([]),
},
},
{
provide: BlocklistRepository,
useValue: {
getByWorkspaceMemberId: jest.fn().mockResolvedValue([]),
},
},
{
provide: EmailAliasManagerService,
useValue: {
refreshHandleAliases: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: TwentyORMManager,
useValue: {
getRepository: jest.fn().mockResolvedValue({
update: jest.fn().mockResolvedValue(undefined),
}),
},
},
{
provide: MessagingGetMessagesService,
useValue: {
getMessages: jest.fn().mockResolvedValue([
{
id: 'message-1',
from: 'sender@example.com',
to: 'test@gmail.com',
},
{
id: 'message-2',
from: 'test@gmail.com',
to: 'recipient@example.com',
},
]),
},
},
{
provide: MessagingSaveMessagesAndEnqueueContactCreationService,
useValue: {
saveMessagesAndEnqueueContactCreation: jest
.fn()
.mockResolvedValue(undefined),
},
},
{
provide: MessageImportExceptionHandlerService,
useValue: {
handleDriverException: jest.fn().mockResolvedValue(undefined),
},
},
];
const module: TestingModule = await Test.createTestingModule({
providers: [
...providersBase,
{
provide: CacheStorageNamespace.ModuleMessaging,
useValue: {
setPop: jest
.fn()
.mockResolvedValue(['message-id-1', 'message-id-2']),
setAdd: jest.fn().mockResolvedValue(undefined),
},
},
],
})
.overrideProvider(Logger)
.useValue({ log: jest.fn() })
.compile();
service = module.get<MessagingMessagesImportService>(
MessagingMessagesImportService,
);
messageChannelSyncStatusService =
module.get<MessageChannelSyncStatusService>(
MessageChannelSyncStatusService,
);
connectedAccountRefreshTokensService =
module.get<ConnectedAccountRefreshTokensService>(
ConnectedAccountRefreshTokensService,
);
emailAliasManagerService = module.get<EmailAliasManagerService>(
EmailAliasManagerService,
);
messagingGetMessagesService = module.get<MessagingGetMessagesService>(
MessagingGetMessagesService,
);
saveMessagesService =
module.get<MessagingSaveMessagesAndEnqueueContactCreationService>(
MessagingSaveMessagesAndEnqueueContactCreationService,
);
});
it('should fails if SyncStage is not MESSAGES_IMPORT_PENDING', async () => {
mockMessageChannel.syncStage =
MessageChannelSyncStage.PARTIAL_MESSAGE_LIST_FETCH_PENDING;
expect(
service.processMessageBatchImport(
mockMessageChannel,
mockConnectedAccount,
workspaceId,
),
).resolves.toBeFalsy();
});
it('should process message batch import successfully', async () => {
await service.processMessageBatchImport(
mockMessageChannel,
mockConnectedAccount,
workspaceId,
);
expect(
messageChannelSyncStatusService.markAsMessagesImportOngoing,
).toHaveBeenCalledWith([mockMessageChannel.id]);
expect(
connectedAccountRefreshTokensService.refreshAndSaveTokens,
).toHaveBeenCalledWith(mockConnectedAccount, workspaceId);
expect(emailAliasManagerService.refreshHandleAliases).toHaveBeenCalledWith(
mockConnectedAccount,
);
expect(messagingGetMessagesService.getMessages).toHaveBeenCalledWith(
['message-id-1', 'message-id-2'],
mockConnectedAccount,
workspaceId,
);
expect(
saveMessagesService.saveMessagesAndEnqueueContactCreation,
).toHaveBeenCalled();
expect(
messageChannelSyncStatusService.scheduleMessagesImport,
).toHaveBeenCalledTimes(0);
});
it('should process message batch import of more than MESSAGING_GMAIL_USERS_MESSAGES_GET_BATCH_SIZE successfully', async () => {
const arrayMessagesBig = Array.from(
{ length: MESSAGING_GMAIL_USERS_MESSAGES_GET_BATCH_SIZE + 1 },
(_, index) => `message-id-${index + 1}`,
);
const module: TestingModule = await Test.createTestingModule({
providers: [
...providersBase,
{
provide: CacheStorageNamespace.ModuleMessaging,
useValue: {
setPop: jest.fn().mockResolvedValue(arrayMessagesBig),
setAdd: jest.fn().mockResolvedValue(undefined),
},
},
],
})
.overrideProvider(Logger)
.useValue({ log: jest.fn() })
.compile();
service = module.get<MessagingMessagesImportService>(
MessagingMessagesImportService,
);
messageChannelSyncStatusService =
module.get<MessageChannelSyncStatusService>(
MessageChannelSyncStatusService,
);
connectedAccountRefreshTokensService =
module.get<ConnectedAccountRefreshTokensService>(
ConnectedAccountRefreshTokensService,
);
emailAliasManagerService = module.get<EmailAliasManagerService>(
EmailAliasManagerService,
);
messagingGetMessagesService = module.get<MessagingGetMessagesService>(
MessagingGetMessagesService,
);
saveMessagesService =
module.get<MessagingSaveMessagesAndEnqueueContactCreationService>(
MessagingSaveMessagesAndEnqueueContactCreationService,
);
await service.processMessageBatchImport(
mockMessageChannel,
mockConnectedAccount,
workspaceId,
);
expect(
messageChannelSyncStatusService.scheduleMessagesImport,
).toHaveBeenCalledTimes(1);
});
});

View File

@ -0,0 +1,328 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { getQueueToken } from 'src/engine/core-modules/message-queue/utils/get-queue-token.util';
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { CreateCompanyAndContactJob } from 'src/modules/contact-creation-manager/jobs/create-company-and-contact.job';
import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
import {
MessageChannelContactAutoCreationPolicy,
MessageChannelWorkspaceEntity,
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import { MessagingMessageService } from 'src/modules/messaging/message-import-manager/services/messaging-message.service';
import { MessagingSaveMessagesAndEnqueueContactCreationService } from 'src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service';
import { MessageWithParticipants } from 'src/modules/messaging/message-import-manager/types/message';
import { MessagingMessageParticipantService } from 'src/modules/messaging/message-participant-manager/services/messaging-message-participant.service';
describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => {
let service: MessagingSaveMessagesAndEnqueueContactCreationService;
let messageQueueService: MessageQueueService;
let messageService: MessagingMessageService;
let messageParticipantService: MessagingMessageParticipantService;
let datasourceInstance: { transaction: jest.Mock };
const workspaceId = 'workspace-id';
const mockConnectedAccount: ConnectedAccountWorkspaceEntity = {
id: 'connected-account-id',
handle: 'test@example.com',
handleAliases: 'alias1@example.com,alias2@example.com',
} as ConnectedAccountWorkspaceEntity;
const mockMessageChannel: MessageChannelWorkspaceEntity = {
id: 'message-channel-id',
isContactAutoCreationEnabled: true,
contactAutoCreationPolicy:
MessageChannelContactAutoCreationPolicy.SENT_AND_RECEIVED,
excludeNonProfessionalEmails: true,
excludeGroupEmails: true,
} as MessageChannelWorkspaceEntity;
const mockMessages: MessageWithParticipants[] = [
{
externalId: 'message-1',
headerMessageId: 'header-message-id-1',
subject: 'Test Subject 1',
text: 'Test content 1',
receivedAt: new Date(),
attachments: [],
messageThreadExternalId: 'thread-1',
direction: MessageDirection.OUTGOING,
participants: [
{ role: 'from', handle: 'test@example.com', displayName: 'Test User' },
{ role: 'to', handle: 'contact@company.com', displayName: 'Contact' },
],
},
{
externalId: 'message-2',
headerMessageId: 'header-message-id-2',
subject: 'Test Subject 2',
text: 'Test content 2',
receivedAt: new Date(),
attachments: [],
messageThreadExternalId: 'thread-1',
direction: MessageDirection.INCOMING,
participants: [
{ role: 'from', handle: 'contact@company.com', displayName: 'Contact' },
{ role: 'to', handle: 'test@example.com', displayName: 'Test User' },
{ role: 'to', handle: 'personal@gmail.com', displayName: 'Personal' },
{
role: 'to',
handle: 'team@lists.company.com',
displayName: 'Group email',
},
],
},
];
beforeEach(async () => {
datasourceInstance = {
transaction: jest.fn().mockImplementation(async (callback) => {
return callback({});
}),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
MessagingSaveMessagesAndEnqueueContactCreationService,
{
provide: MessageQueueService,
useValue: {
add: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: getQueueToken(MessageQueue.contactCreationQueue),
useValue: {
add: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: WorkspaceEventEmitter,
useValue: {
emitDatabaseBatchEvent: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: getRepositoryToken(ObjectMetadataEntity, 'metadata'),
useValue: {
findOneOrFail: jest.fn(),
},
},
{
provide: MessagingMessageService,
useValue: {
saveMessagesWithinTransaction: jest.fn().mockResolvedValue(
new Map([
['message-1', 'db-message-id-1'],
['message-2', 'db-message-id-2'],
]),
),
},
},
{
provide: MessagingMessageParticipantService,
useValue: {
saveMessageParticipants: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: TwentyORMManager,
useValue: {
getDatasource: jest.fn().mockResolvedValue(datasourceInstance),
},
},
],
}).compile();
service = module.get<MessagingSaveMessagesAndEnqueueContactCreationService>(
MessagingSaveMessagesAndEnqueueContactCreationService,
);
messageQueueService = module.get<MessageQueueService>(
getQueueToken(MessageQueue.contactCreationQueue),
);
messageService = module.get<MessagingMessageService>(
MessagingMessageService,
);
messageParticipantService = module.get<MessagingMessageParticipantService>(
MessagingMessageParticipantService,
);
});
it('should save messages and enqueue contact creation', async () => {
await service.saveMessagesAndEnqueueContactCreation(
mockMessages,
mockMessageChannel,
mockConnectedAccount,
workspaceId,
);
expect(messageService.saveMessagesWithinTransaction).toHaveBeenCalledWith(
mockMessages,
mockMessageChannel.id,
expect.any(Object),
);
expect(
messageParticipantService.saveMessageParticipants,
).toHaveBeenCalled();
expect(messageQueueService.add).toHaveBeenCalled();
});
it('should not enqueue contact creation when it is disabled', async () => {
await service.saveMessagesAndEnqueueContactCreation(
mockMessages,
{
...mockMessageChannel,
isContactAutoCreationEnabled: false,
},
mockConnectedAccount,
workspaceId,
);
expect(messageService.saveMessagesWithinTransaction).toHaveBeenCalled();
expect(messageQueueService.add).not.toHaveBeenCalled();
});
it('should create external contacts', async () => {
await service.saveMessagesAndEnqueueContactCreation(
[
{
...mockMessages[1],
participants: [
{
role: 'from',
handle: 'tim@apple.com',
displayName: 'participant email',
},
],
},
],
mockMessageChannel,
mockConnectedAccount,
workspaceId,
);
expect(messageQueueService.add).toHaveBeenCalledWith(
CreateCompanyAndContactJob.name,
{
workspaceId,
connectedAccount: mockConnectedAccount,
source: FieldActorSource.EMAIL,
contactsToCreate: [
{
handle: 'tim@apple.com',
displayName: 'participant email',
role: 'from',
shouldCreateContact: true,
messageId: 'db-message-id-2',
},
],
},
);
});
it('should not create group emails contacts', async () => {
await service.saveMessagesAndEnqueueContactCreation(
[
{
...mockMessages[0],
participants: [
{
role: 'from',
handle: 'contact@group.com',
displayName: 'participant that is the Connected Account',
},
],
},
],
mockMessageChannel,
mockConnectedAccount,
workspaceId,
);
expect(messageQueueService.add).toHaveBeenCalledWith(
CreateCompanyAndContactJob.name,
{
workspaceId,
connectedAccount: mockConnectedAccount,
source: FieldActorSource.EMAIL,
contactsToCreate: [],
},
);
});
it('should not create personal emails contacts', async () => {
await service.saveMessagesAndEnqueueContactCreation(
[
{
...mockMessages[0],
participants: [
{
role: 'from',
handle: 'test@gmail.com',
displayName: 'participant personal email',
},
],
},
],
mockMessageChannel,
mockConnectedAccount,
workspaceId,
);
expect(messageQueueService.add).toHaveBeenCalledWith(
CreateCompanyAndContactJob.name,
{
workspaceId,
connectedAccount: mockConnectedAccount,
source: FieldActorSource.EMAIL,
contactsToCreate: [],
},
);
});
it('should not create contact if the participant is the connected account', async () => {
const mockMessagesWithConnectedAccount = [
{
...mockMessages[0],
participants: [
{
role: 'from',
handle: 'connected@account.com',
displayName: 'participant that is the Connected Account',
},
],
},
];
await service.saveMessagesAndEnqueueContactCreation(
mockMessagesWithConnectedAccount,
mockMessageChannel,
{
...mockConnectedAccount,
handle: 'connected@account.com',
},
workspaceId,
);
expect(messageQueueService.add).toHaveBeenCalledWith(
CreateCompanyAndContactJob.name,
{
workspaceId,
connectedAccount: {
...mockConnectedAccount,
handle: 'connected@account.com',
},
source: FieldActorSource.EMAIL,
contactsToCreate: [],
},
);
});
});