test: finished writing tests for sogs mutation cache

refactored processMessagesWithCachce function
pull/2454/head
William Grant 3 years ago
parent 7c6af17327
commit f138ea31b2

@ -35,6 +35,11 @@ export type OpenGroupMessageV4 = {
reactions: Record<string, OpenGroupReaction>; reactions: Record<string, OpenGroupReaction>;
}; };
// seqno is not set for SOGS < 1.3.4
export type OpenGroupReactionMessageV4 = Omit<OpenGroupMessageV4, 'seqno'> & {
seqno: number | undefined;
};
const pollForEverythingInterval = DURATION.SECONDS * 10; const pollForEverythingInterval = DURATION.SECONDS * 10;
export const invalidAuthRequiresBlinding = export const invalidAuthRequiresBlinding =

@ -5,7 +5,7 @@
import { filter, findIndex, remove } from 'lodash'; import { filter, findIndex, remove } from 'lodash';
import { Reactions } from '../../../../util/reactions'; import { Reactions } from '../../../../util/reactions';
import { OpenGroupMessageV4 } from '../opengroupV2/OpenGroupServerPoller'; import { OpenGroupReactionMessageV4 } from '../opengroupV2/OpenGroupServerPoller';
export enum ChangeType { export enum ChangeType {
REACTIONS = 0, REACTIONS = 0,
@ -30,6 +30,11 @@ export type SogsV3Mutation = {
// we don't want to export this, we want to export functions that manipulate it // we don't want to export this, we want to export functions that manipulate it
const sogsMutationCache: Array<SogsV3Mutation> = []; const sogsMutationCache: Array<SogsV3Mutation> = [];
// for testing purposes only
export function getMutationCache() {
return sogsMutationCache;
}
function verifyEntry(entry: SogsV3Mutation): boolean { function verifyEntry(entry: SogsV3Mutation): boolean {
return Boolean( return Boolean(
entry.server && entry.server &&
@ -46,19 +51,16 @@ function verifyEntry(entry: SogsV3Mutation): boolean {
); );
} }
// we return the cache for testing export function addToMutationCache(entry: SogsV3Mutation) {
export function addToMutationCache(entry: SogsV3Mutation): Array<SogsV3Mutation> {
if (!verifyEntry(entry)) { if (!verifyEntry(entry)) {
window.log.error('SOGS Mutation Cache: Entry verification on add failed!', entry); window.log.error('SOGS Mutation Cache: Entry verification on add failed!', entry);
} else { } else {
sogsMutationCache.push(entry); sogsMutationCache.push(entry);
window.log.info('SOGS Mutation Cache: Entry added!', 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) {
export function updateMutationCache(entry: SogsV3Mutation, seqno: number): Array<SogsV3Mutation> {
if (!verifyEntry(entry)) { if (!verifyEntry(entry)) {
window.log.error('SOGS Mutation Cache: Entry verification on update failed!', entry); window.log.error('SOGS Mutation Cache: Entry verification on update failed!', entry);
} else { } 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); window.log.error('SOGS Mutation Cache: Updated failed! Cannot find entry', entry);
} }
} }
return sogsMutationCache;
} }
// return is for testing purposes only // return is for testing purposes only
export async function processMessagesUsingCache( export async function processMessagesUsingCache(
server: string, server: string,
room: string, room: string,
message: OpenGroupMessageV4 message: OpenGroupReactionMessageV4
): Promise<OpenGroupMessageV4> { ): Promise<OpenGroupReactionMessageV4> {
const updatedReactions = message.reactions; const updatedReactions = message.reactions;
const roomMatches: Array<SogsV3Mutation> = filter(sogsMutationCache, { server, room });
for (const roomMatch of roomMatches) { const roomMatches: Array<SogsV3Mutation> = filter(sogsMutationCache, { server, room });
if (message.seqno && roomMatch.seqno && roomMatch.seqno <= message.seqno) { for (let i = 0; i < roomMatches.length; i++) {
const removedEntry = remove(sogsMutationCache, roomMatch); 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); window.log.info('SOGS Mutation Cache: Entry ignored and removed!', removedEntry);
} else if ( remove(sogsMutationCache, removedEntry);
!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
);
}
}
}
} }
} }
const removedMatches = remove(sogsMutationCache, ...roomMatches); for (const reaction of Object.keys(message.reactions)) {
if (removedMatches?.length) { const reactionMatches = filter(roomMatches, {
window.log.info('SOGS Mutation Cache: Removed processed entries from cache!', removedMatches); 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; message.reactions = updatedReactions;

@ -21,9 +21,9 @@ describe('filterDuplicatesFromDbAndIncoming', () => {
}); });
it('no duplicates', async () => { it('no duplicates', async () => {
const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); const msg1 = TestUtils.generateOpenGroupMessageV2();
const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: 222 }); const msg2 = TestUtils.generateOpenGroupMessageV2();
const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); const msg3 = TestUtils.generateOpenGroupMessageV2();
const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]);
expect(filtered.length).to.be.eq(3); expect(filtered.length).to.be.eq(3);
expect(filtered[0]).to.be.deep.eq(msg1); expect(filtered[0]).to.be.deep.eq(msg1);
@ -32,11 +32,11 @@ describe('filterDuplicatesFromDbAndIncoming', () => {
}); });
it('two duplicate sender but not the same timestamp', async () => { it('two duplicate sender but not the same timestamp', async () => {
const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); const msg1 = TestUtils.generateOpenGroupMessageV2();
const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: 222 }); const msg2 = TestUtils.generateOpenGroupMessageV2();
msg2.sender = msg1.sender; msg2.sender = msg1.sender;
msg2.sentTimestamp = Date.now() + 2; msg2.sentTimestamp = Date.now() + 2;
const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); const msg3 = TestUtils.generateOpenGroupMessageV2();
const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]);
expect(filtered.length).to.be.eq(3); expect(filtered.length).to.be.eq(3);
expect(filtered[0]).to.be.deep.eq(msg1); expect(filtered[0]).to.be.deep.eq(msg1);
@ -45,10 +45,10 @@ describe('filterDuplicatesFromDbAndIncoming', () => {
}); });
it('two duplicate timestamp but not the same sender', async () => { it('two duplicate timestamp but not the same sender', async () => {
const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); const msg1 = TestUtils.generateOpenGroupMessageV2();
const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: 222 }); const msg2 = TestUtils.generateOpenGroupMessageV2();
msg2.sentTimestamp = msg1.sentTimestamp; msg2.sentTimestamp = msg1.sentTimestamp;
const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); const msg3 = TestUtils.generateOpenGroupMessageV2();
const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]);
expect(filtered.length).to.be.eq(3); expect(filtered.length).to.be.eq(3);
expect(filtered[0]).to.be.deep.eq(msg1); expect(filtered[0]).to.be.deep.eq(msg1);
@ -57,10 +57,10 @@ describe('filterDuplicatesFromDbAndIncoming', () => {
}); });
it('two duplicate timestamp but not the same sender', async () => { it('two duplicate timestamp but not the same sender', async () => {
const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); const msg1 = TestUtils.generateOpenGroupMessageV2();
const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: 222 }); const msg2 = TestUtils.generateOpenGroupMessageV2();
msg2.sentTimestamp = msg1.sentTimestamp; msg2.sentTimestamp = msg1.sentTimestamp;
const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); const msg3 = TestUtils.generateOpenGroupMessageV2();
const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]);
expect(filtered.length).to.be.eq(3); expect(filtered.length).to.be.eq(3);
expect(filtered[0]).to.be.deep.eq(msg1); expect(filtered[0]).to.be.deep.eq(msg1);
@ -69,11 +69,11 @@ describe('filterDuplicatesFromDbAndIncoming', () => {
}); });
it('two duplicates in the same poll ', async () => { it('two duplicates in the same poll ', async () => {
const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); const msg1 = TestUtils.generateOpenGroupMessageV2();
const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: msg1.serverId! }); const msg2 = TestUtils.generateOpenGroupMessageV2();
msg2.sentTimestamp = msg1.sentTimestamp; msg2.sentTimestamp = msg1.sentTimestamp;
msg2.sender = msg1.sender; msg2.sender = msg1.sender;
const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: 333 }); const msg3 = TestUtils.generateOpenGroupMessageV2();
const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]); const filtered = await filterDuplicatesFromDbAndIncoming([msg1, msg2, msg3]);
expect(filtered.length).to.be.eq(2); expect(filtered.length).to.be.eq(2);
expect(filtered[0]).to.be.deep.eq(msg1); expect(filtered[0]).to.be.deep.eq(msg1);
@ -81,10 +81,10 @@ describe('filterDuplicatesFromDbAndIncoming', () => {
}); });
it('three duplicates in the same poll', async () => { it('three duplicates in the same poll', async () => {
const msg1 = TestUtils.generateOpenGroupMessageV2({ serverId: 111 }); const msg1 = TestUtils.generateOpenGroupMessageV2();
const msg2 = TestUtils.generateOpenGroupMessageV2({ serverId: msg1.serverId! }); const msg2 = TestUtils.generateOpenGroupMessageV2();
const msg3 = TestUtils.generateOpenGroupMessageV2({ serverId: msg1.serverId! }); const msg3 = TestUtils.generateOpenGroupMessageV2();
msg2.sentTimestamp = msg1.sentTimestamp; msg2.sentTimestamp = msg1.sentTimestamp;
msg2.sender = msg1.sender; msg2.sender = msg1.sender;
msg3.sentTimestamp = msg1.sentTimestamp; msg3.sentTimestamp = msg1.sentTimestamp;

@ -215,7 +215,7 @@ describe('MessageQueue', () => {
let sendToOpenGroupV2Stub: sinon.SinonStub; let sendToOpenGroupV2Stub: sinon.SinonStub;
beforeEach(() => { beforeEach(() => {
sendToOpenGroupV2Stub = Sinon.stub(MessageSender, 'sendToOpenGroupV2').resolves( sendToOpenGroupV2Stub = Sinon.stub(MessageSender, 'sendToOpenGroupV2').resolves(
TestUtils.generateOpenGroupMessageV2({ serverId: 5125 }) TestUtils.generateOpenGroupMessageV2()
); );
}); });

@ -3,75 +3,29 @@ import Sinon from 'sinon';
import { import {
addToMutationCache, addToMutationCache,
ChangeType, ChangeType,
getMutationCache,
processMessagesUsingCache,
SogsV3Mutation, SogsV3Mutation,
updateMutationCache, updateMutationCache,
} from '../../../../session/apis/open_group_api/sogsv3/sogsV3MutationCache'; } from '../../../../session/apis/open_group_api/sogsv3/sogsV3MutationCache';
import { Action, Reaction } from '../../../../types/Reaction';
import { TestUtils } from '../../../test-utils'; import { TestUtils } from '../../../test-utils';
import { Reactions } from '../../../../util/reactions'; import { Reactions } from '../../../../util/reactions';
import {
OpenGroupMessageV4,
OpenGroupReactionMessageV4,
} from '../../../../session/apis/open_group_api/opengroupV2/OpenGroupServerPoller';
// tslint:disable: chai-vague-errors
describe('mutationCache', () => { describe('mutationCache', () => {
TestUtils.stubWindowLog(); TestUtils.stubWindowLog();
const roomInfos = TestUtils.generateOpenGroupV2RoomInfos(); 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 reactor1 = TestUtils.generateFakePubKey().key;
const reactor2 = TestUtils.generateFakePubKey().key; const reactor2 = TestUtils.generateFakePubKey().key;
const reaction: Reaction = { beforeEach(() => {
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 () => {
// stubs // stubs
Sinon.stub(Reactions, 'handleOpenGroupMessageReactions').resolves(); Sinon.stub(Reactions, 'handleOpenGroupMessageReactions').resolves();
}); });
@ -79,64 +33,333 @@ describe('mutationCache', () => {
afterEach(Sinon.restore); afterEach(Sinon.restore);
describe('add entry to cache', () => { describe('add entry to cache', () => {
it('add entry to cache that is valid', async () => { it('add entry to cache that is valid', () => {
const cacheState = addToMutationCache(validEntry); const entry: SogsV3Mutation = {
expect(cacheState, 'should not empty').to.not.equal([]); server: roomInfos.serverUrl,
expect(cacheState.length, 'should have one entry').to.be.equal(1); room: roomInfos.roomId,
expect(cacheState[0], 'the entry should match the input').to.be.deep.equal(validEntry); 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 () => { it('add entry to cache that is invalid and fail', () => {
const cacheState = addToMutationCache(invalidEntry); const entry: SogsV3Mutation = {
expect(cacheState, 'should not empty').to.not.equal([]); server: '', // this is invalid
expect(cacheState.length, 'should have one entry').to.be.equal(1); 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', () => { describe('update entry in cache', () => {
it('update entry in cache with a valid source entry', async () => { it('update entry in cache with a valid source entry', () => {
const cacheState = updateMutationCache(validEntry, messageResponse.seqno); const entry: SogsV3Mutation = {
expect(cacheState, 'should not empty').to.not.equal([]); server: roomInfos.serverUrl,
expect(cacheState.length, 'should have one entry').to.be.equal(1); 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( expect(
cacheState[0].seqno, cache[0].seqno,
'should have an entry with a matching seqno to the message response' 'should have an entry with a matching seqno to the message response'
).to.be.equal(messageResponse.seqno); ).to.be.equal(messageResponse.seqno);
}); });
it('update entry in cache with an invalid source entry', async () => { it('update entry in cache with an invalid source entry', () => {
const cacheState = updateMutationCache(invalidEntry, messageResponse.seqno); const messageResponse = TestUtils.generateFakeIncomingOpenGroupMessageV4({
expect(cacheState, 'should not empty').to.not.equal([]); id: originalMessage.serverId,
expect(cacheState.length, 'should have one entry').to.be.equal(1); 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( expect(
cacheState[0].seqno, cache[0].seqno,
'should have an entry with a matching seqno to the message response' 'should have an entry with a matching seqno to the message response'
).to.be.equal(messageResponse.seqno); ).to.be.equal(messageResponse.seqno);
}); });
it('update entry in cache with a valid source entry but its not stored in the cache', async () => { it('update entry in cache with a valid source entry but its not stored in the cache', () => {
const notFoundEntry: SogsV3Mutation = { 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, server: roomInfos.serverUrl,
room: roomInfos.roomId, room: roomInfos.roomId,
changeType: ChangeType.REACTIONS, changeType: ChangeType.REACTIONS,
seqno: 400, seqno: 400,
metadata: { metadata: {
messageId: originalMessage.serverId!, messageId: originalMessage.serverId,
emoji: reaction.emoji, emoji: '😄',
action: 'ADD', action: 'ADD',
}, },
}; };
const cacheState = updateMutationCache(notFoundEntry, messageResponse.seqno); updateMutationCache(entry, (messageResponse as OpenGroupMessageV4).seqno);
expect(cacheState, 'should not empty').to.not.equal([]); const cache = getMutationCache();
expect(cacheState.length, 'should have one entry').to.be.equal(1); expect(cache, 'should not empty').to.not.equal([]);
expect(cache.length, 'should have one entry').to.be.equal(1);
expect( expect(
cacheState[0].seqno, cache[0].seqno,
'should have an entry with a matching seqno to the message response' 'should have an entry with a matching seqno to the message response'
).to.be.equal(messageResponse.seqno); ).to.be.equal(messageResponse.seqno);
}); });
}); });
describe('process opengroup messages using the cache', () => { 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 message should return the same message response', async () => {
it('processing a message with valid serverUrl, roomId and invalid message should return undefined', async () => {}); const messageResponse = TestUtils.generateFakeIncomingOpenGroupMessageV4({
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 () => {}); id: originalMessage.serverId,
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 () => {}); 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);
});
}); });
}); });

@ -7,7 +7,10 @@ import { TestUtils } from '..';
import { OpenGroupRequestCommonType } from '../../../session/apis/open_group_api/opengroupV2/ApiUtil'; import { OpenGroupRequestCommonType } from '../../../session/apis/open_group_api/opengroupV2/ApiUtil';
import { OpenGroupVisibleMessage } from '../../../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; import { OpenGroupVisibleMessage } from '../../../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
import { MessageModel } from '../../../models/message'; 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'; import { OpenGroupReaction } from '../../../types/Reaction';
export function generateVisibleMessage({ export function generateVisibleMessage({
@ -29,15 +32,31 @@ export function generateVisibleMessage({
}); });
} }
export function generateOpenGroupMessageV2({ serverId }: { serverId: number }): OpenGroupMessageV2 { export function generateOpenGroupMessageV2(): OpenGroupMessageV2 {
return new OpenGroupMessageV2({ return new OpenGroupMessageV2({
serverId,
sentTimestamp: Date.now(), sentTimestamp: Date.now(),
sender: TestUtils.generateFakePubKey().key, sender: TestUtils.generateFakePubKey().key,
base64EncodedData: 'whatever', base64EncodedData: 'whatever',
}); });
} }
// this is for test purposes only
type OpenGroupMessageV2WithServerId = Omit<OpenGroupMessageV2, 'sender' | 'serverId'> & {
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 { export function generateOpenGroupVisibleMessage(): OpenGroupVisibleMessage {
return new OpenGroupVisibleMessage({ return new OpenGroupVisibleMessage({
timestamp: Date.now(), timestamp: Date.now(),
@ -68,16 +87,16 @@ export function generateFakeIncomingPrivateMessage(): MessageModel {
export function generateFakeIncomingOpenGroupMessageV4({ export function generateFakeIncomingOpenGroupMessageV4({
id, id,
seqno,
reactions, reactions,
seqno,
}: { }: {
seqno: number;
id: number; id: number;
seqno?: number;
reactions?: Record<string, OpenGroupReaction>; reactions?: Record<string, OpenGroupReaction>;
}): OpenGroupMessageV4 { }): OpenGroupMessageV4 | OpenGroupReactionMessageV4 {
return { return {
id, // serverId id, // serverId
seqno, seqno: seqno ?? undefined,
/** base64 */ /** base64 */
signature: 'whatever', signature: 'whatever',
/** timestamp number with decimal */ /** timestamp number with decimal */

@ -68,7 +68,7 @@ export function stubWindow<K extends keyof Window>(fn: K, value: WindowValue<K>)
}; };
} }
export const enableLogRedirect = true; export const enableLogRedirect = false;
export const stubWindowLog = () => { export const stubWindowLog = () => {
stubWindow('log', { stubWindow('log', {

Loading…
Cancel
Save