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:
@ -157,7 +157,6 @@ export class MessagingMessageListFetchJob {
|
||||
|
||||
await this.messagingFullMessageListFetchService.processMessageListFetch(
|
||||
messageChannel,
|
||||
messageChannel.connectedAccount,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
|
||||
@ -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]);
|
||||
});
|
||||
});
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
@ -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: [],
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user