diff --git a/ts/session/apis/snode_api/hfHandling.ts b/ts/session/apis/snode_api/hfHandling.ts index 4933dd002..dc349e283 100644 --- a/ts/session/apis/snode_api/hfHandling.ts +++ b/ts/session/apis/snode_api/hfHandling.ts @@ -15,7 +15,6 @@ export async function getHasSeenHF190() { if (hasSeenHardfork190 === undefined) { // read values from db and cache them as it looks like we did not const oldHhasSeenHardfork190 = (await getItemById('hasSeenHardfork190'))?.value; - // values do not exist in the db yet. Let's store false for now in the db and update our cached value. if (oldHhasSeenHardfork190 === undefined) { await createOrUpdateItem({ id: 'hasSeenHardfork190', value: false }); diff --git a/ts/session/apis/snode_api/swarmPolling.ts b/ts/session/apis/snode_api/swarmPolling.ts index b6cb04d14..31b4697bf 100644 --- a/ts/session/apis/snode_api/swarmPolling.ts +++ b/ts/session/apis/snode_api/swarmPolling.ts @@ -3,7 +3,7 @@ import * as snodePool from './snodePool'; import { ERROR_CODE_NO_CONNECT, retrieveNextMessages } from './SNodeAPI'; import { SignalService } from '../../../protobuf'; import * as Receiver from '../../../receiver/receiver'; -import _ from 'lodash'; +import _, { concat } from 'lodash'; import { getLastHashBySnode, getSeenMessagesByHashList, @@ -20,6 +20,7 @@ import { perfEnd, perfStart } from '../../utils/Performance'; import { ed25519Str } from '../../onions/onionPath'; import { updateIsOnline } from '../../../state/ducks/onion'; import pRetry from 'p-retry'; +import { getHasSeenHF190, getHasSeenHF191 } from './hfHandling'; interface Message { hash: string; @@ -145,7 +146,10 @@ export class SwarmPolling { } // we always poll as often as possible for our pubkey const ourPubkey = UserUtils.getOurPubKeyFromCache(); - const directPromise = this.pollOnceForKey(ourPubkey, false, 0); + const directPromises = Promise.all([ + this.pollOnceForKey(ourPubkey, false, 0), + // this.pollOnceForKey(ourPubkey, false, 5), // uncomment, and test me once we store the config messages to the namespace 5 + ]).then(() => undefined); const now = Date.now(); const groupPromises = this.groupPolling.map(async group => { @@ -162,6 +166,25 @@ export class SwarmPolling { window?.log?.info( `Polling for ${loggingId}; timeout: ${convoPollingTimeout} ; diff: ${diff}` ); + + const hardfork190Happened = await getHasSeenHF190(); + const hardfork191Happened = await getHasSeenHF191(); + + if (hardfork190Happened && !hardfork191Happened) { + // during the transition period, we poll from both namespaces (0 and -10) for groups + return Promise.all([ + this.pollOnceForKey(group.pubkey, true, 0), + this.pollOnceForKey(group.pubkey, true, -10), + ]).then(() => undefined); + } + + if (hardfork190Happened && hardfork191Happened) { + // after the transition period, we poll from the namespace -10 only for groups + return this.pollOnceForKey(group.pubkey, true, -10); + } + + // before any of those hardforks, we just poll from the default namespace being 0 + console.warn('before any of those hardforks'); return this.pollOnceForKey(group.pubkey, true, 0); } window?.log?.info( @@ -171,7 +194,7 @@ export class SwarmPolling { return Promise.resolve(); }); try { - await Promise.all(_.concat(directPromise, groupPromises)); + await Promise.all(concat([directPromises], groupPromises)); } catch (e) { window?.log?.info('pollForAllKeys exception: ', e); throw e; @@ -193,22 +216,19 @@ export class SwarmPolling { // If we need more nodes, select randomly from the remaining nodes: - // Use 1 node for now: - const COUNT = 1; - - let nodesToPoll = _.sampleSize(alreadyPolled, COUNT); - if (nodesToPoll.length < COUNT) { + // We only poll from a single node. + let nodesToPoll = _.sampleSize(alreadyPolled, 1); + if (nodesToPoll.length < 1) { const notPolled = _.difference(swarmSnodes, alreadyPolled); - const newNeeded = COUNT - alreadyPolled.length; - - const newNodes = _.sampleSize(notPolled, newNeeded); + const newNodes = _.sampleSize(notPolled, 1); nodesToPoll = _.concat(nodesToPoll, newNodes); } + // this actually doesn't make much sense as we are at only polling from a single one const promisesSettled = await Promise.allSettled( - nodesToPoll.map(async (n: Snode) => { + nodesToPoll.map(async n => { return this.pollNodeForKey(n, pubkey, namespace); }) ); diff --git a/ts/session/crypto/MessageEncrypter.ts b/ts/session/crypto/MessageEncrypter.ts index 410e0d4f8..9a8b6959b 100644 --- a/ts/session/crypto/MessageEncrypter.ts +++ b/ts/session/crypto/MessageEncrypter.ts @@ -1,4 +1,3 @@ -import { EncryptionType } from '../types/EncryptionType'; import { SignalService } from '../../protobuf'; import { PubKey } from '../types'; import { concatUInt8Array, getSodiumRenderer, MessageEncrypter } from '.'; @@ -24,15 +23,15 @@ type EncryptResult = { export async function encrypt( device: PubKey, plainTextBuffer: Uint8Array, - encryptionType: EncryptionType + encryptionType: SignalService.Envelope.Type ): Promise { const { CLOSED_GROUP_MESSAGE, SESSION_MESSAGE } = SignalService.Envelope.Type; - if (encryptionType !== EncryptionType.ClosedGroup && encryptionType !== EncryptionType.Fallback) { + if (encryptionType !== CLOSED_GROUP_MESSAGE && encryptionType !== SESSION_MESSAGE) { throw new Error(`Invalid encryption type:${encryptionType}`); } - const encryptForClosedGroup = encryptionType === EncryptionType.ClosedGroup; + const encryptForClosedGroup = encryptionType === CLOSED_GROUP_MESSAGE; const plainText = addMessagePadding(plainTextBuffer); if (encryptForClosedGroup) { diff --git a/ts/session/sending/MessageSender.ts b/ts/session/sending/MessageSender.ts index ba311ce6a..7b0099354 100644 --- a/ts/session/sending/MessageSender.ts +++ b/ts/session/sending/MessageSender.ts @@ -75,7 +75,7 @@ export async function send( ): Promise<{ wrappedEnvelope: Uint8Array; effectiveTimestamp: number }> { return pRetry( async () => { - const device = PubKey.cast(message.device); + const recipient = PubKey.cast(message.device); const { encryption, ttl } = message; const { @@ -84,12 +84,17 @@ export async function send( } = overwriteOutgoingTimestampWithNetworkTimestamp(message); const { envelopeType, cipherText } = await MessageEncrypter.encrypt( - device, + recipient, overRiddenTimestampBuffer, encryption ); - const envelope = await buildEnvelope(envelopeType, device.key, networkTimestamp, cipherText); + const envelope = await buildEnvelope( + envelopeType, + recipient.key, + networkTimestamp, + cipherText + ); const data = wrapEnvelope(envelope); // make sure to update the local sent_at timestamp, because sometimes, we will get the just pushed message in the receiver side @@ -102,8 +107,8 @@ export async function send( found.set({ sent_at: networkTimestamp }); await found.commit(); } - await MessageSender.TEST_sendMessageToSnode( - device.key, + await MessageSender.sendMessageToSnode( + recipient.key, data, ttl, networkTimestamp, @@ -121,7 +126,7 @@ export async function send( } // tslint:disable-next-line: function-name -export async function TEST_sendMessageToSnode( +export async function sendMessageToSnode( pubKey: string, data: Uint8Array, ttl: number, diff --git a/ts/session/sending/MessageSentHandler.ts b/ts/session/sending/MessageSentHandler.ts index 25b34bd3c..aab707890 100644 --- a/ts/session/sending/MessageSentHandler.ts +++ b/ts/session/sending/MessageSentHandler.ts @@ -3,7 +3,7 @@ import { getMessageById } from '../../data/data'; import { SignalService } from '../../protobuf'; import { PnServer } from '../apis/push_notification_api'; import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; -import { EncryptionType, RawMessage } from '../types'; +import { RawMessage } from '../types'; import { UserUtils } from '../utils'; // tslint:disable-next-line: no-unnecessary-class @@ -58,7 +58,8 @@ export class MessageSentHandler { // FIXME this is not correct and will cause issues with syncing // At this point the only way to check for medium // group is by comparing the encryption type - const isClosedGroupMessage = sentMessage.encryption === EncryptionType.ClosedGroup; + const isClosedGroupMessage = + sentMessage.encryption === SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE; // We trigger a sync message only when the message is not to one of our devices, AND // the message is not for an open group (there is no sync for opengroups, each device pulls all messages), AND diff --git a/ts/session/types/EncryptionType.ts b/ts/session/types/EncryptionType.ts deleted file mode 100644 index 82acb5d30..000000000 --- a/ts/session/types/EncryptionType.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum EncryptionType { - Signal, - Fallback, - ClosedGroup, -} diff --git a/ts/session/types/RawMessage.ts b/ts/session/types/RawMessage.ts index 87293dd7c..07a933b67 100644 --- a/ts/session/types/RawMessage.ts +++ b/ts/session/types/RawMessage.ts @@ -1,11 +1,11 @@ -import { EncryptionType } from './EncryptionType'; +import { SignalService } from '../../protobuf'; export type RawMessage = { identifier: string; plainTextBuffer: Uint8Array; device: string; ttl: number; - encryption: EncryptionType; + encryption: SignalService.Envelope.Type; }; // For building RawMessages from JSON diff --git a/ts/session/types/index.ts b/ts/session/types/index.ts index c7c994c52..340b7ec9c 100644 --- a/ts/session/types/index.ts +++ b/ts/session/types/index.ts @@ -1,3 +1,2 @@ -export * from './EncryptionType'; export * from './RawMessage'; export * from './PubKey'; diff --git a/ts/session/utils/Messages.ts b/ts/session/utils/Messages.ts index a73d77d11..7a42ca5c0 100644 --- a/ts/session/utils/Messages.ts +++ b/ts/session/utils/Messages.ts @@ -1,22 +1,23 @@ import { RawMessage } from '../types/RawMessage'; -import { EncryptionType, PubKey } from '../types'; +import { PubKey } from '../types'; import { ClosedGroupMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupMessage'; import { ClosedGroupNewMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupNewMessage'; import { ClosedGroupEncryptionPairReplyMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupEncryptionPairReplyMessage'; import { ContentMessage } from '../messages/outgoing'; import { ExpirationTimerUpdateMessage } from '../messages/outgoing/controlMessage/ExpirationTimerUpdateMessage'; +import { SignalService } from '../../protobuf'; function getEncryptionTypeFromMessageType( message: ContentMessage, isGroup = false -): EncryptionType { +): SignalService.Envelope.Type { // ClosedGroupNewMessage is sent using established channels, so using fallback if ( message instanceof ClosedGroupNewMessage || message instanceof ClosedGroupEncryptionPairReplyMessage ) { - return EncryptionType.Fallback; + return SignalService.Envelope.Type.SESSION_MESSAGE; } // 1. any ClosedGroupMessage which is not a ClosedGroupNewMessage must be encoded with ClosedGroup @@ -26,9 +27,9 @@ function getEncryptionTypeFromMessageType( (message instanceof ExpirationTimerUpdateMessage && message.groupId) || isGroup ) { - return EncryptionType.ClosedGroup; + return SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE; } else { - return EncryptionType.Fallback; + return SignalService.Envelope.Type.SESSION_MESSAGE; } } diff --git a/ts/test/session/unit/crypto/MessageEncrypter_test.ts b/ts/test/session/unit/crypto/MessageEncrypter_test.ts index b92c0efd1..2e8b7702a 100644 --- a/ts/test/session/unit/crypto/MessageEncrypter_test.ts +++ b/ts/test/session/unit/crypto/MessageEncrypter_test.ts @@ -2,7 +2,6 @@ import chai, { expect } from 'chai'; import * as crypto from 'crypto'; import Sinon, * as sinon from 'sinon'; import { concatUInt8Array, getSodiumRenderer, MessageEncrypter } from '../../../../session/crypto'; -import { EncryptionType } from '../../../../session/types/EncryptionType'; import { TestUtils } from '../../../test-utils'; import { SignalService } from '../../../../protobuf'; @@ -120,7 +119,7 @@ describe('MessageEncrypter', () => { const result = await MessageEncrypter.encrypt( TestUtils.generateFakePubKey(), data, - EncryptionType.ClosedGroup + SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE ); chai .expect(result.envelopeType) @@ -133,7 +132,7 @@ describe('MessageEncrypter', () => { const result = await MessageEncrypter.encrypt( TestUtils.generateFakePubKey(), data, - EncryptionType.Fallback + SignalService.Envelope.Type.SESSION_MESSAGE ); chai.expect(result.envelopeType).to.deep.equal(SignalService.Envelope.Type.SESSION_MESSAGE); }); @@ -143,7 +142,7 @@ describe('MessageEncrypter', () => { return MessageEncrypter.encrypt( TestUtils.generateFakePubKey(), data, - EncryptionType.Signal + 3 as any ).should.eventually.be.rejectedWith(Error); }); }); @@ -162,7 +161,11 @@ describe('MessageEncrypter', () => { it('should pass the padded message body to encrypt', async () => { const data = crypto.randomBytes(10); const spy = sinon.spy(MessageEncrypter, 'encryptUsingSessionProtocol'); - await MessageEncrypter.encrypt(TestUtils.generateFakePubKey(), data, EncryptionType.Fallback); + await MessageEncrypter.encrypt( + TestUtils.generateFakePubKey(), + data, + SignalService.Envelope.Type.SESSION_MESSAGE + ); chai.expect(spy.callCount).to.be.equal(1); const paddedData = addMessagePadding(data); const firstArgument = new Uint8Array(spy.args[0][1]); diff --git a/ts/test/session/unit/sending/MessageSender_test.ts b/ts/test/session/unit/sending/MessageSender_test.ts index 614c2954a..c900a2e8d 100644 --- a/ts/test/session/unit/sending/MessageSender_test.ts +++ b/ts/test/session/unit/sending/MessageSender_test.ts @@ -5,7 +5,6 @@ import { MessageSender } from '../../../../session/sending'; import { TestUtils } from '../../../test-utils'; import { MessageEncrypter } from '../../../../session/crypto'; import { SignalService } from '../../../../protobuf'; -import { EncryptionType } from '../../../../session/types/EncryptionType'; import { PubKey, RawMessage } from '../../../../session/types'; import { MessageUtils, UserUtils } from '../../../../session/utils'; import { ApiV2 } from '../../../../session/apis/open_group_api/opengroupV2'; @@ -26,10 +25,10 @@ describe('MessageSender', () => { describe('send', () => { const ourNumber = '0123456789abcdef'; let sessionMessageAPISendStub: sinon.SinonStub; - let encryptStub: sinon.SinonStub<[PubKey, Uint8Array, EncryptionType]>; + let encryptStub: sinon.SinonStub<[PubKey, Uint8Array, SignalService.Envelope.Type]>; beforeEach(() => { - sessionMessageAPISendStub = Sinon.stub(MessageSender, 'TEST_sendMessageToSnode').resolves(); + sessionMessageAPISendStub = Sinon.stub(MessageSender, 'sendMessageToSnode').resolves(); Sinon.stub(Data, 'getMessageById').resolves(); diff --git a/ts/test/session/unit/swarm_polling/SwarmPolling_test.ts b/ts/test/session/unit/swarm_polling/SwarmPolling_test.ts index 5b010717d..a539393cf 100644 --- a/ts/test/session/unit/swarm_polling/SwarmPolling_test.ts +++ b/ts/test/session/unit/swarm_polling/SwarmPolling_test.ts @@ -20,6 +20,8 @@ import { } from '../../../../models/conversation'; import { PubKey } from '../../../../session/types'; import { generateFakeSnodes } from '../../../test-utils/utils'; +import { resetHardForkCachedValues } from '../../../../session/apis/snode_api/hfHandling'; +import { sleepFor } from '../../../../session/utils/Promise'; // tslint:disable: chai-vague-errors chai.use(chaiAsPromised as any); @@ -37,6 +39,7 @@ describe('SwarmPolling', () => { let pollOnceForKeySpy: Sinon.SinonSpy; let swarmPolling: SwarmPolling; + let getItemByIdStub: Sinon.SinonStub; let clock: Sinon.SinonFakeTimers; beforeEach(async () => { @@ -44,7 +47,7 @@ describe('SwarmPolling', () => { Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(ourNumber); Sinon.stub(Data, 'getAllConversations').resolves(new ConversationCollection()); - Sinon.stub(DataItem, 'getItemById').resolves(); + getItemByIdStub = TestUtils.stubDataItem('getItemById'); Sinon.stub(Data, 'saveConversation').resolves(); Sinon.stub(Data, 'getSwarmNodesForPubkey').resolves(); Sinon.stub(Data, 'getLastHashBySnode').resolves(); @@ -70,6 +73,7 @@ describe('SwarmPolling', () => { Sinon.restore(); getConversationController().reset(); clock.restore(); + resetHardForkCachedValues(); }); describe('getPollingTimeout', () => { @@ -130,6 +134,12 @@ describe('SwarmPolling', () => { }); describe('pollForAllKeys', () => { + beforeEach(() => { + Sinon.stub(DataItem, 'createOrUpdateItem').resolves(); + }); + afterEach(() => { + Sinon.restore(); + }); it('does run for our pubkey even if activeAt is really old ', async () => { const convo = getConversationController().getOrCreate( ourNumber, @@ -139,7 +149,19 @@ describe('SwarmPolling', () => { await swarmPolling.start(true); expect(pollOnceForKeySpy.callCount).to.eq(1); - expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + }); + + it('does run for our pubkey even if activeAt is recent ', async () => { + const convo = getConversationController().getOrCreate( + ourNumber, + ConversationTypeEnum.PRIVATE + ); + convo.set('active_at', Date.now()); + await swarmPolling.start(true); + + expect(pollOnceForKeySpy.callCount).to.eq(1); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); }); it('does run for our pubkey even if activeAt is recent ', async () => { @@ -151,7 +173,7 @@ describe('SwarmPolling', () => { await swarmPolling.start(true); expect(pollOnceForKeySpy.callCount).to.eq(1); - expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); }); it('does run for group pubkey on start no matter the recent timestamp ', async () => { @@ -166,11 +188,11 @@ describe('SwarmPolling', () => { // our pubkey will be polled for, hence the 2 expect(pollOnceForKeySpy.callCount).to.eq(2); - expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]); }); - it('does run for group pubkey on start no matter the old timestamp ', async () => { + it('does run for group pubkey on start no matter the old timestamp if HF < 19', async () => { const convo = getConversationController().getOrCreate( TestUtils.generateFakePubKeyStr(), ConversationTypeEnum.GROUP @@ -183,8 +205,67 @@ describe('SwarmPolling', () => { // our pubkey will be polled for, hence the 2 expect(pollOnceForKeySpy.callCount).to.eq(2); - expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]); + }); + + it('does run for group pubkey on start no matter the old timestamp if HF >= 19.0 & < 19.1 ', async () => { + const convo = getConversationController().getOrCreate( + TestUtils.generateFakePubKeyStr(), + ConversationTypeEnum.GROUP + ); + getItemByIdStub.restore(); + getItemByIdStub = TestUtils.stubDataItem('getItemById'); + getItemByIdStub + .withArgs('hasSeenHardfork190') + .resolves({ id: 'hasSeenHardfork190', value: true }) + .withArgs('hasSeenHardfork191') + .resolves({ id: 'hasSeenHardfork191', value: false }); + + convo.set('active_at', 1); + const groupConvoPubkey = PubKey.cast(convo.id as string); + swarmPolling.addGroupId(groupConvoPubkey); + + await swarmPolling.start(true); + + // our pubkey will be polled for, hence the 2 + expect(pollOnceForKeySpy.callCount).to.eq(3); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]); + expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([groupConvoPubkey, true, -10]); + getItemByIdStub.restore(); + getItemByIdStub = TestUtils.stubDataItem('getItemById'); + + getItemByIdStub.resolves(); + }); + + it('does only poll from -10 for closed groups if HF >= 19.1 ', async () => { + const convo = getConversationController().getOrCreate( + TestUtils.generateFakePubKeyStr(), + ConversationTypeEnum.GROUP + ); + getItemByIdStub.restore(); + getItemByIdStub = TestUtils.stubDataItem('getItemById'); + getItemByIdStub + .withArgs('hasSeenHardfork190') + .resolves({ id: 'hasSeenHardfork190', value: true }) + .withArgs('hasSeenHardfork191') + .resolves({ id: 'hasSeenHardfork191', value: true }); + + convo.set('active_at', 1); + const groupConvoPubkey = PubKey.cast(convo.id as string); + swarmPolling.addGroupId(groupConvoPubkey); + + await swarmPolling.start(true); + + // our pubkey will be polled for, hence the 2 + expect(pollOnceForKeySpy.callCount).to.eq(2); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, -10]); + getItemByIdStub.restore(); + getItemByIdStub = TestUtils.stubDataItem('getItemById'); + + getItemByIdStub.resolves(); }); it('does run for group pubkey on start but not another time if activeAt is old ', async () => { @@ -204,9 +285,9 @@ describe('SwarmPolling', () => { await swarmPolling.pollForAllKeys(); expect(pollOnceForKeySpy.callCount).to.eq(3); - expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); - expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]); + expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, 0]); }); it('does run twice if activeAt less than one hour ', async () => { @@ -219,14 +300,21 @@ describe('SwarmPolling', () => { const groupConvoPubkey = PubKey.cast(convo.id as string); swarmPolling.addGroupId(groupConvoPubkey); await swarmPolling.start(true); - clock.tick(6000); - // no need to do that as the tick will trigger a call in all cases after 5 secs - // await swarmPolling.pollForAllKeys(); + clock.tick(9000); + // no need to do that as the tick will trigger a call in all cases after 5 secs await swarmPolling.pollForAllKeys(); + /** this is not easy to explain, but + * - during the swarmPolling.start, we get two calls to pollOnceForKeySpy (one for our id and one for group od) + * - the clock ticks 9sec, and another call of pollOnceForKeySpy get started, but as we do not await them, this test fails. + * the only fix is to restore the clock and force the a small sleep to let the thing run in bg + */ + clock.restore(); + await sleepFor(10); expect(pollOnceForKeySpy.callCount).to.eq(4); - expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); - expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.lastCall.args).to.deep.eq([groupConvoPubkey, true]); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]); + expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.getCall(3).args).to.deep.eq([groupConvoPubkey, true, 0]); }); it('does run twice if activeAt is inactive and we tick longer than 2 minutes', async () => { @@ -246,13 +334,19 @@ describe('SwarmPolling', () => { // more than week old, so inactive group but we have to tick after more than 2 min convo.set('active_at', Date.now() - 7 * 25 * 3600 * 1000); clock.tick(timeToTick); - + /** this is not easy to explain, but + * - during the swarmPolling.start, we get two calls to pollOnceForKeySpy (one for our id and one for group od) + * - the clock ticks 9sec, and another call of pollOnceForKeySpy get started, but as we do not await them, this test fails. + * the only fix is to restore the clock and force the a small sleep to let the thing run in bg + */ + clock.restore(); + await sleepFor(10); // we should have two more calls here, so 4 total. expect(pollOnceForKeySpy.callCount).to.eq(4); - expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); - expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true]); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]); + expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, 0]); }); it('does run once only if group is inactive and we tick less than 2 minutes ', async () => { @@ -274,8 +368,8 @@ describe('SwarmPolling', () => { // we should have only one more call here, the one for our direct pubkey fetch expect(pollOnceForKeySpy.callCount).to.eq(3); - expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); // this one comes from the swarmPolling.start - expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, 0]); // this one comes from the swarmPolling.start + expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, 0]); }); describe('multiple runs', () => { @@ -309,11 +403,11 @@ describe('SwarmPolling', () => { // we have 4 calls total. 2 for our direct promises run each 5 seconds, and 2 for the group pubkey active (so run every 5 sec too) expect(pollOnceForKeySpy.callCount).to.eq(4); // first two calls are our pubkey - expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([groupConvoPubkey, true, 0]); - expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true]); + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, 0]); }); it('does run twice if activeAt is more than 2 days old and we tick more than one minute ', async () => { @@ -329,11 +423,12 @@ describe('SwarmPolling', () => { expect(pollOnceForKeySpy.callCount).to.eq(4); // first two calls are our pubkey - expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]); + expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, 0]); + expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([groupConvoPubkey, true, 0]); + + expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([ourPubkey, false, 0]); - expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]); - expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true]); + expect(pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true, 0]); }); }); }); diff --git a/ts/test/session/unit/utils/Messages_test.ts b/ts/test/session/unit/utils/Messages_test.ts index e320f2bfc..8e5dbd456 100644 --- a/ts/test/session/unit/utils/Messages_test.ts +++ b/ts/test/session/unit/utils/Messages_test.ts @@ -3,7 +3,7 @@ import chai from 'chai'; import { TestUtils } from '../../../test-utils'; import { MessageUtils, UserUtils } from '../../../../session/utils'; -import { EncryptionType, PubKey } from '../../../../session/types'; +import { PubKey } from '../../../../session/types'; import { ClosedGroupVisibleMessage } from '../../../../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; import { ConfigurationMessage } from '../../../../session/messages/outgoing/controlMessage/ConfigurationMessage'; @@ -73,7 +73,7 @@ describe('Message Utils', () => { const rawMessage = await MessageUtils.toRawMessage(device, message); const derivedPubKey = PubKey.from(rawMessage.device); - expect(derivedPubKey).to.exist; + expect(derivedPubKey).to.not.be.eq(undefined, 'should maintain pubkey'); expect(derivedPubKey?.isEqual(device)).to.equal( true, 'pubkey of message was not converted correctly' @@ -87,7 +87,7 @@ describe('Message Utils', () => { const message = new ClosedGroupVisibleMessage({ chatMessage, groupId }); const rawMessage = await MessageUtils.toRawMessage(device, message); - expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); + expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE); }); it('should set encryption to Fallback on other messages', async () => { @@ -95,7 +95,7 @@ describe('Message Utils', () => { const message = TestUtils.generateVisibleMessage(); const rawMessage = await MessageUtils.toRawMessage(device, message); - expect(rawMessage.encryption).to.equal(EncryptionType.Fallback); + expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.SESSION_MESSAGE); }); it('passing ClosedGroupNewMessage returns Fallback', async () => { @@ -112,7 +112,7 @@ describe('Message Utils', () => { expireTimer: 0, }); const rawMessage = await MessageUtils.toRawMessage(device, msg); - expect(rawMessage.encryption).to.equal(EncryptionType.Fallback); + expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.SESSION_MESSAGE); }); it('passing ClosedGroupNameChangeMessage returns ClosedGroup', async () => { @@ -124,7 +124,7 @@ describe('Message Utils', () => { groupId: TestUtils.generateFakePubKey().key, }); const rawMessage = await MessageUtils.toRawMessage(device, msg); - expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); + expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE); }); it('passing ClosedGroupAddedMembersMessage returns ClosedGroup', async () => { @@ -136,7 +136,7 @@ describe('Message Utils', () => { groupId: TestUtils.generateFakePubKey().key, }); const rawMessage = await MessageUtils.toRawMessage(device, msg); - expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); + expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE); }); it('passing ClosedGroupRemovedMembersMessage returns ClosedGroup', async () => { @@ -148,7 +148,7 @@ describe('Message Utils', () => { groupId: TestUtils.generateFakePubKey().key, }); const rawMessage = await MessageUtils.toRawMessage(device, msg); - expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); + expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE); }); it('passing ClosedGroupEncryptionPairMessage returns ClosedGroup', async () => { @@ -169,7 +169,7 @@ describe('Message Utils', () => { encryptedKeyPairs: fakeWrappers, }); const rawMessage = await MessageUtils.toRawMessage(device, msg); - expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup); + expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE); }); it('passing ClosedGroupEncryptionKeyPairReply returns Fallback', async () => { @@ -190,7 +190,7 @@ describe('Message Utils', () => { encryptedKeyPairs: fakeWrappers, }); const rawMessage = await MessageUtils.toRawMessage(device, msg); - expect(rawMessage.encryption).to.equal(EncryptionType.Fallback); + expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.SESSION_MESSAGE); }); it('passing a ConfigurationMessage returns Fallback', async () => { @@ -204,7 +204,7 @@ describe('Message Utils', () => { contacts: [], }); const rawMessage = await MessageUtils.toRawMessage(device, msg); - expect(rawMessage.encryption).to.equal(EncryptionType.Fallback); + expect(rawMessage.encryption).to.equal(SignalService.Envelope.Type.SESSION_MESSAGE); }); });