@ -3,7 +3,6 @@ import { describe } from 'mocha';
import Sinon , * as sinon from 'sinon' ;
import chaiAsPromised from 'chai-as-promised' ;
import { ConversationModel } from '../../../../models/conversation' ;
import { getSwarmPollingInstance } from '../../../../session/apis/snode_api' ;
import { resetHardForkCachedValues } from '../../../../session/apis/snode_api/hfHandling' ;
import { SnodeAPIRetrieve } from '../../../../session/apis/snode_api/retrieveRequest' ;
@ -12,10 +11,8 @@ import { SWARM_POLLING_TIMEOUT } from '../../../../session/constants';
import { PubKey } from '../../../../session/types' ;
import { UserUtils } from '../../../../session/utils' ;
import { UserSync } from '../../../../session/utils/job_runners/jobs/UserSyncJob' ;
import { sleepFor } from '../../../../session/utils/Promise' ;
import { UserGroupsWrapperActions } from '../../../../webworker/workers/browser/libsession_worker_interface' ;
import { TestUtils } from '../../../test-utils' ;
import { generateFakeSnodes , stubData , stubLibSessionWorker } from '../../../test-utils/utils' ;
import { generateFakeSnodes , stubData } from '../../../test-utils/utils' ;
import { ConversationTypeEnum } from '../../../../models/types' ;
import { ConvoHub } from '../../../../session/conversations' ;
import { SnodePool } from '../../../../session/apis/snode_api/snodePool' ;
@ -30,11 +27,7 @@ describe('SwarmPolling', () => {
const ourNumber = TestUtils . generateFakePubKeyStr ( ) ;
const ourPubkey = PubKey . cast ( ourNumber ) ;
let pollOnceForKeySpy : Sinon.SinonSpy < any > ;
let swarmPolling : SwarmPolling ;
let getItemByIdStub : Sinon.SinonStub ;
let clock : Sinon.SinonFakeTimers ;
beforeEach ( async ( ) = > {
ConvoHub . use ( ) . reset ( ) ;
@ -45,7 +38,6 @@ describe('SwarmPolling', () => {
Sinon . stub ( UserUtils , 'getOurPubKeyStrFromCache' ) . returns ( ourNumber ) ;
stubData ( 'getAllConversations' ) . resolves ( [ ] ) ;
getItemByIdStub = TestUtils . stubData ( 'getItemById' ) ;
stubData ( 'saveConversation' ) . resolves ( ) ;
stubData ( 'getSwarmNodesForPubkey' ) . resolves ( ) ;
stubData ( 'getLastHashBySnode' ) . resolves ( ) ;
@ -62,7 +54,6 @@ describe('SwarmPolling', () => {
swarmPolling = getSwarmPollingInstance ( ) ;
swarmPolling . resetSwarmPolling ( ) ;
pollOnceForKeySpy = Sinon . spy ( swarmPolling , 'pollOnceForKey' ) ;
clock = sinon . useFakeTimers ( { now : Date.now ( ) , shouldAdvanceTime : true } ) ;
} ) ;
@ -186,270 +177,4 @@ describe('SwarmPolling', () => {
} ) ;
} ) ;
} ) ;
describe ( 'pollForAllKeys' , ( ) = > {
beforeEach ( ( ) = > {
stubData ( 'createOrUpdateItem' ) . resolves ( ) ;
} ) ;
afterEach ( ( ) = > {
Sinon . restore ( ) ;
} ) ;
it ( 'does run for our pubkey even if activeAt is really old ' , async ( ) = > {
stubLibSessionWorker ( [ ] ) ;
const convo = ConvoHub . use ( ) . getOrCreate ( ourNumber , ConversationTypeEnum . PRIVATE ) ;
convo . set ( 'active_at' , Date . now ( ) - 1000 * 3600 * 25 ) ;
await swarmPolling . start ( true ) ;
expect ( pollOnceForKeySpy . callCount ) . to . eq ( 1 ) ;
expect ( pollOnceForKeySpy . firstCall . args [ 0 ] ) . to . deep . eq ( [ ourPubkey . key , 'private' ] ) ;
} ) ;
it ( 'does run for our pubkey even if activeAt is recent ' , async ( ) = > {
stubLibSessionWorker ( [ ] ) ;
const convo = ConvoHub . use ( ) . getOrCreate ( ourNumber , ConversationTypeEnum . PRIVATE ) ;
convo . set ( 'active_at' , Date . now ( ) ) ;
await swarmPolling . start ( true ) ;
expect ( pollOnceForKeySpy . callCount ) . to . eq ( 1 ) ;
expect ( pollOnceForKeySpy . firstCall . args [ 0 ] ) . to . deep . eq ( [ ourPubkey . key , 'private' ] ) ;
} ) ;
describe ( 'legacy group' , ( ) = > {
it ( 'does run for group pubkey on start no matter the recent timestamp' , async ( ) = > {
const convo = ConvoHub . use ( ) . getOrCreate (
TestUtils . generateFakePubKeyStr ( ) ,
ConversationTypeEnum . GROUP
) ;
TestUtils . stubLibSessionWorker ( [ ] ) ;
stubData ( 'removeAllMessagesInConversation' ) . resolves ( ) ;
stubData ( 'getLatestClosedGroupEncryptionKeyPair' ) . resolves ( ) ;
stubData ( 'removeAllClosedGroupEncryptionKeyPairs' ) . resolves ( ) ;
stubData ( 'removeConversation' ) . resolves ( ) ;
stubData ( 'fetchConvoMemoryDetails' ) . resolves ( ) ;
convo . set ( 'active_at' , Date . now ( ) ) ;
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 [ 0 ] ) . to . deep . eq ( [ ourPubkey . key , 'private' ] ) ;
expect ( pollOnceForKeySpy . secondCall . args [ 0 ] ) . to . deep . eq ( [ groupConvoPubkey . key , 'private' ] ) ;
} ) ;
it ( 'does only poll from -10 for closed groups if HF >= 19.1 ' , async ( ) = > {
const convo = ConvoHub . use ( ) . getOrCreate (
TestUtils . generateFakePubKeyStr ( ) ,
ConversationTypeEnum . GROUP
) ;
TestUtils . stubLibSessionWorker ( undefined ) ;
getItemByIdStub . restore ( ) ;
getItemByIdStub = TestUtils . stubData ( '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 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . secondCall . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
getItemByIdStub . restore ( ) ;
getItemByIdStub = TestUtils . stubData ( 'getItemById' ) ;
getItemByIdStub . resolves ( ) ;
} ) ;
it ( 'does run for group pubkey on start but not another time if activeAt is old ' , async ( ) = > {
const convo = ConvoHub . use ( ) . getOrCreate (
TestUtils . generateFakePubKeyStr ( ) ,
ConversationTypeEnum . GROUP
) ;
TestUtils . stubLibSessionWorker ( undefined ) ;
convo . set ( 'active_at' , 1 ) ; // really old, but active
const groupConvoPubkey = PubKey . cast ( convo . id as string ) ;
swarmPolling . addGroupId ( groupConvoPubkey ) ;
// this calls the stub 2 times, one for our direct pubkey and one for the group
await swarmPolling . start ( true ) ;
// this should only call the stub one more time: for our direct pubkey but not for the group pubkey
await swarmPolling . pollForAllKeys ( ) ;
expect ( pollOnceForKeySpy . callCount ) . to . eq ( 3 ) ;
expect ( pollOnceForKeySpy . firstCall . args ) . to . deep . eq ( [ ourPubkey , false , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . secondCall . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
expect ( pollOnceForKeySpy . thirdCall . args ) . to . deep . eq ( [ ourPubkey , false , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
} ) ;
it ( 'does run twice if activeAt less than one hour ' , async ( ) = > {
const convo = ConvoHub . use ( ) . getOrCreate (
TestUtils . generateFakePubKeyStr ( ) ,
ConversationTypeEnum . GROUP
) ;
// fake that the group is part of the wrapper otherwise we stop tracking it after the first polling event
Sinon . stub ( UserGroupsWrapperActions , 'getLegacyGroup' ) . resolves ( { } as any ) ;
convo . set ( 'active_at' , Date . now ( ) ) ;
const groupConvoPubkey = PubKey . cast ( convo . id as string ) ;
swarmPolling . addGroupId ( groupConvoPubkey ) ;
await swarmPolling . start ( true ) ;
expect ( pollOnceForKeySpy . callCount ) . to . eq ( 2 ) ;
expect ( pollOnceForKeySpy . firstCall . args ) . to . deep . eq ( [ ourPubkey , false , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . secondCall . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
pollOnceForKeySpy . resetHistory ( ) ;
clock . tick ( 9000 ) ;
// no need to do that as the tick will trigger a call in all cases after 5 secs await swarmPolling.pollForAllKeys();
/ * * t h i s i s n o t e a s y t o e x p l a i n , b u t
* - during the swarmPolling . start , we get two calls to pollOnceForKeySpy ( one for our id and one for group id )
* - the clock ticks 9 sec , 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
* /
await sleepFor ( 10 ) ;
expect ( pollOnceForKeySpy . callCount ) . to . eq ( 2 ) ;
expect ( pollOnceForKeySpy . firstCall . args ) . to . deep . eq ( [ ourPubkey , false , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . secondCall . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
} ) ;
it ( 'does run twice if activeAt is inactive and we tick longer than 2 minutes' , async ( ) = > {
const convo = ConvoHub . use ( ) . getOrCreate (
TestUtils . generateFakePubKeyStr ( ) ,
ConversationTypeEnum . GROUP
) ;
// fake that the group is part of the wrapper otherwise we stop tracking it after the first polling event
Sinon . stub ( UserGroupsWrapperActions , 'getLegacyGroup' ) . resolves ( { } as any ) ;
pollOnceForKeySpy . resetHistory ( ) ;
convo . set ( 'active_at' , Date . now ( ) ) ;
const groupConvoPubkey = convo . id ;
swarmPolling . addGroupId ( groupConvoPubkey ) ;
// this call the stub two times already, one for our direct pubkey and one for the group
await swarmPolling . start ( true ) ;
const timeToTick = 3 * 60 * 1000 ;
swarmPolling . forcePolledTimestamp ( groupConvoPubkey , Date . now ( ) - timeToTick ) ;
// 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 ) ;
/ * * t h i s i s n o t e a s y t o e x p l a i n , b u t
* - during the swarmPolling . start , we get two calls to pollOnceForKeySpy ( one for our id and one for group od )
* - the clock ticks 9 sec , 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
* /
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 , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . secondCall . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
expect ( pollOnceForKeySpy . thirdCall . args ) . to . deep . eq ( [ ourPubkey , false , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . getCalls ( ) [ 3 ] . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
} ) ;
it ( 'does run once only if group is inactive and we tick less than 2 minutes ' , async ( ) = > {
const convo = ConvoHub . use ( ) . getOrCreate (
TestUtils . generateFakePubKeyStr ( ) ,
ConversationTypeEnum . GROUP
) ;
pollOnceForKeySpy . resetHistory ( ) ;
TestUtils . stubLibSessionWorker ( undefined ) ;
convo . set ( 'active_at' , Date . now ( ) ) ;
const groupConvoPubkey = PubKey . cast ( convo . id as string ) ;
swarmPolling . addGroupId ( groupConvoPubkey ) ;
await swarmPolling . start ( true ) ;
// more than a week old, we should not tick after just 5 seconds
convo . set ( 'active_at' , Date . now ( ) - 7 * 24 * 3600 * 1000 - 3600 * 1000 ) ;
clock . tick ( 1 * 60 * 1000 ) ;
await sleepFor ( 10 ) ;
// 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 , [ - 10 ] ] ) ; // this one comes from the swarmPolling.start
expect ( pollOnceForKeySpy . thirdCall . args ) . to . deep . eq ( [ ourPubkey , false , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
} ) ;
describe ( 'multiple runs' , ( ) = > {
let convo : ConversationModel ;
let groupConvoPubkey : PubKey ;
beforeEach ( async ( ) = > {
convo = ConvoHub . use ( ) . getOrCreate (
TestUtils . generateFakePubKeyStr ( ) ,
ConversationTypeEnum . GROUP
) ;
TestUtils . stubLibSessionWorker ( { } ) ;
convo . set ( 'active_at' , Date . now ( ) ) ;
groupConvoPubkey = PubKey . cast ( convo . id as string ) ;
swarmPolling . addGroupId ( groupConvoPubkey ) ;
await swarmPolling . start ( true ) ;
} ) ;
afterEach ( ( ) = > {
Sinon . restore ( ) ;
ConvoHub . use ( ) . reset ( ) ;
clock . restore ( ) ;
resetHardForkCachedValues ( ) ;
} ) ;
it ( 'does run twice if activeAt is less than 2 days' , async ( ) = > {
pollOnceForKeySpy . resetHistory ( ) ;
// less than 2 days old, this is an active group
convo . set ( 'active_at' , Date . now ( ) - 2 * 24 * 3600 * 1000 - 3600 * 1000 ) ;
const timeToTick = 6 * 1000 ;
swarmPolling . forcePolledTimestamp ( convo . id , timeToTick ) ;
// we tick more than 5 sec
clock . tick ( timeToTick ) ;
await swarmPolling . pollForAllKeys ( ) ;
// 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 , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . secondCall . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
expect ( pollOnceForKeySpy . thirdCall . args ) . to . deep . eq ( [ ourPubkey , false , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . getCalls ( ) [ 3 ] . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
} ) ;
it ( 'does run twice if activeAt is more than 2 days old and we tick more than one minute' , async ( ) = > {
pollOnceForKeySpy . resetHistory ( ) ;
TestUtils . stubWindowLog ( ) ;
convo . set ( 'active_at' , Date . now ( ) - 2 * 25 * 3600 * 1000 ) ; // medium active
// fake that the group is part of the wrapper otherwise we stop tracking it after the first polling event
const timeToTick = 65 * 1000 ; // more than one minute
swarmPolling . forcePolledTimestamp ( convo . id , timeToTick ) ;
clock . tick ( timeToTick ) ; // should tick twice more (one more our direct pubkey and one for the group)
// fake that the group is part of the wrapper otherwise we stop tracking it after the first polling event
await swarmPolling . pollForAllKeys ( ) ;
expect ( pollOnceForKeySpy . callCount ) . to . eq ( 4 ) ;
// first two calls are our pubkey
expect ( pollOnceForKeySpy . firstCall . args ) . to . deep . eq ( [ ourPubkey , false , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . secondCall . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
expect ( pollOnceForKeySpy . thirdCall . args ) . to . deep . eq ( [ ourPubkey , false , [ 0 , 2 , 3 , 5 , 4 ] ] ) ;
expect ( pollOnceForKeySpy . getCalls ( ) [ 3 ] . args ) . to . deep . eq ( [ groupConvoPubkey , true , [ - 10 ] ] ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;