import { expect } from 'chai'; import { LegacyGroupInfo, UserGroupsGet } from 'libsession_util_nodejs'; import Sinon from 'sinon'; import { ConversationTypeEnum } from '../../../../models/conversationAttributes'; import { getSwarmPollingInstance } from '../../../../session/apis/snode_api'; import { resetHardForkCachedValues } from '../../../../session/apis/snode_api/hfHandling'; import { SwarmPolling } from '../../../../session/apis/snode_api/swarmPolling'; import { SWARM_POLLING_TIMEOUT } from '../../../../session/constants'; import { PubKey } from '../../../../session/types'; import { UserUtils } from '../../../../session/utils'; import { TestUtils } from '../../../test-utils'; import { stubData } from '../../../test-utils/utils'; describe('getPollingDetails', () => { // Initialize new stubbed cache const ourPubkey = TestUtils.generateFakePubKey(); const ourNumber = ourPubkey.key; let swarmPolling: SwarmPolling; let clock: Sinon.SinonFakeTimers; beforeEach(async () => { TestUtils.stubWindowFeatureFlags(); TestUtils.stubWindowLog(); stubData('createOrUpdateItem').resolves(); Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(ourNumber); swarmPolling = getSwarmPollingInstance(); TestUtils.stubLibSessionWorker(undefined); clock = Sinon.useFakeTimers({ now: Date.now(), shouldAdvanceTime: true }); }); afterEach(() => { Sinon.restore(); clock.restore(); resetHardForkCachedValues(); }); it('without anything else, we should be part of it', async () => { TestUtils.stubUserGroupWrapper('getAllLegacyGroups', []); TestUtils.stubUserGroupWrapper('getAllGroups', []); swarmPolling.resetSwarmPolling(); const details = await swarmPolling.getPollingDetails([]); expect(details.toPollDetails.length).to.be.eq(1); expect(details.toPollDetails[0][0]).to.be.eq(ourNumber); }); it('throws if polling entries include our pk', async () => { TestUtils.stubUserGroupWrapper('getAllLegacyGroups', []); TestUtils.stubUserGroupWrapper('getAllGroups', []); swarmPolling.resetSwarmPolling(); const fn = async () => swarmPolling.getPollingDetails([{ pubkey: PubKey.cast(ourPubkey), lastPolledTimestamp: 0 }]); await expect(fn()).to.be.rejectedWith(''); }); describe("groups not in wrapper should be included in 'to leave' only", () => { it('legacy group', async () => { TestUtils.stubUserGroupWrapper('getAllLegacyGroups', []); TestUtils.stubUserGroupWrapper('getAllGroups', []); const groupPk = TestUtils.generateFakePubKeyStr(); Sinon.stub(swarmPolling, 'getPollingTimeout').returns(SWARM_POLLING_TIMEOUT.ACTIVE); const { groupsToLeave, legacyGroupsToLeave, toPollDetails } = await swarmPolling.getPollingDetails([ { pubkey: PubKey.cast(groupPk), lastPolledTimestamp: 0 }, ]); expect(toPollDetails.length).to.be.eq(1); expect(toPollDetails[0]).to.be.deep.eq([ourNumber, ConversationTypeEnum.PRIVATE]); expect(legacyGroupsToLeave.length).to.be.eq(1); expect(legacyGroupsToLeave[0]).to.be.eq(groupPk); expect(groupsToLeave.length).to.be.eq(0); }); it('new group NOT in wrapper should be requested for leaving', async () => { TestUtils.stubUserGroupWrapper('getAllLegacyGroups', []); TestUtils.stubUserGroupWrapper('getAllGroups', []); const groupPk = TestUtils.generateFakeClosedGroupV2PkStr(); Sinon.stub(swarmPolling, 'getPollingTimeout').returns(SWARM_POLLING_TIMEOUT.ACTIVE); const { groupsToLeave, legacyGroupsToLeave, toPollDetails } = await swarmPolling.getPollingDetails([ { pubkey: PubKey.cast(groupPk), lastPolledTimestamp: 0 }, ]); expect(toPollDetails.length).to.be.eq(1); expect(toPollDetails[0]).to.be.deep.eq([ourNumber, ConversationTypeEnum.PRIVATE]); expect(groupsToLeave.length).to.be.eq(1); expect(groupsToLeave[0]).to.be.eq(groupPk); expect(legacyGroupsToLeave.length).to.be.eq(0); }); }); describe('groups in wrapper but polled recently should not be polled and not to leave neither', () => { it('legacy group', async () => { const groupPk = TestUtils.generateFakePubKeyStr(); TestUtils.stubUserGroupWrapper('getAllLegacyGroups', [ { pubkeyHex: groupPk } as LegacyGroupInfo, ]); TestUtils.stubUserGroupWrapper('getAllGroups', []); Sinon.stub(swarmPolling, 'getPollingTimeout').returns(SWARM_POLLING_TIMEOUT.ACTIVE); const { groupsToLeave, legacyGroupsToLeave, toPollDetails } = await swarmPolling.getPollingDetails([ { pubkey: PubKey.cast(groupPk), lastPolledTimestamp: Date.now() }, ]); expect(toPollDetails.length).to.be.eq(1); expect(toPollDetails[0]).to.be.deep.eq([ourNumber, ConversationTypeEnum.PRIVATE]); expect(legacyGroupsToLeave.length).to.be.eq(0); expect(groupsToLeave.length).to.be.eq(0); }); it('new group', async () => { const groupPk = TestUtils.generateFakeClosedGroupV2PkStr(); TestUtils.stubUserGroupWrapper('getAllLegacyGroups', []); TestUtils.stubUserGroupWrapper('getAllGroups', [{ pubkeyHex: groupPk } as UserGroupsGet]); Sinon.stub(swarmPolling, 'getPollingTimeout').returns(SWARM_POLLING_TIMEOUT.ACTIVE); const { groupsToLeave, legacyGroupsToLeave, toPollDetails } = await swarmPolling.getPollingDetails([ { pubkey: PubKey.cast(groupPk), lastPolledTimestamp: Date.now() }, ]); expect(toPollDetails.length).to.be.eq(1); expect(toPollDetails[0]).to.be.deep.eq([ourNumber, ConversationTypeEnum.PRIVATE]); expect(groupsToLeave.length).to.be.eq(0); expect(legacyGroupsToLeave.length).to.be.eq(0); }); }); describe("groups in wrapper should be included in 'to poll' only", () => { it('legacy group in wrapper should be polled', async () => { const groupPk = TestUtils.generateFakePubKeyStr(); TestUtils.stubUserGroupWrapper('getAllLegacyGroups', [ { pubkeyHex: groupPk } as LegacyGroupInfo, ]); TestUtils.stubUserGroupWrapper('getAllGroups', []); swarmPolling.resetSwarmPolling(); Sinon.stub(swarmPolling, 'getPollingTimeout').returns(SWARM_POLLING_TIMEOUT.ACTIVE); const { groupsToLeave, legacyGroupsToLeave, toPollDetails } = await swarmPolling.getPollingDetails([ { pubkey: PubKey.cast(groupPk), lastPolledTimestamp: 0 }, ]); expect(toPollDetails.length).to.be.eq(2, 'both our and closed group should be polled'); expect(toPollDetails[0]).to.be.deep.eq([ourNumber, ConversationTypeEnum.PRIVATE]); expect(toPollDetails[1]).to.be.deep.eq([groupPk, ConversationTypeEnum.GROUP]); // no groups to leave nor legacy ones expect(legacyGroupsToLeave.length).to.be.eq(0); expect(groupsToLeave.length).to.be.eq(0); }); it('new group in wrapper should be polled', async () => { const groupPk = TestUtils.generateFakeClosedGroupV2PkStr(); TestUtils.stubUserGroupWrapper('getAllLegacyGroups', []); TestUtils.stubUserGroupWrapper('getAllGroups', [{ pubkeyHex: groupPk } as UserGroupsGet]); Sinon.stub(swarmPolling, 'getPollingTimeout').returns(SWARM_POLLING_TIMEOUT.ACTIVE); const { groupsToLeave, legacyGroupsToLeave, toPollDetails } = await swarmPolling.getPollingDetails([ { pubkey: PubKey.cast(groupPk), lastPolledTimestamp: 0 }, ]); expect(toPollDetails.length).to.be.eq(2); expect(toPollDetails[0]).to.be.deep.eq([ourNumber, ConversationTypeEnum.PRIVATE]); expect(toPollDetails[1]).to.be.deep.eq([groupPk, ConversationTypeEnum.GROUPV2]); // no groups to leave nor legacy ones expect(legacyGroupsToLeave.length).to.be.eq(0); expect(groupsToLeave.length).to.be.eq(0); }); }); describe('multiple groups', () => { it('one legacy group with a few v2 group not in wrapper', async () => { const groupPk = TestUtils.generateFakePubKeyStr(); const groupV2Pk = TestUtils.generateFakeClosedGroupV2PkStr(); const groupV2Pk2 = TestUtils.generateFakeClosedGroupV2PkStr(); TestUtils.stubUserGroupWrapper('getAllLegacyGroups', [ { pubkeyHex: groupPk } as LegacyGroupInfo, ]); TestUtils.stubUserGroupWrapper('getAllGroups', []); swarmPolling.resetSwarmPolling(); Sinon.stub(swarmPolling, 'getPollingTimeout').returns(SWARM_POLLING_TIMEOUT.ACTIVE); const { groupsToLeave, legacyGroupsToLeave, toPollDetails } = await swarmPolling.getPollingDetails([ { pubkey: PubKey.cast(groupPk), lastPolledTimestamp: 0 }, { pubkey: PubKey.cast(groupV2Pk), lastPolledTimestamp: 0 }, { pubkey: PubKey.cast(groupV2Pk2), lastPolledTimestamp: 0 }, ]); expect(toPollDetails.length).to.be.eq(2, 'both our and closed group should be polled'); expect(toPollDetails[0]).to.be.deep.eq([ourNumber, ConversationTypeEnum.PRIVATE]); expect(toPollDetails[1]).to.be.deep.eq([groupPk, ConversationTypeEnum.GROUP]); expect(legacyGroupsToLeave.length).to.be.eq(0); expect(groupsToLeave.length).to.be.eq(2); expect(groupsToLeave[0]).to.be.deep.eq(groupV2Pk); expect(groupsToLeave[1]).to.be.deep.eq(groupV2Pk2); }); it('new group in wrapper with a few legacy groups not in wrapper', async () => { const groupPk = TestUtils.generateFakeClosedGroupV2PkStr(); const groupPkLeg1 = TestUtils.generateFakePubKeyStr(); const groupPkLeg2 = TestUtils.generateFakePubKeyStr(); TestUtils.stubUserGroupWrapper('getAllLegacyGroups', []); TestUtils.stubUserGroupWrapper('getAllGroups', [{ pubkeyHex: groupPk } as UserGroupsGet]); Sinon.stub(swarmPolling, 'getPollingTimeout').returns(SWARM_POLLING_TIMEOUT.ACTIVE); const { groupsToLeave, legacyGroupsToLeave, toPollDetails } = await swarmPolling.getPollingDetails([ { pubkey: PubKey.cast(groupPk), lastPolledTimestamp: 0 }, { pubkey: PubKey.cast(groupPkLeg1), lastPolledTimestamp: 0 }, { pubkey: PubKey.cast(groupPkLeg2), lastPolledTimestamp: 0 }, ]); expect(toPollDetails.length).to.be.eq(2); expect(toPollDetails[0]).to.be.deep.eq([ourNumber, ConversationTypeEnum.PRIVATE]); expect(toPollDetails[1]).to.be.deep.eq([groupPk, ConversationTypeEnum.GROUPV2]); expect(legacyGroupsToLeave.length).to.be.eq(2); expect(legacyGroupsToLeave[0]).to.be.eq(groupPkLeg1); expect(legacyGroupsToLeave[1]).to.be.eq(groupPkLeg2); expect(groupsToLeave.length).to.be.eq(0); }); it('two of each, all should be polled', async () => { const groupPk1 = TestUtils.generateFakeClosedGroupV2PkStr(); const groupPk2 = TestUtils.generateFakeClosedGroupV2PkStr(); const groupPkLeg1 = TestUtils.generateFakePubKeyStr(); const groupPkLeg2 = TestUtils.generateFakePubKeyStr(); TestUtils.stubUserGroupWrapper('getAllLegacyGroups', [ { pubkeyHex: groupPkLeg1 } as LegacyGroupInfo, { pubkeyHex: groupPkLeg2 } as LegacyGroupInfo, ]); TestUtils.stubUserGroupWrapper('getAllGroups', [ { pubkeyHex: groupPk1 } as UserGroupsGet, { pubkeyHex: groupPk2 } as UserGroupsGet, ]); Sinon.stub(swarmPolling, 'getPollingTimeout').returns(SWARM_POLLING_TIMEOUT.ACTIVE); const { groupsToLeave, legacyGroupsToLeave, toPollDetails } = await swarmPolling.getPollingDetails([ { pubkey: PubKey.cast(groupPk1), lastPolledTimestamp: 0 }, { pubkey: PubKey.cast(groupPk2), lastPolledTimestamp: 0 }, { pubkey: PubKey.cast(groupPkLeg1), lastPolledTimestamp: 0 }, { pubkey: PubKey.cast(groupPkLeg2), lastPolledTimestamp: 0 }, ]); expect(toPollDetails.length).to.be.eq(5); expect(toPollDetails[0]).to.be.deep.eq([ourNumber, ConversationTypeEnum.PRIVATE]); expect(toPollDetails[1]).to.be.deep.eq([groupPkLeg1, ConversationTypeEnum.GROUP]); expect(toPollDetails[2]).to.be.deep.eq([groupPkLeg2, ConversationTypeEnum.GROUP]); expect(toPollDetails[3]).to.be.deep.eq([groupPk1, ConversationTypeEnum.GROUPV2]); expect(toPollDetails[4]).to.be.deep.eq([groupPk2, ConversationTypeEnum.GROUPV2]); // no groups to leave nor legacy ones expect(legacyGroupsToLeave.length).to.be.eq(0); expect(groupsToLeave.length).to.be.eq(0); }); }); });