From 4b93c5779a03118c566076beaf222081ef270350 Mon Sep 17 00:00:00 2001 From: William Grant Date: Wed, 11 Oct 2023 17:38:22 +1100 Subject: [PATCH] test: started libsession LegacyGroups tests specifically insertGroupsFromDBIntoWrapperAndRefresh --- .../libsession_utils_user_groups.ts | 22 +- .../libsession_wrapper_user_groups_test.ts | 262 ++++++++++++++++++ .../libsession_wrapper_usergroups_test.ts | 150 ---------- ts/types/sqlSharedTypes.ts | 13 +- 4 files changed, 288 insertions(+), 159 deletions(-) create mode 100644 ts/test/session/unit/libsession_wrapper/libsession_wrapper_user_groups_test.ts delete mode 100644 ts/test/session/unit/libsession_wrapper/libsession_wrapper_usergroups_test.ts diff --git a/ts/session/utils/libsession/libsession_utils_user_groups.ts b/ts/session/utils/libsession/libsession_utils_user_groups.ts index 3da54ee77..b33a5bde0 100644 --- a/ts/session/utils/libsession/libsession_utils_user_groups.ts +++ b/ts/session/utils/libsession/libsession_utils_user_groups.ts @@ -1,9 +1,10 @@ /* eslint-disable no-case-declarations */ -import { CommunityInfo, UserGroupsType } from 'libsession_util_nodejs'; +import { CommunityInfo, LegacyGroupInfo, UserGroupsType } from 'libsession_util_nodejs'; import { Data } from '../../../data/data'; import { OpenGroupData } from '../../../data/opengroups'; import { ConversationModel } from '../../../models/conversation'; import { + CommunityInfoFromDBValues, assertUnreachable, getCommunityInfoFromDBValues, getLegacyGroupInfoFromDBValues, @@ -61,14 +62,16 @@ function isLegacyGroupToRemoveFromDBIfNotInWrapper(convo: ConversationModel): bo * If that community does not exist in the wrapper, it is created before being updated. * Same applies for a legacy group. */ -async function insertGroupsFromDBIntoWrapperAndRefresh(convoId: string): Promise { +async function insertGroupsFromDBIntoWrapperAndRefresh( + convoId: string +): Promise { const foundConvo = getConversationController().get(convoId); if (!foundConvo) { - return; + return null; } if (!isUserGroupToStoreInWrapper(foundConvo)) { - return; + return null; } const convoType: UserGroupsType = isCommunityToStoreInWrapper(foundConvo) @@ -81,7 +84,7 @@ async function insertGroupsFromDBIntoWrapperAndRefresh(convoId: string): Promise const roomDetails = OpenGroupData.getV2OpenGroupRoomByRoomId(asOpengroup); if (!roomDetails) { - return; + return null; } // we need to build the full URL with the pubkey so we can add it to the wrapper. Let's reuse the exposed method from the wrapper for that @@ -103,6 +106,12 @@ async function insertGroupsFromDBIntoWrapperAndRefresh(convoId: string): Promise wrapperComm.fullUrl, wrapperComm.priority ); + + // returned testing purposes only + return { + fullUrl: wrapperComm.fullUrl, + priority: wrapperComm.priority, + }; } catch (e) { window.log.warn(`UserGroupsWrapperActions.set of ${convoId} failed with ${e.message}`); // we still let this go through @@ -131,6 +140,8 @@ async function insertGroupsFromDBIntoWrapperAndRefresh(convoId: string): Promise ); // this does the create or the update of the matching existing legacy group await UserGroupsWrapperActions.setLegacyGroup(wrapperLegacyGroup); + // returned testing purposes only + return wrapperLegacyGroup; } catch (e) { window.log.warn(`UserGroupsWrapperActions.set of ${convoId} failed with ${e.message}`); // we still let this go through @@ -143,6 +154,7 @@ async function insertGroupsFromDBIntoWrapperAndRefresh(convoId: string): Promise `insertGroupsFromDBIntoWrapperAndRefresh case not handeld "${convoType}"` ); } + return null; } async function getCommunityByConvoIdNotCached(convoId: string) { diff --git a/ts/test/session/unit/libsession_wrapper/libsession_wrapper_user_groups_test.ts b/ts/test/session/unit/libsession_wrapper/libsession_wrapper_user_groups_test.ts new file mode 100644 index 000000000..df17d6ac5 --- /dev/null +++ b/ts/test/session/unit/libsession_wrapper/libsession_wrapper_user_groups_test.ts @@ -0,0 +1,262 @@ +import { expect } from 'chai'; + +import { LegacyGroupInfo } from 'libsession_util_nodejs'; +import { describe } from 'mocha'; +import Sinon from 'sinon'; +import { Data } from '../../../../data/data'; +import { ConversationModel } from '../../../../models/conversation'; +import { + CONVERSATION_PRIORITIES, + ConversationTypeEnum, +} from '../../../../models/conversationAttributes'; +import { GetNetworkTime } from '../../../../session/apis/snode_api/getNetworkTime'; +import { getConversationController } from '../../../../session/conversations'; +import { UserUtils } from '../../../../session/utils'; +import { SessionUtilUserGroups } from '../../../../session/utils/libsession/libsession_utils_user_groups'; +import { TestUtils } from '../../../test-utils'; +import { generateFakeECKeyPair, stubWindowLog } from '../../../test-utils/utils'; + +describe('libsession_user_groups', () => { + stubWindowLog(); + + const getLatestTimestampOffset = 200000; + const ourNumber = '051234567890acbdef'; + const groupECKeyPair = generateFakeECKeyPair(); + const communityUrl = 'http://example.org/roomId1234'; + const validArgs = { + type: ConversationTypeEnum.GROUP, + active_at: 1234, + }; + + beforeEach(() => { + Sinon.stub(GetNetworkTime, 'getLatestTimestampOffset').returns(getLatestTimestampOffset); + Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(ourNumber); + TestUtils.stubLibSessionWorker(undefined); + }); + + afterEach(() => { + Sinon.restore(); + }); + + describe('isUserGroupToStoreInWrapper', () => { + describe('communities', () => { + const communityArgs = { + id: communityUrl, + }; + it('includes public group/community', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ ...validArgs, ...communityArgs } as any) + ) + ).to.be.eq(true); + }); + + it('excludes public group/community inactive', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ ...validArgs, ...communityArgs, active_at: undefined } as any) + ) + ).to.be.eq(false); + }); + }); + + describe('legacy closed groups', () => { + const validLegacyGroupArgs = { + ...validArgs, + type: ConversationTypeEnum.GROUP, + id: '05123456564', + } as any; + + it('includes legacy group', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ + ...validLegacyGroupArgs, + }) + ) + ).to.be.eq(true); + }); + + it('exclude legacy group left', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ + ...validLegacyGroupArgs, + left: true, + }) + ) + ).to.be.eq(false); + }); + it('exclude legacy group kicked', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ + ...validLegacyGroupArgs, + isKickedFromGroup: true, + }) + ) + ).to.be.eq(false); + }); + + it('exclude legacy group not active', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ + ...validLegacyGroupArgs, + active_at: undefined, + }) + ) + ).to.be.eq(false); + }); + + it('include hidden legacy group', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ + ...validLegacyGroupArgs, + priority: CONVERSATION_PRIORITIES.hidden, + }) + ) + ).to.be.eq(true); + }); + }); + + it('excludes closed group v3 (for now)', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ + ...validArgs, + type: ConversationTypeEnum.GROUPV3, + id: '03123456564', + } as any) + ) + ).to.be.eq(false); + }); + + it('excludes empty id', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ + ...validArgs, + id: '', + } as any) + ) + ).to.be.eq(false); + + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ + ...validArgs, + id: '9871', + } as any) + ) + ).to.be.eq(false); + }); + + it('excludes private', () => { + expect( + SessionUtilUserGroups.isUserGroupToStoreInWrapper( + new ConversationModel({ + ...validArgs, + id: '0511111', + type: ConversationTypeEnum.PRIVATE, + } as any) + ) + ).to.be.eq(false); + }); + }); + + describe('LegacyGroups', () => { + describe('insertGroupsFromDBIntoWrapperAndRefresh', () => { + const groupArgs = { + id: groupECKeyPair.publicKeyData.toString(), + displayNameInProfile: 'Test Group', + expirationMode: 'off', + expireTimer: 0, + }; + + Sinon.stub(Data, 'getLatestClosedGroupEncryptionKeyPair').resolves( + groupECKeyPair.toHexKeyPair() + ); + + it('returns wrapper values that match with the inputted group', async () => { + const group = new ConversationModel({ + ...validArgs, + ...groupArgs, + } as any); + Sinon.stub(getConversationController(), 'get').returns(group); + // Sinon.stub(SessionUtilUserGroups, 'isLegacyGroupToStoreInWrapper').returns(true); + + const wrapperGroup = await SessionUtilUserGroups.insertGroupsFromDBIntoWrapperAndRefresh( + group.get('id') + ); + + expect(wrapperGroup, 'something should be returned from the wrapper').to.not.be.null; + if (!wrapperGroup) { + throw Error('something should be returned from the wrapper'); + } + + expect( + (wrapperGroup as LegacyGroupInfo).pubkeyHex, + 'pubkeyHex in the wrapper should match the inputted group' + ).to.equal(group.id); + expect( + (wrapperGroup as LegacyGroupInfo).name, + 'name in the wrapper should match the inputted group' + ).to.equal(group.get('displayNameInProfile')); + expect( + (wrapperGroup as LegacyGroupInfo).priority, + 'priority in the wrapper should match the inputted group' + ).to.equal(group.get('priority')); + expect( + (wrapperGroup as LegacyGroupInfo).members, + 'members in the wrapper should match the inputted group' + ).to.equal(group.get('members')); + expect( + (wrapperGroup as LegacyGroupInfo).disappearingTimerSeconds, + 'disappearingTimerSeconds in the wrapper should match the inputted group' + ).to.equal(group.get('expireTimer')); + expect( + (wrapperGroup as LegacyGroupInfo).encPubkey, + 'encPubkey in the wrapper should match the inputted group' + ).to.equal(groupECKeyPair.toHexKeyPair().publicHex); + expect( + (wrapperGroup as LegacyGroupInfo).encSeckey, + 'encSeckey in the wrapper should match the inputted group' + ).to.equal(groupECKeyPair.toHexKeyPair().privateHex); + expect( + (wrapperGroup as LegacyGroupInfo).joinedAtSeconds, + 'joinedAtSeconds in the wrapper should match the inputted group' + ).to.equal(group.get('lastJoinedTimestamp')); + }); + // it('if disappearing messages is on then the wrapper returned values should match the inputted group', async () => { + // const contact = new ConversationModel({ + // ...validArgs, + // ...groupArgs, + // expirationMode: 'deleteAfterSend', + // expireTimer: 300, + // } as any); + // Sinon.stub(getConversationController(), 'get').returns(contact); + // Sinon.stub(SessionUtilContact, 'isContactToStoreInWrapper').returns(true); + + // const wrapperContact = await SessionUtilContact.insertContactFromDBIntoWrapperAndRefresh( + // contact.get('id') + // ); + + // expect(wrapperContact, 'something should be returned from the wrapper').to.not.be.null; + // if (!wrapperContact) { + // throw Error('something should be returned from the wrapper'); + // } + + // expect( + // wrapperContact.expirationMode, + // 'expirationMode in the wrapper should match the inputted group' + // ).to.equal(contact.get('expirationMode')); + // expect( + // wrapperContact.expirationTimerSeconds, + // 'expirationTimerSeconds in the wrapper should match the inputted group' + // ).to.equal(contact.get('expireTimer')); + // }); + }); + }); +}); diff --git a/ts/test/session/unit/libsession_wrapper/libsession_wrapper_usergroups_test.ts b/ts/test/session/unit/libsession_wrapper/libsession_wrapper_usergroups_test.ts deleted file mode 100644 index 9eb29164a..000000000 --- a/ts/test/session/unit/libsession_wrapper/libsession_wrapper_usergroups_test.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { expect } from 'chai'; - -import Sinon from 'sinon'; -import { ConversationModel } from '../../../../models/conversation'; -import { - CONVERSATION_PRIORITIES, - ConversationTypeEnum, -} from '../../../../models/conversationAttributes'; -import { UserUtils } from '../../../../session/utils'; -import { SessionUtilUserGroups } from '../../../../session/utils/libsession/libsession_utils_user_groups'; - -describe('libsession_groups', () => { - describe('filter user groups for wrapper', () => { - const ourNumber = '051234567890acbdef'; - const validArgs = { - id: 'http://example.org/roomId1234', - type: ConversationTypeEnum.GROUP, - active_at: 1234, - }; - beforeEach(() => { - Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(ourNumber); - }); - afterEach(() => { - Sinon.restore(); - }); - - describe('communities', () => { - it('includes public group/community', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ ...validArgs } as any) - ) - ).to.be.eq(true); - }); - - it('excludes public group/community inactive', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ ...validArgs, active_at: undefined } as any) - ) - ).to.be.eq(false); - }); - }); - - describe('legacy closed groups', () => { - const validLegacyGroupArgs = { - ...validArgs, - type: ConversationTypeEnum.GROUP, - id: '05123456564', - } as any; - - it('includes legacy group', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ - ...validLegacyGroupArgs, - }) - ) - ).to.be.eq(true); - }); - - it('exclude legacy group left', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ - ...validLegacyGroupArgs, - left: true, - }) - ) - ).to.be.eq(false); - }); - it('exclude legacy group kicked', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ - ...validLegacyGroupArgs, - isKickedFromGroup: true, - }) - ) - ).to.be.eq(false); - }); - - it('exclude legacy group not active', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ - ...validLegacyGroupArgs, - active_at: undefined, - }) - ) - ).to.be.eq(false); - }); - - it('include hidden legacy group', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ - ...validLegacyGroupArgs, - priority: CONVERSATION_PRIORITIES.hidden, - }) - ) - ).to.be.eq(true); - }); - }); - - it('excludes closed group v3 (for now)', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ - ...validArgs, - type: ConversationTypeEnum.GROUPV3, - id: '03123456564', - } as any) - ) - ).to.be.eq(false); - }); - - it('excludes empty id', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ - ...validArgs, - id: '', - } as any) - ) - ).to.be.eq(false); - - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ - ...validArgs, - id: '9871', - } as any) - ) - ).to.be.eq(false); - }); - - it('excludes private', () => { - expect( - SessionUtilUserGroups.isUserGroupToStoreInWrapper( - new ConversationModel({ - ...validArgs, - id: '0511111', - type: ConversationTypeEnum.PRIVATE, - } as any) - ) - ).to.be.eq(false); - }); - }); -}); diff --git a/ts/types/sqlSharedTypes.ts b/ts/types/sqlSharedTypes.ts index 28379f378..1e40c3862 100644 --- a/ts/types/sqlSharedTypes.ts +++ b/ts/types/sqlSharedTypes.ts @@ -169,6 +169,11 @@ export function getContactInfoFromDBValues({ return wrapperContact; } +export type CommunityInfoFromDBValues = { + priority: number; + fullUrl: string; +}; + /** * NOTE This code should always match the last known version of the same function used in a libsession migration (V31) * @@ -181,7 +186,7 @@ export function getCommunityInfoFromDBValues({ }: { priority: number; fullUrl: string; -}) { +}): CommunityInfoFromDBValues { const community = { fullUrl, priority: priority || 0, @@ -244,6 +249,9 @@ export function getLegacyGroupInfoFromDBValues({ const legacyGroup: LegacyGroupInfo = { pubkeyHex: id, + name: displayNameInProfile || '', + priority: priority || 0, + members: wrappedMembers, disappearingTimerSeconds: expirationMode && (expirationMode as DisappearingMessageConversationModeType) !== 'off' && @@ -251,9 +259,6 @@ export function getLegacyGroupInfoFromDBValues({ expireTimer > 0 ? expireTimer : 0, - name: displayNameInProfile || '', - priority: priority || 0, - members: wrappedMembers, encPubkey: !isEmpty(encPubkeyHex) ? from_hex(encPubkeyHex) : new Uint8Array(), encSeckey: !isEmpty(encSeckeyHex) ? from_hex(encSeckeyHex) : new Uint8Array(), joinedAtSeconds: Math.floor(lastJoinedTimestamp / 1000),