diff --git a/ts/session/apis/open_group_api/opengroupV2/OpenGroupServerPoller.ts b/ts/session/apis/open_group_api/opengroupV2/OpenGroupServerPoller.ts index 1f34bb390..8c2eae034 100644 --- a/ts/session/apis/open_group_api/opengroupV2/OpenGroupServerPoller.ts +++ b/ts/session/apis/open_group_api/opengroupV2/OpenGroupServerPoller.ts @@ -35,6 +35,11 @@ export type OpenGroupMessageV4 = { reactions: Record; }; +// seqno is not set for SOGS < 1.3.4 +export type OpenGroupReactionMessageV4 = Omit & { + seqno: number | undefined; +}; + const pollForEverythingInterval = DURATION.SECONDS * 10; export const invalidAuthRequiresBlinding = diff --git a/ts/session/apis/open_group_api/sogsv3/sogsV3MutationCache.ts b/ts/session/apis/open_group_api/sogsv3/sogsV3MutationCache.ts index 6790b6e8e..547147e28 100644 --- a/ts/session/apis/open_group_api/sogsv3/sogsV3MutationCache.ts +++ b/ts/session/apis/open_group_api/sogsv3/sogsV3MutationCache.ts @@ -5,7 +5,7 @@ import { filter, findIndex, remove } from 'lodash'; import { Reactions } from '../../../../util/reactions'; -import { OpenGroupMessageV4 } from '../opengroupV2/OpenGroupServerPoller'; +import { OpenGroupReactionMessageV4 } from '../opengroupV2/OpenGroupServerPoller'; export enum ChangeType { REACTIONS = 0, @@ -30,6 +30,11 @@ export type SogsV3Mutation = { // we don't want to export this, we want to export functions that manipulate it const sogsMutationCache: Array = []; +// for testing purposes only +export function getMutationCache() { + return sogsMutationCache; +} + function verifyEntry(entry: SogsV3Mutation): boolean { return Boolean( entry.server && @@ -46,19 +51,16 @@ function verifyEntry(entry: SogsV3Mutation): boolean { ); } -// we return the cache for testing -export function addToMutationCache(entry: SogsV3Mutation): Array { +export function addToMutationCache(entry: SogsV3Mutation) { if (!verifyEntry(entry)) { window.log.error('SOGS Mutation Cache: Entry verification on add failed!', entry); } else { sogsMutationCache.push(entry); window.log.info('SOGS Mutation Cache: Entry added!', entry); } - return sogsMutationCache; } -// we return the cache for testing -export function updateMutationCache(entry: SogsV3Mutation, seqno: number): Array { +export function updateMutationCache(entry: SogsV3Mutation, seqno: number) { if (!verifyEntry(entry)) { window.log.error('SOGS Mutation Cache: Entry verification on update failed!', entry); } else { @@ -70,69 +72,64 @@ export function updateMutationCache(entry: SogsV3Mutation, seqno: number): Array window.log.error('SOGS Mutation Cache: Updated failed! Cannot find entry', entry); } } - return sogsMutationCache; } // return is for testing purposes only export async function processMessagesUsingCache( server: string, room: string, - message: OpenGroupMessageV4 -): Promise { + message: OpenGroupReactionMessageV4 +): Promise { const updatedReactions = message.reactions; - const roomMatches: Array = filter(sogsMutationCache, { server, room }); - for (const roomMatch of roomMatches) { - if (message.seqno && roomMatch.seqno && roomMatch.seqno <= message.seqno) { - const removedEntry = remove(sogsMutationCache, roomMatch); + const roomMatches: Array = filter(sogsMutationCache, { server, room }); + for (let i = 0; i < roomMatches.length; i++) { + const matchSeqno = roomMatches[i].seqno; + if (message.seqno && matchSeqno && matchSeqno <= message.seqno) { + const removedEntry = roomMatches.splice(i, 1)[0]; window.log.info('SOGS Mutation Cache: Entry ignored and removed!', removedEntry); - } else if ( - !message.seqno || - (message.seqno && roomMatch.seqno && roomMatch.seqno > message.seqno) - ) { - for (const reaction of Object.keys(message.reactions)) { - const reactionMatches = filter(sogsMutationCache, { - server, - room, - changeType: ChangeType.REACTIONS, - metadata: { - messageId: message.id, - emoji: reaction, - }, - }); - - for (const reactionMatch of reactionMatches) { - switch (reactionMatch.metadata.action) { - case 'ADD': - updatedReactions[reaction].you = true; - updatedReactions[reaction].count += 1; - window.log.info( - 'SOGS Mutation Cache: Added our reaction based on the cache', - updatedReactions[reaction] - ); - break; - case 'REMOVE': - updatedReactions[reaction].you = false; - updatedReactions[reaction].count -= 1; - window.log.info( - 'SOGS Mutation Cache: Removed our reaction based on the cache', - updatedReactions[reaction] - ); - break; - default: - window.log.warn( - 'SOGS Mutation Cache: Unsupported metadata action in OpenGroupMessageV4', - reactionMatch - ); - } - } - } + remove(sogsMutationCache, removedEntry); } } - const removedMatches = remove(sogsMutationCache, ...roomMatches); - if (removedMatches?.length) { - window.log.info('SOGS Mutation Cache: Removed processed entries from cache!', removedMatches); + for (const reaction of Object.keys(message.reactions)) { + const reactionMatches = filter(roomMatches, { + server, + room, + changeType: ChangeType.REACTIONS, + metadata: { + messageId: message.id, + emoji: reaction, + }, + }); + + for (const reactionMatch of reactionMatches) { + switch (reactionMatch.metadata.action) { + case 'ADD': + updatedReactions[reaction].you = true; + updatedReactions[reaction].count += 1; + window.log.info( + 'SOGS Mutation Cache: Added our reaction based on the cache', + updatedReactions[reaction] + ); + break; + case 'REMOVE': + updatedReactions[reaction].you = false; + updatedReactions[reaction].count -= 1; + window.log.info( + 'SOGS Mutation Cache: Removed our reaction based on the cache', + updatedReactions[reaction] + ); + break; + default: + window.log.warn( + 'SOGS Mutation Cache: Unsupported metadata action in OpenGroupMessageV4', + reactionMatch + ); + } + const removedEntry = remove(sogsMutationCache, reactionMatch); + window.log.info('SOGS Mutation Cache: Entry removed!', removedEntry); + } } message.reactions = updatedReactions; diff --git a/ts/test/session/unit/receiver/opengroup/deduplicate_test.ts b/ts/test/session/unit/receiver/opengroup/deduplicate_test.ts index e7f675e44..0d73db702 100644 --- a/ts/test/session/unit/receiver/opengroup/deduplicate_test.ts +++ b/ts/test/session/unit/receiver/opengroup/deduplicate_test.ts @@ -21,9 +21,9 @@ describe('filterDuplicatesFromDbAndIncoming', () => { }); it('no duplicates', async () => { - const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); - const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: 222 }); - const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); + const msg1 = TestUtils.generateOpenGroupMessageV2(); + const msg2 = TestUtils.generateOpenGroupMessageV2(); + const msg3 = TestUtils.generateOpenGroupMessageV2(); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); expect(filtered.length).to.be.eq(3); expect(filtered[0]).to.be.deep.eq(msg1); @@ -32,11 +32,11 @@ describe('filterDuplicatesFromDbAndIncoming', () => { }); it('two duplicate sender but not the same timestamp', async () => { - const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); - const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: 222 }); + const msg1 = TestUtils.generateOpenGroupMessageV2(); + const msg2 = TestUtils.generateOpenGroupMessageV2(); msg2.sender = msg1.sender; msg2.sentTimestamp = Date.now() + 2; - const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); + const msg3 = TestUtils.generateOpenGroupMessageV2(); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); expect(filtered.length).to.be.eq(3); expect(filtered[0]).to.be.deep.eq(msg1); @@ -45,10 +45,10 @@ describe('filterDuplicatesFromDbAndIncoming', () => { }); it('two duplicate timestamp but not the same sender', async () => { - const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); - const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: 222 }); + const msg1 = TestUtils.generateOpenGroupMessageV2(); + const msg2 = TestUtils.generateOpenGroupMessageV2(); msg2.sentTimestamp = msg1.sentTimestamp; - const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); + const msg3 = TestUtils.generateOpenGroupMessageV2(); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); expect(filtered.length).to.be.eq(3); expect(filtered[0]).to.be.deep.eq(msg1); @@ -57,10 +57,10 @@ describe('filterDuplicatesFromDbAndIncoming', () => { }); it('two duplicate timestamp but not the same sender', async () => { - const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); - const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: 222 }); + const msg1 = TestUtils.generateOpenGroupMessageV2(); + const msg2 = TestUtils.generateOpenGroupMessageV2(); msg2.sentTimestamp = msg1.sentTimestamp; - const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); + const msg3 = TestUtils.generateOpenGroupMessageV2(); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); expect(filtered.length).to.be.eq(3); expect(filtered[0]).to.be.deep.eq(msg1); @@ -69,11 +69,11 @@ describe('filterDuplicatesFromDbAndIncoming', () => { }); it('two duplicates in the same poll ', async () => { - const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); - const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: msg1.serverId! }); + const msg1 = TestUtils.generateOpenGroupMessageV2(); + const msg2 = TestUtils.generateOpenGroupMessageV2(); msg2.sentTimestamp = msg1.sentTimestamp; msg2.sender = msg1.sender; - const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); + const msg3 = TestUtils.generateOpenGroupMessageV2(); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); expect(filtered.length).to.be.eq(2); expect(filtered[0]).to.be.deep.eq(msg1); @@ -81,10 +81,10 @@ describe('filterDuplicatesFromDbAndIncoming', () => { }); it('three duplicates in the same poll', async () => { - const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); - const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: msg1.serverId! }); + const msg1 = TestUtils.generateOpenGroupMessageV2(); + const msg2 = TestUtils.generateOpenGroupMessageV2(); - const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: msg1.serverId! }); + const msg3 = TestUtils.generateOpenGroupMessageV2(); msg2.sentTimestamp = msg1.sentTimestamp; msg2.sender = msg1.sender; msg3.sentTimestamp = msg1.sentTimestamp; diff --git a/ts/test/session/unit/sending/MessageQueue_test.ts b/ts/test/session/unit/sending/MessageQueue_test.ts index 53a3d609b..51892a99b 100644 --- a/ts/test/session/unit/sending/MessageQueue_test.ts +++ b/ts/test/session/unit/sending/MessageQueue_test.ts @@ -215,7 +215,7 @@ describe('MessageQueue', () => { let sendToOpenGroupV2Stub: sinon.SinonStub; beforeEach(() => { sendToOpenGroupV2Stub = Sinon.stub(MessageSender, 'sendToOpenGroupV2').resolves( - TestUtils.generateOpenGroupMessageV2({ serverId: 5125 }) + TestUtils.generateOpenGroupMessageV2() ); }); diff --git a/ts/test/session/unit/sogsv3/MutationCache_test.ts b/ts/test/session/unit/sogsv3/MutationCache_test.ts index 9eb17250d..eae02332e 100644 --- a/ts/test/session/unit/sogsv3/MutationCache_test.ts +++ b/ts/test/session/unit/sogsv3/MutationCache_test.ts @@ -3,75 +3,29 @@ import Sinon from 'sinon'; import { addToMutationCache, ChangeType, + getMutationCache, + processMessagesUsingCache, SogsV3Mutation, updateMutationCache, } from '../../../../session/apis/open_group_api/sogsv3/sogsV3MutationCache'; -import { Action, Reaction } from '../../../../types/Reaction'; import { TestUtils } from '../../../test-utils'; import { Reactions } from '../../../../util/reactions'; +import { + OpenGroupMessageV4, + OpenGroupReactionMessageV4, +} from '../../../../session/apis/open_group_api/opengroupV2/OpenGroupServerPoller'; +// tslint:disable: chai-vague-errors describe('mutationCache', () => { TestUtils.stubWindowLog(); const roomInfos = TestUtils.generateOpenGroupV2RoomInfos(); - const originalMessage = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); + const originalMessage = TestUtils.generateOpenGroupMessageV2WithServerId(111); + const originalMessage2 = TestUtils.generateOpenGroupMessageV2WithServerId(112); const reactor1 = TestUtils.generateFakePubKey().key; const reactor2 = TestUtils.generateFakePubKey().key; - const reaction: Reaction = { - id: originalMessage.serverId!, - author: originalMessage.sender!, - emoji: '😄', - action: Action.REACT, - }; - const validEntry: SogsV3Mutation = { - server: roomInfos.serverUrl, - room: roomInfos.roomId, - changeType: ChangeType.REACTIONS, - seqno: null, - metadata: { - messageId: originalMessage.serverId!, - emoji: reaction.emoji, - action: 'ADD', - }, - }; - const invalidEntry: SogsV3Mutation = { - server: '', - room: roomInfos.roomId, - changeType: ChangeType.REACTIONS, - seqno: 100, - metadata: { - messageId: originalMessage.serverId!, - emoji: reaction.emoji, - action: 'ADD', - }, - }; - const messageResponse = TestUtils.generateFakeIncomingOpenGroupMessageV4({ - id: originalMessage.serverId!, - seqno: 200, - reactions: { - '😄': { - index: 0, - count: 1, - you: true, - reactors: [originalMessage.sender!], - }, - '❤️': { - index: 1, - count: 2, - you: true, - reactors: [originalMessage.sender!, reactor1], - }, - '😈': { - index: 0, - count: 2, - you: false, - reactors: [reactor1, reactor2], - }, - }, - }); - - beforeEach(async () => { + beforeEach(() => { // stubs Sinon.stub(Reactions, 'handleOpenGroupMessageReactions').resolves(); }); @@ -79,64 +33,333 @@ describe('mutationCache', () => { afterEach(Sinon.restore); describe('add entry to cache', () => { - it('add entry to cache that is valid', async () => { - const cacheState = addToMutationCache(validEntry); - expect(cacheState, 'should not empty').to.not.equal([]); - expect(cacheState.length, 'should have one entry').to.be.equal(1); - expect(cacheState[0], 'the entry should match the input').to.be.deep.equal(validEntry); + it('add entry to cache that is valid', () => { + const entry: SogsV3Mutation = { + server: roomInfos.serverUrl, + room: roomInfos.roomId, + changeType: ChangeType.REACTIONS, + seqno: null, + metadata: { + messageId: originalMessage.serverId, + emoji: '😄', + action: 'ADD', + }, + }; + addToMutationCache(entry); + const cache = getMutationCache(); + expect(cache, 'should not empty').to.not.equal([]); + expect(cache.length, 'should have one entry').to.be.equal(1); + expect(cache[0], 'the entry should match the input').to.be.deep.equal(entry); }); - it('add entry to cache that is invalid and fail', async () => { - const cacheState = addToMutationCache(invalidEntry); - expect(cacheState, 'should not empty').to.not.equal([]); - expect(cacheState.length, 'should have one entry').to.be.equal(1); + it('add entry to cache that is invalid and fail', () => { + const entry: SogsV3Mutation = { + server: '', // this is invalid + room: roomInfos.roomId, + changeType: ChangeType.REACTIONS, + seqno: 100, + metadata: { + messageId: originalMessage.serverId, + emoji: '😄', + action: 'ADD', + }, + }; + addToMutationCache(entry); + const cache = getMutationCache(); + expect(cache, 'should not empty').to.not.equal([]); + expect(cache.length, 'should have one entry').to.be.equal(1); }); }); describe('update entry in cache', () => { - it('update entry in cache with a valid source entry', async () => { - const cacheState = updateMutationCache(validEntry, messageResponse.seqno); - expect(cacheState, 'should not empty').to.not.equal([]); - expect(cacheState.length, 'should have one entry').to.be.equal(1); + it('update entry in cache with a valid source entry', () => { + const entry: SogsV3Mutation = { + server: roomInfos.serverUrl, + room: roomInfos.roomId, + changeType: ChangeType.REACTIONS, + seqno: null, // mutation before we have received a response + metadata: { + messageId: originalMessage.serverId, + emoji: '😄', + action: 'ADD', + }, + }; + const messageResponse = TestUtils.generateFakeIncomingOpenGroupMessageV4({ + id: originalMessage.serverId, + seqno: 200, + reactions: { + '😄': { + index: 0, + count: 1, + you: false, + reactors: [reactor1], + }, + '❤️': { + index: 1, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor1], + }, + '😈': { + index: 2, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor2], + }, + }, + }) as OpenGroupMessageV4; + updateMutationCache(entry, (messageResponse as OpenGroupMessageV4).seqno); + const cache = getMutationCache(); + expect(cache, 'should not empty').to.not.equal([]); + expect(cache.length, 'should have one entry').to.be.equal(1); expect( - cacheState[0].seqno, + cache[0].seqno, 'should have an entry with a matching seqno to the message response' ).to.be.equal(messageResponse.seqno); }); - it('update entry in cache with an invalid source entry', async () => { - const cacheState = updateMutationCache(invalidEntry, messageResponse.seqno); - expect(cacheState, 'should not empty').to.not.equal([]); - expect(cacheState.length, 'should have one entry').to.be.equal(1); + it('update entry in cache with an invalid source entry', () => { + const messageResponse = TestUtils.generateFakeIncomingOpenGroupMessageV4({ + id: originalMessage.serverId, + seqno: 200, + reactions: { + '😄': { + index: 0, + count: 1, + you: false, + reactors: [reactor1], + }, + '❤️': { + index: 1, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor1], + }, + '😈': { + index: 2, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor2], + }, + }, + }) as OpenGroupMessageV4; + const entry: SogsV3Mutation = { + server: '', + room: roomInfos.roomId, + changeType: ChangeType.REACTIONS, + seqno: 100, + metadata: { + messageId: originalMessage.serverId, + emoji: '😄', + action: 'ADD', + }, + }; + updateMutationCache(entry, (messageResponse as OpenGroupMessageV4).seqno); + const cache = getMutationCache(); + expect(cache, 'should not empty').to.not.equal([]); + expect(cache.length, 'should have one entry').to.be.equal(1); expect( - cacheState[0].seqno, + cache[0].seqno, 'should have an entry with a matching seqno to the message response' ).to.be.equal(messageResponse.seqno); }); - it('update entry in cache with a valid source entry but its not stored in the cache', async () => { - const notFoundEntry: SogsV3Mutation = { + it('update entry in cache with a valid source entry but its not stored in the cache', () => { + const messageResponse = TestUtils.generateFakeIncomingOpenGroupMessageV4({ + id: originalMessage.serverId, + seqno: 200, + reactions: { + '😄': { + index: 0, + count: 1, + you: false, + reactors: [reactor1], + }, + '❤️': { + index: 1, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor1], + }, + '😈': { + index: 2, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor2], + }, + }, + }) as OpenGroupMessageV4; + const entry: SogsV3Mutation = { server: roomInfos.serverUrl, room: roomInfos.roomId, changeType: ChangeType.REACTIONS, seqno: 400, metadata: { - messageId: originalMessage.serverId!, - emoji: reaction.emoji, + messageId: originalMessage.serverId, + emoji: '😄', action: 'ADD', }, }; - const cacheState = updateMutationCache(notFoundEntry, messageResponse.seqno); - expect(cacheState, 'should not empty').to.not.equal([]); - expect(cacheState.length, 'should have one entry').to.be.equal(1); + updateMutationCache(entry, (messageResponse as OpenGroupMessageV4).seqno); + const cache = getMutationCache(); + expect(cache, 'should not empty').to.not.equal([]); + expect(cache.length, 'should have one entry').to.be.equal(1); expect( - cacheState[0].seqno, + cache[0].seqno, 'should have an entry with a matching seqno to the message response' ).to.be.equal(messageResponse.seqno); }); }); describe('process opengroup messages using the cache', () => { - it('processing a message with valid serverUrl, roomId and message should return an updated message', async () => {}); - it('processing a message with valid serverUrl, roomId and invalid message should return undefined', async () => {}); - it('processing a message with valid entries in the cache should remove them if the cached entry seqno number is less than the message seqo', async () => {}); - it('processing a message with valid entries in the cache should calculate the optimistic state if there is no message seqo or the cached entry seqno is larger than the message seqno', async () => {}); + it('processing a message with valid serverUrl, roomId and message should return the same message response', async () => { + const messageResponse = TestUtils.generateFakeIncomingOpenGroupMessageV4({ + id: originalMessage.serverId, + seqno: 200, + reactions: { + '😄': { + index: 0, + count: 1, + you: false, + reactors: [reactor1], + }, + '❤️': { + index: 1, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor1], + }, + '😈': { + index: 2, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor2], + }, + }, + }) as OpenGroupMessageV4; + const message = await processMessagesUsingCache( + roomInfos.serverUrl, + roomInfos.roomId, + messageResponse + ); + const cache = getMutationCache(); + expect(cache, 'cache should be empty').to.be.empty; + expect(message, 'message response should match').to.be.deep.equal(messageResponse); + }); + it('processing a message with valid serverUrl, roomId and message (from SOGS < 1.3.4) should return the same message response', async () => { + const messageResponse = TestUtils.generateFakeIncomingOpenGroupMessageV4({ + id: originalMessage2.serverId, + // in version less than 1.3.4 there is no a seqno set + reactions: { + '🤣': { + index: 0, + count: 3, + you: true, + reactors: [reactor1, reactor2, originalMessage2.sender], + }, + '😈': { + index: 0, + count: 1, + you: false, + reactors: [reactor2], + }, + }, + }) as OpenGroupReactionMessageV4; + const message = await processMessagesUsingCache( + roomInfos.serverUrl, + roomInfos.roomId, + messageResponse + ); + const cache = getMutationCache(); + expect(cache, 'cache should be empty').to.be.empty; + expect(message, 'message response should match').to.be.deep.equal(messageResponse); + }); + it('processing a message with valid entries in the cache should calculate the optimistic state if there is no message seqo or the cached entry seqno is larger than the message seqno', async () => { + const messageResponse = TestUtils.generateFakeIncomingOpenGroupMessageV4({ + id: originalMessage.serverId, + seqno: 200, + reactions: { + '😄': { + index: 0, + count: 1, + you: false, + reactors: [reactor1], + }, + '❤️': { + index: 1, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor1], + }, + '😈': { + index: 2, + count: 2, + you: true, + reactors: [originalMessage.sender, reactor2], + }, + }, + }) as OpenGroupMessageV4; + const entry: SogsV3Mutation = { + server: roomInfos.serverUrl, + room: roomInfos.roomId, + changeType: ChangeType.REACTIONS, + seqno: 100, // less than response messageResponse seqno should be ignored + metadata: { + messageId: originalMessage.serverId, + emoji: '❤️', + action: 'ADD', + }, + }; + const entry2: SogsV3Mutation = { + server: roomInfos.serverUrl, + room: roomInfos.roomId, + changeType: ChangeType.REACTIONS, + seqno: 300, // greater than response messageResponse seqno should be procesed + metadata: { + messageId: originalMessage.serverId, + emoji: '😄', + action: 'ADD', + }, + }; + const entry3: SogsV3Mutation = { + server: roomInfos.serverUrl, + room: roomInfos.roomId, + changeType: ChangeType.REACTIONS, + seqno: 301, //// greater than response messageResponse seqno should be procesed + metadata: { + messageId: originalMessage.serverId, + emoji: '😈', + action: 'REMOVE', + }, + }; + addToMutationCache(entry); + addToMutationCache(entry2); + addToMutationCache(entry3); + + const message = await processMessagesUsingCache( + roomInfos.serverUrl, + roomInfos.roomId, + messageResponse + ); + const cache = getMutationCache(); + expect(cache, 'cache should be empty').to.be.empty; + expect( + message.reactions['❤️'].count, + 'message response reaction count for ❤️ should be unchanged with 2' + ).to.equal(2); + expect( + message.reactions['😄'].count, + 'message response reaction count for 😄 should be 2' + ).to.equal(2); + expect( + message.reactions['😄'].you, + 'message response reaction for 😄 should have you = true' + ).to.equal(true); + expect( + message.reactions['😈'].count, + 'message response reaction count for 😈 should be 1' + ).to.equal(1); + expect( + message.reactions['😈'].you, + 'message response reaction for 😈 should have you = false' + ).to.equal(false); + }); }); }); diff --git a/ts/test/test-utils/utils/message.ts b/ts/test/test-utils/utils/message.ts index f0f5b6518..1f511a8fc 100644 --- a/ts/test/test-utils/utils/message.ts +++ b/ts/test/test-utils/utils/message.ts @@ -7,7 +7,10 @@ import { TestUtils } from '..'; import { OpenGroupRequestCommonType } from '../../../session/apis/open_group_api/opengroupV2/ApiUtil'; import { OpenGroupVisibleMessage } from '../../../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; import { MessageModel } from '../../../models/message'; -import { OpenGroupMessageV4 } from '../../../session/apis/open_group_api/opengroupV2/OpenGroupServerPoller'; +import { + OpenGroupMessageV4, + OpenGroupReactionMessageV4, +} from '../../../session/apis/open_group_api/opengroupV2/OpenGroupServerPoller'; import { OpenGroupReaction } from '../../../types/Reaction'; export function generateVisibleMessage({ @@ -29,15 +32,31 @@ export function generateVisibleMessage({ }); } -export function generateOpenGroupMessageV2({ serverId }: { serverId: number }): OpenGroupMessageV2 { +export function generateOpenGroupMessageV2(): OpenGroupMessageV2 { return new OpenGroupMessageV2({ - serverId, sentTimestamp: Date.now(), sender: TestUtils.generateFakePubKey().key, base64EncodedData: 'whatever', }); } +// this is for test purposes only +type OpenGroupMessageV2WithServerId = Omit & { + sender: string; + serverId: number; +}; + +export function generateOpenGroupMessageV2WithServerId( + serverId: number +): OpenGroupMessageV2WithServerId { + return new OpenGroupMessageV2({ + serverId, + sentTimestamp: Date.now(), + sender: TestUtils.generateFakePubKey().key, + base64EncodedData: 'whatever', + }) as OpenGroupMessageV2WithServerId; +} + export function generateOpenGroupVisibleMessage(): OpenGroupVisibleMessage { return new OpenGroupVisibleMessage({ timestamp: Date.now(), @@ -68,16 +87,16 @@ export function generateFakeIncomingPrivateMessage(): MessageModel { export function generateFakeIncomingOpenGroupMessageV4({ id, - seqno, reactions, + seqno, }: { - seqno: number; id: number; + seqno?: number; reactions?: Record; -}): OpenGroupMessageV4 { +}): OpenGroupMessageV4 | OpenGroupReactionMessageV4 { return { id, // serverId - seqno, + seqno: seqno ?? undefined, /** base64 */ signature: 'whatever', /** timestamp number with decimal */ diff --git a/ts/test/test-utils/utils/stubbing.ts b/ts/test/test-utils/utils/stubbing.ts index d465d6f9a..7ec27485f 100644 --- a/ts/test/test-utils/utils/stubbing.ts +++ b/ts/test/test-utils/utils/stubbing.ts @@ -68,7 +68,7 @@ export function stubWindow(fn: K, value: WindowValue) }; } -export const enableLogRedirect = true; +export const enableLogRedirect = false; export const stubWindowLog = () => { stubWindow('log', {