chore: fix unit tests for userconfig changes

This commit is contained in:
Audric Ackermann 2023-05-18 16:28:01 +10:00
parent 10f6f9c892
commit 6676bf77f9
24 changed files with 104 additions and 271 deletions

View file

@ -84,7 +84,7 @@ export class LeftPaneMessageSection extends React.Component<Props> {
throw new Error('renderRow: conversations selector returned element containing falsy value.');
}
return <MemoConversationListItemWithDetails key={key} style={style} {...conversation} />;
return <MemoConversationListItemWithDetails key={key} style={style} {...conversation} />; // TODO there should not be a need for the ...conversation here?
};
public renderList(): JSX.Element {

View file

@ -34,7 +34,7 @@ const MessageRequestList = () => {
return (
<MessageRequestListContainer>
{conversationRequests.map(conversation => {
return <MemoConversationListItemWithDetails key={conversation.id} {...conversation} />;
return <MemoConversationListItemWithDetails key={conversation.id} {...conversation} />; // TODO there should not be a need for the ...conversation here?
})}
</MessageRequestListContainer>
);

View file

@ -1000,9 +1000,7 @@ async function handleConfigurationMessageLegacy(
}
await handleOurProfileUpdateLegacy(envelope.timestamp, configurationMessage);
await handleGroupsAndContactsFromConfigMessageLegacy(envelope, configurationMessage);
await removeFromCache(envelope);
}

View file

@ -119,7 +119,7 @@ async function handlePollInfoResponse(
read,
write,
upload,
active_users: active_users,
active_users,
details: pick(
details,
'admins',

View file

@ -16,7 +16,7 @@ export enum SnodeNamespaces {
*/
UserContacts = 3,
/**
* This is the namespace used to sync our contacts
* This is the namespace used to sync our volatile info (currently read status only)
*/
ConvoInfoVolatile = 4,
@ -94,9 +94,9 @@ function namespacePriority(namespace: SnodeNamespaces): number {
default:
try {
assertUnreachable(namespace, `isUserConfigNamespace case not handled: ${namespace}`);
assertUnreachable(namespace, `namespacePriority case not handled: ${namespace}`);
} catch (e) {
window.log.warn(`isUserConfigNamespace case not handled: ${namespace}: ${e.message}`);
window.log.warn(`namespacePriority case not handled: ${namespace}: ${e.message}`);
return 1;
}
}

View file

@ -172,7 +172,6 @@ export class SwarmPolling {
getConversationController()
.get(group.pubkey.key)
?.idForLogging() || group.pubkey.key;
if (diff >= convoPollingTimeout) {
window?.log?.debug(
`Polling for ${loggingId}; timeout: ${convoPollingTimeout}; diff: ${diff} `

View file

@ -1,55 +0,0 @@
// import { SignalService } from '../../../../../../protobuf';
// import { isArray } from 'lodash';
// import { GroupMessage, GroupMessageParams } from './GroupMessage';
// // import { PubKey } from '../../../../../types';
// // import { from_hex } from 'libsodium-wrappers-sumo';
// interface GroupAdminMessageParams extends GroupMessageParams {
// /*
// * A proof that we are an admin (a proof that we have access to the private key of the closed group).
// * this field is needed for all types of admin messages so that every member can make
// */
// groupSignature: Uint8Array;
// }
// interface GroupAdminMessageParams extends GroupMessageParams {
// /**
// * hex string of the members to delete the group from.
// * a single '*' is allowed too and means 'every members'
// *
// */
// members: Array<string>;
// }
// export class GroupAdminGroupMessage extends GroupMessage {
// // private readonly members: Array<string>;
// constructor(params: GroupAdminMessageParams) {
// super(params);
// if (!params.members || !isArray(params.members) || !params.members.length) {
// throw new Error('members parameter must be set');
// }
// // if (params.members.length === 1 && params.members[0] === '*') {
// // this.members = params.members;
// // } else {
// // const allAreValid = params.members.every(PubKey.isValidGroupPubkey);
// // if (!allAreValid) {
// // throw new Error('One of the members is not a `isValidGroupPubkey`');
// // }
// // this.members = params.members;
// // }
// throw new Error('TODO and add tests');
// }
// public dataProto(): SignalService.DataMessage {
// const dataMessage = new SignalService.DataMessage();
// dataMessage.groupMessage = super.groupMessage();
// dataMessage.groupMessage.adminMessage = new SignalService.GroupAdminMessage();
// // dataMessage.groupMessage.members = this.members.map(from_hex);
// return dataMessage;
// }
// }

View file

@ -1,48 +0,0 @@
// import { SignalService } from '../../../../../../protobuf';
// import { isEmpty, isString } from 'lodash';
// import { GroupMessage, GroupMessageParams } from './GroupMessage';
// import { PubKey } from '../../../../../types';
// import { from_hex } from 'libsodium-wrappers-sumo';
// export interface GroupInviteMessageParams extends GroupMessageParams {
// name: string;
// /**
// * hex string of that member private key
// */
// memberPrivateKey: string;
// }
// export class GroupInviteMessage extends GroupMessage {
// private readonly name: string;
// private readonly memberPrivateKey: string;
// constructor(params: GroupInviteMessageParams) {
// super(params);
// if (!params.name || isEmpty(params.name) || !isString(params.name)) {
// throw new Error('name parameter must be valid');
// }
// if (
// !params.memberPrivateKey ||
// isEmpty(params.memberPrivateKey) ||
// !isString(params.memberPrivateKey) ||
// !PubKey.isHexOnly(params.memberPrivateKey)
// ) {
// throw new Error('memberPrivateKey parameter must be valid');
// }
// this.name = params.name;
// this.memberPrivateKey = params.memberPrivateKey;
// }
// public dataProto(): SignalService.DataMessage {
// const dataMessage = new SignalService.DataMessage();
// dataMessage.groupMessage = super.groupMessage();
// dataMessage.groupMessage.inviteMessage = new SignalService.GroupInviteMessage();
// dataMessage.groupMessage.inviteMessage.name = this.name;
// dataMessage.groupMessage.inviteMessage.memberPrivateKey = from_hex(this.memberPrivateKey);
// return dataMessage;
// }
// }

View file

@ -1,18 +0,0 @@
// import { SignalService } from '../../../../../../protobuf';
// import { GroupMessage, GroupMessageParams } from './GroupMessage';
// interface GroupMemberLeftMessageParams extends GroupMessageParams {}
// export class GroupMemberLeftMessage extends GroupMessage {
// constructor(params: GroupMemberLeftMessageParams) {
// super(params);
// }
// public dataProto(): SignalService.DataMessage {
// const dataMessage = new SignalService.DataMessage();
// dataMessage.groupMessage = super.groupMessage();
// dataMessage.groupMessage.memberLeftMessage = new SignalService.GroupMemberLeftMessage();
// return dataMessage;
// }
// }

View file

@ -1,18 +0,0 @@
// import { SignalService } from '../../../../../../protobuf';
// import { DataMessage } from '../../../DataMessage';
// import { MessageParams } from '../../../Message';
// export interface GroupMessageParams extends MessageParams {}
// export abstract class GroupMessage extends DataMessage {
// constructor(params: GroupMessageParams) {
// super({
// timestamp: params.timestamp,
// identifier: params.identifier,
// });
// }
// protected groupMessage() {
// return new SignalService.GroupMessage();
// }
// }

View file

@ -1,34 +0,0 @@
// import { SignalService } from '../../../../../../protobuf';
// import { isEmpty } from 'lodash';
// import { GroupMessage, GroupMessageParams } from './GroupMessage';
// import { from_hex } from 'libsodium-wrappers-sumo';
// export interface GroupPromoteMessageParams extends GroupMessageParams {
// /**
// * hex string of the group private key
// */
// privateKey: string;
// }
// export class GroupPromoteMessage extends GroupMessage {
// private readonly privateKey: string;
// constructor(params: GroupPromoteMessageParams) {
// super(params);
// if (!params.privateKey || isEmpty(params.privateKey)) {
// throw new Error('privateKey parameter must be set');
// }
// this.privateKey = params.privateKey;
// }
// public dataProto(): SignalService.DataMessage {
// const dataMessage = new SignalService.DataMessage();
// dataMessage.groupMessage = super.groupMessage();
// dataMessage.groupMessage.promoteMessage = new SignalService.GroupPromoteMessage();
// dataMessage.groupMessage.promoteMessage.privateKey = from_hex(this.privateKey);
// return dataMessage;
// }
// }

View file

@ -1,6 +1,5 @@
export type JobRunnerType =
| 'ConfigurationSyncJob'
| 'ConfigurationSyncDumpJob'
| 'FakeSleepForJob'
| 'FakeSleepForMultiJob'
| 'AvatarDownloadJob';

View file

@ -7,7 +7,7 @@ import {
clickOnMatchingText,
clickOnTestIdWithText,
waitForMatchingText,
waitForTestIdWithText
waitForTestIdWithText,
} from './utilities/utils';
// tslint:disable: no-console
@ -20,9 +20,9 @@ sessionTestTwoWindows('Disappearing messages', async ([windowA, windowB]) => {
// Create User
const [userA, userB] = await Promise.all([newUser(windowA, 'Alice'), newUser(windowB, 'Bob')]);
// Create Contact
await createContact(windowA, windowB, userA, userB)
await createContact(windowA, windowB, userA, userB);
// Click on user's avatar to open conversation options
await clickOnTestIdWithText(windowA, 'conversation-options-avatar')
await clickOnTestIdWithText(windowA, 'conversation-options-avatar');
// Select disappearing messages drop down
await clickOnMatchingText(windowA, 'Disappearing messages');
// Select 5 seconds
@ -74,7 +74,7 @@ sessionTestTwoWindows('Disappearing messages', async ([windowA, windowB]) => {
`${userA.userName} set the disappearing message timer to 5 seconds`
);
// Wait 5 seconds
await sleepFor(5000)
await sleepFor(5000);
await waitForMatchingText(windowB, `${userA.userName} has turned off disappearing messages.`);
// verify message is deleted in windowB
const errorDesc2 = 'Should not be found';

View file

@ -14,7 +14,7 @@ import {
typeIntoInput,
waitForMatchingText,
waitForTestIdWithText,
waitForTextMessage
waitForTextMessage,
} from './utilities/utils';
const windows: Array<Page> = [];

View file

@ -27,14 +27,14 @@ function cleanUpOtherTest() {
const parentFolderOfAllDataPath = isMacOS()
? '~/Library/Application Support/'
: isLinux()
? homedir() + '/.config/'
? `${homedir()}/.config/`
: null;
if (!parentFolderOfAllDataPath) {
throw new Error('Only macOS is currrently supported ');
}
if (!parentFolderOfAllDataPath || parentFolderOfAllDataPath.length < 9) {
throw new Error('parentFolderOfAllDataPath not found or invalid:' + parentFolderOfAllDataPath);
throw new Error(`parentFolderOfAllDataPath not found or invalid: ${parentFolderOfAllDataPath}`);
}
console.info('cleaning other tests leftovers...', parentFolderOfAllDataPath);

View file

@ -17,7 +17,7 @@ type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N
type CountWindows = 1 | 2 | 3 | 4 | 5;
async function sessionTest<T extends CountWindows, N extends Tuple<Page, T>>(
function sessionTest<T extends CountWindows, N extends Tuple<Page, T>>(
testName: string,
testCallback: (windows: N) => Promise<void>,
count: T
@ -43,35 +43,35 @@ async function sessionTest<T extends CountWindows, N extends Tuple<Page, T>>(
});
}
export async function sessionTestOneWindow(
export function sessionTestOneWindow(
testName: string,
testCallback: (windows: Tuple<Page, 1>) => Promise<void>
) {
return sessionTest(testName, testCallback, 1);
}
export async function sessionTestTwoWindows(
export function sessionTestTwoWindows(
testName: string,
testCallback: ([windowA, windowB]: [Page, Page]) => Promise<void>
) {
return sessionTest(testName, testCallback, 2);
}
export async function sessionTestThreeWindows(
export function sessionTestThreeWindows(
testName: string,
testCallback: ([windowA, windowB, windowC]: [Page, Page, Page]) => Promise<void>
) {
return sessionTest(testName, testCallback, 3);
}
export async function sessionTestFourWindows(
export function sessionTestFourWindows(
testName: string,
testCallback: ([windowA, windowB, windowC, windowD]: [Page, Page, Page, Page]) => Promise<void>
) {
return sessionTest(testName, testCallback, 4);
}
export async function sessionTestFiveWindows(
export function sessionTestFiveWindows(
testName: string,
testCallback: ([windowA, windowB, windowC, windowD]: [Page, Page, Page, Page, Page]) => Promise<
void

View file

@ -0,0 +1,10 @@
{
"defaultSeverity": "error",
"extends": ["../tslint.json"],
"rules": {
"no-empty": false,
"array-type": false,
"no-console": false,
"no-void-expression": false
}
}

View file

@ -94,11 +94,4 @@ describe('filterDuplicatesFromDbAndIncomingV4', () => {
expect(filtered[0]).to.be.deep.eq(msg1);
});
});
describe('filters duplicated message from database', () => {
//sadly better-sqlite3 does not allow us to easily create an in memory db for now (issues with sqlite binary)
// so testing this part is not easy as all the logic is made in sqlite
// tslint:disable-next-line: no-empty
it.skip('in memory database', () => {});
});
});

View file

@ -1,7 +1,7 @@
// tslint:disable: no-implicit-dependencies
import { SignalService } from '../../../../protobuf';
import chai from 'chai';
import { SignalService } from '../../../../protobuf';
import { ConfigurationMessage } from '../../../../session/messages/outgoing/controlMessage/ConfigurationMessage';
import { UserUtils } from '../../../../session/utils';
@ -13,13 +13,15 @@ import { EnvelopePlus } from '../../../../receiver/types';
import chaiAsPromised from 'chai-as-promised';
import { ConfigMessageHandler } from '../../../../receiver/configMessage';
import { ConfigurationSync } from '../../../../session/utils/job_runners/jobs/ConfigurationSyncJob';
import { ReleasedFeatures } from '../../../../util/releaseFeature';
import { stubData } from '../../../test-utils/utils';
chai.use(chaiAsPromised as any);
chai.should();
const { expect } = chai;
describe('ConfigurationMessage_receiving', () => {
describe('handleConfigurationMessageLegacy_receiving', () => {
let createOrUpdateStub: Sinon.SinonStub<any>;
let getItemByIdStub: Sinon.SinonStub<any>;
let sender: string;
@ -39,6 +41,8 @@ describe('ConfigurationMessage_receiving', () => {
displayName: 'displayName',
contacts: [],
});
Sinon.stub(ConfigurationSync, 'queueNewJobIfNeeded').resolves();
TestUtils.stubWindow('setSettingValue', () => undefined);
});
afterEach(() => {
@ -46,18 +50,26 @@ describe('ConfigurationMessage_receiving', () => {
});
it('should not be processed if we do not have a pubkey', async () => {
TestUtils.stubWindowLog();
Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').resolves(undefined);
envelope = TestUtils.generateEnvelopePlus(sender);
const proto = config.contentProto();
createOrUpdateStub = stubData('createOrUpdateItem').resolves();
getItemByIdStub = stubData('getItemById').resolves();
const checkIsUserConfigFeatureReleasedStub = Sinon.stub(
ReleasedFeatures,
'checkIsUserConfigFeatureReleased'
).resolves(false);
await ConfigMessageHandler.handleConfigurationMessageLegacy(
envelope,
proto.configurationMessage as SignalService.ConfigurationMessage
);
expect(createOrUpdateStub.callCount).to.equal(0);
expect(getItemByIdStub.callCount).to.equal(0);
expect(checkIsUserConfigFeatureReleasedStub.callCount).to.be.eq(1); // should only have the one as part of the global legacy check, but none for the smaller handlers
});
describe('with ourNumber set', () => {
@ -71,7 +83,7 @@ describe('ConfigurationMessage_receiving', () => {
const proto = config.contentProto();
// sender !== ourNumber
envelope = TestUtils.generateEnvelopePlus(sender);
Sinon.stub(ReleasedFeatures, 'checkIsUserConfigFeatureReleased').resolves(false);
createOrUpdateStub = stubData('createOrUpdateItem').resolves();
getItemByIdStub = stubData('getItemById').resolves();
await ConfigMessageHandler.handleConfigurationMessageLegacy(
@ -81,15 +93,5 @@ describe('ConfigurationMessage_receiving', () => {
expect(createOrUpdateStub.callCount).to.equal(0);
expect(getItemByIdStub.callCount).to.equal(0);
});
// it('should be processed if the message is coming from our number', async () => {
// const proto = config.contentProto();
// envelope = TestUtils.generateEnvelopePlus(ourNumber);
// createOrUpdateStub = sandbox.stub(data, 'createOrUpdateItem').resolves();
// getItemByIdStub = sandbox.stub(data, 'getItemById').resolves();
// await handleConfigurationMessageLegacy(envelope, proto.configurationMessage as SignalService.ConfigurationMessage);
// expect(getItemByIdStub.callCount).to.equal(1);
// });
});
});

View file

@ -15,7 +15,9 @@ import { SWARM_POLLING_TIMEOUT } from '../../../../session/constants';
import { getConversationController } from '../../../../session/conversations';
import { PubKey } from '../../../../session/types';
import { UserUtils } from '../../../../session/utils';
import { ConfigurationSync } from '../../../../session/utils/job_runners/jobs/ConfigurationSyncJob';
import { sleepFor } from '../../../../session/utils/Promise';
import { UserGroupsWrapperActions } from '../../../../webworker/workers/browser/libsession_worker_interface';
import { TestUtils } from '../../../test-utils';
import { generateFakeSnodes, stubData } from '../../../test-utils/utils';
// tslint:disable: chai-vague-errors
@ -41,6 +43,8 @@ describe('SwarmPolling', () => {
beforeEach(async () => {
getConversationController().reset();
TestUtils.stubWindowFeatureFlags();
Sinon.stub(ConfigurationSync, 'queueNewJobIfNeeded').resolves();
// Utils Stubs
Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(ourNumber);
@ -75,6 +79,9 @@ describe('SwarmPolling', () => {
});
describe('getPollingTimeout', () => {
beforeEach(() => {
TestUtils.stubLibSessionWorker(undefined);
});
it('returns INACTIVE for non existing convo', () => {
const fakeConvo = TestUtils.generateFakePubKey();
@ -200,7 +207,7 @@ describe('SwarmPolling', () => {
await swarmPolling.start(true);
expect(pollOnceForKeySpy.callCount).to.eq(1);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]);
});
it('does run for our pubkey even if activeAt is recent ', async () => {
@ -212,27 +219,16 @@ describe('SwarmPolling', () => {
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 () => {
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]]);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]);
});
describe('legacy group', () => {
it('does run for group pubkey on start no matter the recent timestamp ', async () => {
it('does run for group pubkey on start no matter the recent timestamp', async () => {
const convo = getConversationController().getOrCreate(
TestUtils.generateFakePubKeyStr(),
ConversationTypeEnum.GROUP
);
TestUtils.stubLibSessionWorker(undefined);
convo.set('active_at', Date.now());
const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
@ -240,23 +236,7 @@ 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, [0]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
});
it('does run for groupv3 pubkey on start no matter the recent timestamp ', async () => {
const convo = getConversationController().getOrCreate(
TestUtils.generateFakePubKeyStr(),
ConversationTypeEnum.GROUP
);
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).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]);
expect(pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true, [-10]]);
});
@ -265,6 +245,7 @@ describe('SwarmPolling', () => {
TestUtils.generateFakePubKeyStr(),
ConversationTypeEnum.GROUP
);
TestUtils.stubLibSessionWorker(undefined);
getItemByIdStub.restore();
getItemByIdStub = TestUtils.stubData('getItemById');
getItemByIdStub
@ -281,7 +262,7 @@ 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, [0]]);
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');
@ -294,6 +275,7 @@ describe('SwarmPolling', () => {
TestUtils.generateFakePubKeyStr(),
ConversationTypeEnum.GROUP
);
TestUtils.stubLibSessionWorker(undefined);
convo.set('active_at', 1); // really old, but active
const groupConvoPubkey = PubKey.cast(convo.id as string);
@ -306,9 +288,9 @@ describe('SwarmPolling', () => {
await swarmPolling.pollForAllKeys();
expect(pollOnceForKeySpy.callCount).to.eq(3);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
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]]);
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 () => {
@ -317,27 +299,31 @@ describe('SwarmPolling', () => {
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]]);
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();
/** 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 id)
* - 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
*/
await sleepFor(10);
expect(pollOnceForKeySpy.callCount).to.eq(4);
expect(pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false, [0]]);
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]]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.getCall(3).args).to.deep.eq([groupConvoPubkey, true, [-10]]);
});
it('does run twice if activeAt is inactive and we tick longer than 2 minutes', async () => {
@ -345,7 +331,8 @@ describe('SwarmPolling', () => {
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 = PubKey.cast(convo.id as string);
@ -365,9 +352,9 @@ describe('SwarmPolling', () => {
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]]);
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]]);
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]]);
});
@ -377,7 +364,7 @@ describe('SwarmPolling', () => {
ConversationTypeEnum.GROUP
);
pollOnceForKeySpy.resetHistory();
TestUtils.stubLibSessionWorker(undefined);
convo.set('active_at', Date.now());
const groupConvoPubkey = PubKey.cast(convo.id as string);
swarmPolling.addGroupId(groupConvoPubkey);
@ -392,7 +379,7 @@ 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, [-10]]); // this one comes from the swarmPolling.start
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0]]);
expect(pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false, [0, 2, 3, 5, 4]]);
});
describe('multiple runs', () => {
@ -404,6 +391,7 @@ describe('SwarmPolling', () => {
TestUtils.generateFakePubKeyStr(),
ConversationTypeEnum.GROUP
);
TestUtils.stubLibSessionWorker({});
convo.set('active_at', Date.now());
groupConvoPubkey = PubKey.cast(convo.id as string);
@ -411,6 +399,13 @@ describe('SwarmPolling', () => {
await swarmPolling.start(true);
});
afterEach(() => {
Sinon.restore();
getConversationController().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
@ -426,29 +421,33 @@ 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, [0]]);
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]]);
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 () => {
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;
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]]);
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]]);
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]]);
});
});

View file

@ -5,6 +5,7 @@ import { OpenGroupData } from '../../../data/opengroups';
import { ConfigDumpData } from '../../../data/configDump/configDump';
import * as utilWorker from '../../../webworker/workers/browser/util_worker_interface';
import * as libsessionWorker from '../../../webworker/workers/browser/libsession_worker_interface';
const globalAny: any = global;
@ -38,6 +39,11 @@ export function stubUtilWorker(fnName: string, returnedValue: any): sinon.SinonS
.withArgs(fnName as any)
.resolves(returnedValue);
}
export function stubLibSessionWorker(value: any) {
Sinon.stub(libsessionWorker, 'callLibSessionWorker').resolves(value);
}
export function stubCreateObjectUrl() {
// tslint:disable-next-line: no-empty no-function-expression
(global as any).URL = function() {};

View file

@ -7,5 +7,3 @@ export type RenderTextCallbackType = (options: {
}) => JSX.Element;
export type LocalizerType = (key: LocalizerKeys, values?: Array<string>) => string;
export type FixedLengthArray<T, Length extends number> = Array<T> & { length: Length };

View file

@ -314,6 +314,8 @@ export const ConvoInfoVolatileWrapperActions: ConvoInfoVolatileWrapperActionsCal
]) as Promise<ReturnType<ConvoInfoVolatileWrapperActionsCalls['eraseCommunityByFullUrl']>>,
};
const callLibSessionWorker = async (callToMake: LibSessionWorkerFunctions): Promise<unknown> => {
export const callLibSessionWorker = async (
callToMake: LibSessionWorkerFunctions
): Promise<unknown> => {
return internalCallLibSessionWorker(callToMake);
};