diff --git a/src/script/event/preprocessor/EventStorageMiddleware/EventStorageMiddleware.test.ts b/src/script/event/preprocessor/EventStorageMiddleware/EventStorageMiddleware.test.ts index 710e443f411a..a042642aa733 100644 --- a/src/script/event/preprocessor/EventStorageMiddleware/EventStorageMiddleware.test.ts +++ b/src/script/event/preprocessor/EventStorageMiddleware/EventStorageMiddleware.test.ts @@ -20,9 +20,16 @@ import {Asset as ProtobufAsset} from '@wireapp/protocol-messaging'; import {AssetTransferState} from 'src/script/assets/AssetTransferState'; +import {ConversationState} from 'src/script/conversation/ConversationState'; +import {Conversation} from 'src/script/entity/Conversation'; import {User} from 'src/script/entity/User'; import {EventError} from 'src/script/error/EventError'; -import {createAssetAddEvent, createMessageAddEvent, toSavedEvent} from 'test/helper/EventGenerator'; +import { + createAssetAddEvent, + createMemberLeaveEvent, + createMessageAddEvent, + toSavedEvent, +} from 'test/helper/EventGenerator'; import {createUuid} from 'Util/uuid'; import {EventStorageMiddleware} from './EventStorageMiddleware'; @@ -37,9 +44,15 @@ function buildEventStorageMiddleware() { replaceEvent: jest.fn(event => event), deleteEvent: jest.fn(), } as unknown as jest.Mocked; - + const conversationState = { + findConversation: jest.fn(), + } as unknown as jest.Mocked; const selfUser = new User(createUuid()); - return [new EventStorageMiddleware(eventService, selfUser), {eventService, selfUser}] as const; + + return [ + new EventStorageMiddleware(eventService, selfUser, conversationState), + {eventService, conversationState, selfUser}, + ] as const; } describe('EventStorageMiddleware', () => { @@ -74,6 +87,66 @@ describe('EventStorageMiddleware', () => { ); }); + it('fails for a member leave event when users are not part of the conversation', async () => { + const [eventStorageMiddleware, {conversationState}] = buildEventStorageMiddleware(); + const conversationId = createUuid(); + const userIds = [createUuid(), createUuid(), createUuid(), createUuid()]; + const conversation = new Conversation(conversationId, ''); + + conversationState.findConversation.mockImplementation(() => conversation); + + const event = createMemberLeaveEvent(conversationId, userIds); + + await expect(eventStorageMiddleware.processEvent(event)).rejects.toEqual( + new EventError( + EventError.TYPE.VALIDATION_FAILED, + 'Event validation failed: User is not part of the conversation', + ), + ); + }); + + it('fails for a member leave event when users are part of the conversation but are deleted already', async () => { + const [eventStorageMiddleware, {conversationState}] = buildEventStorageMiddleware(); + const conversationId = createUuid(); + const userIds = [createUuid(), createUuid(), createUuid()]; + const user1 = new User(userIds[0]); + const user2 = new User(userIds[1]); + const user3 = new User(userIds[2]); + user1.isDeleted = true; + user2.isDeleted = true; + user3.isDeleted = true; + const conversation = new Conversation(conversationId, ''); + conversation.participating_user_ets([user1, user2, user3]); + + conversationState.findConversation.mockImplementation(() => conversation); + + const event = createMemberLeaveEvent(conversationId, userIds); + + await expect(eventStorageMiddleware.processEvent(event)).rejects.toEqual( + new EventError( + EventError.TYPE.VALIDATION_FAILED, + 'Event validation failed: User is not part of the conversation', + ), + ); + }); + + it('does not return an error for a member leave event when users are part of the conversation', async () => { + const [eventStorageMiddleware, {conversationState}] = buildEventStorageMiddleware(); + const conversationId = createUuid(); + const userIds = [createUuid(), createUuid(), createUuid()]; + const user1 = new User(userIds[0]); + const user2 = new User(userIds[1]); + const user3 = new User(userIds[2]); + const conversation = new Conversation(conversationId, ''); + conversation.participating_user_ets([user1, user2, user3]); + + conversationState.findConversation.mockImplementation(() => conversation); + + const event = createMemberLeaveEvent(conversationId, userIds); + + await expect(eventStorageMiddleware.processEvent(event)).resolves.toEqual(event); + }); + it('fails for a non-"text message" with an ID previously used by the same user', async () => { const [eventStorageMiddleware, {eventService}] = buildEventStorageMiddleware(); const event = createMessageAddEvent(); diff --git a/test/helper/EventGenerator.ts b/test/helper/EventGenerator.ts index 496c0bd46a85..a9f7c46ea801 100644 --- a/test/helper/EventGenerator.ts +++ b/test/helper/EventGenerator.ts @@ -17,11 +17,15 @@ * */ +import {MemberLeaveReason} from '@wireapp/api-client/lib/conversation/data/'; +import {CONVERSATION_EVENT} from '@wireapp/api-client/lib/event'; + import {AssetTransferState} from 'src/script/assets/AssetTransferState'; import { AssetAddEvent, DeleteEvent, EventBuilder, + MemberLeaveEvent, MessageAddEvent, ReactionEvent, } from 'src/script/conversation/EventBuilder'; @@ -68,6 +72,24 @@ export function createReactionEvent(targetMessageId: string, reaction: string = }; } +export function createMemberLeaveEvent(conversationId: string, userIds: string[]): MemberLeaveEvent { + const conversationQualifiedId = {id: conversationId, domain: ''}; + + return { + conversation: conversationId, + qualified_conversation: conversationQualifiedId, + data: { + qualified_user_ids: userIds.map(userId => ({id: userId, domain: ''})), + reason: MemberLeaveReason.USER_DELETED, + user_ids: userIds, + }, + from: createUuid(), + id: createUuid(), + time: new Date().toISOString(), + type: CONVERSATION_EVENT.MEMBER_LEAVE, + }; +} + export function createDeleteEvent(deleteMessageId: string, conversationId: string = createUuid()): DeleteEvent { return { conversation: conversationId,