You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/ts/test/session/unit/libsession_util/libsession_utils_test.ts

353 lines
12 KiB
TypeScript

import { expect } from 'chai';
import { GroupPubkeyType, PubkeyType } from 'libsession_util_nodejs';
import { randombytes_buf } from 'libsodium-wrappers-sumo';
import Long from 'long';
import Sinon from 'sinon';
import { ConfigDumpData } from '../../../../data/configDump/configDump';
import { GetNetworkTime } from '../../../../session/apis/snode_api/getNetworkTime';
import { SnodeNamespaces } from '../../../../session/apis/snode_api/namespaces';
import { UserUtils } from '../../../../session/utils';
import { LibSessionUtil } from '../../../../session/utils/libsession/libsession_utils';
import {
GenericWrapperActions,
MetaGroupWrapperActions,
} from '../../../../webworker/workers/browser/libsession_worker_interface';
import { TestUtils } from '../../../test-utils';
describe('LibSessionUtil saveDumpsToDb', () => {
describe('for group', () => {
let groupPk: GroupPubkeyType;
beforeEach(() => {
groupPk = TestUtils.generateFakeClosedGroupV2PkStr();
});
afterEach(() => {
Sinon.restore();
});
it('does not save to DB if needsDump reports false', async () => {
Sinon.stub(MetaGroupWrapperActions, 'needsDump').resolves(false);
const metaDump = Sinon.stub(MetaGroupWrapperActions, 'metaDump').resolves(new Uint8Array());
const saveConfigDump = Sinon.stub(ConfigDumpData, 'saveConfigDump').resolves();
await LibSessionUtil.saveDumpsToDb(groupPk);
expect(saveConfigDump.callCount).to.be.equal(0);
expect(metaDump.callCount).to.be.equal(0);
});
it('does save to DB if needsDump reports true', async () => {
Sinon.stub(MetaGroupWrapperActions, 'needsDump').resolves(true);
const dump = [1, 2, 3, 4, 5];
const metaDump = Sinon.stub(MetaGroupWrapperActions, 'metaDump').resolves(
new Uint8Array(dump)
);
const saveConfigDump = Sinon.stub(ConfigDumpData, 'saveConfigDump').resolves();
await LibSessionUtil.saveDumpsToDb(groupPk);
expect(saveConfigDump.callCount).to.be.equal(1);
expect(metaDump.callCount).to.be.equal(1);
expect(metaDump.firstCall.args).to.be.deep.eq([groupPk]);
expect(saveConfigDump.firstCall.args).to.be.deep.eq([
{
publicKey: groupPk,
variant: `MetaGroupConfig-${groupPk}`,
data: new Uint8Array(dump),
},
]);
});
});
describe('for user', () => {
let userDetails: TestUtils.TestUserKeyPairs;
let sessionId: PubkeyType;
beforeEach(async () => {
userDetails = await TestUtils.generateUserKeyPairs();
sessionId = userDetails.x25519KeyPair.pubkeyHex;
});
afterEach(() => {
Sinon.restore();
});
it('does not save to DB if all needsDump reports false', async () => {
Sinon.stub(GenericWrapperActions, 'needsDump').resolves(false);
const dump = Sinon.stub(GenericWrapperActions, 'dump').resolves(new Uint8Array());
const saveConfigDump = Sinon.stub(ConfigDumpData, 'saveConfigDump').resolves();
Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(sessionId);
await LibSessionUtil.saveDumpsToDb(sessionId);
expect(saveConfigDump.callCount).to.be.equal(0);
expect(dump.callCount).to.be.equal(0);
});
it('does save to DB if any needsDump reports true', async () => {
Sinon.stub(GenericWrapperActions, 'needsDump')
.resolves(false)
.withArgs('ConvoInfoVolatileConfig')
.resolves(true);
const dump = Sinon.stub(GenericWrapperActions, 'dump').resolves(new Uint8Array());
const saveConfigDump = Sinon.stub(ConfigDumpData, 'saveConfigDump').resolves();
Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(sessionId);
await LibSessionUtil.saveDumpsToDb(sessionId);
expect(saveConfigDump.callCount).to.be.equal(1);
expect(dump.callCount).to.be.equal(1);
});
it('does save to DB if all needsDump reports true', async () => {
const needsDump = Sinon.stub(GenericWrapperActions, 'needsDump').resolves(true);
const dumped = new Uint8Array([1, 2, 3]);
const dump = Sinon.stub(GenericWrapperActions, 'dump').resolves(dumped);
const saveConfigDump = Sinon.stub(ConfigDumpData, 'saveConfigDump').resolves();
Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(sessionId);
await LibSessionUtil.saveDumpsToDb(userDetails.x25519KeyPair.pubkeyHex);
expect(needsDump.callCount).to.be.equal(4);
expect(dump.callCount).to.be.equal(4);
expect(needsDump.getCalls().map(call => call.args)).to.be.deep.eq([
['UserConfig'],
['ContactsConfig'],
['UserGroupsConfig'],
['ConvoInfoVolatileConfig'],
]);
expect(saveConfigDump.callCount).to.be.equal(4);
expect(saveConfigDump.getCalls().map(call => call.args)).to.be.deep.eq([
[{ variant: 'UserConfig', publicKey: sessionId, data: dumped }],
[{ variant: 'ContactsConfig', publicKey: sessionId, data: dumped }],
[{ variant: 'UserGroupsConfig', publicKey: sessionId, data: dumped }],
[{ variant: 'ConvoInfoVolatileConfig', publicKey: sessionId, data: dumped }],
]);
expect(dump.getCalls().map(call => call.args)).to.be.deep.eq([
['UserConfig'],
['ContactsConfig'],
['UserGroupsConfig'],
['ConvoInfoVolatileConfig'],
]);
});
});
});
describe('LibSessionUtil pendingChangesForGroup', () => {
let groupPk: GroupPubkeyType;
beforeEach(() => {
groupPk = TestUtils.generateFakeClosedGroupV2PkStr();
});
afterEach(() => {
Sinon.restore();
});
it('empty results if needsPush is false', async () => {
Sinon.stub(MetaGroupWrapperActions, 'needsPush').resolves(false);
const result = await LibSessionUtil.pendingChangesForGroup(groupPk);
expect(result.allOldHashes.size).to.be.equal(0);
expect(result.messages.length).to.be.equal(0);
});
it('valid results if needsPush is true', async () => {
const pushResults = {
groupKeys: { data: new Uint8Array([3, 2, 1]), namespace: 13 },
groupInfo: {
seqno: 1,
data: new Uint8Array([1, 2, 3]),
hashes: ['123', '333'],
namespace: 12,
},
groupMember: {
seqno: 2,
data: new Uint8Array([1, 2]),
hashes: ['321', '111'],
namespace: 14,
},
};
Sinon.stub(MetaGroupWrapperActions, 'needsPush').resolves(true);
Sinon.stub(MetaGroupWrapperActions, 'push').resolves(pushResults);
Sinon.stub(GetNetworkTime, 'now').returns(1234);
const result = await LibSessionUtil.pendingChangesForGroup(groupPk);
expect(result.allOldHashes.size).to.be.equal(4);
// check that all of the hashes are there
expect([...result.allOldHashes]).to.have.members([
...pushResults.groupInfo.hashes,
...pushResults.groupMember.hashes,
]);
expect(result.messages.length).to.be.equal(3);
// check for the keys push content
expect(result.messages[0]).to.be.deep.eq({
type: 'GroupKeys',
ciphertext: new Uint8Array([3, 2, 1]),
namespace: 13,
});
// check for the info push content
expect(result.messages[1]).to.be.deep.eq({
type: 'GroupInfo',
ciphertext: new Uint8Array([1, 2, 3]),
namespace: 12,
seqno: Long.fromInt(pushResults.groupInfo.seqno),
});
// check for the members pusu content
expect(result.messages[2]).to.be.deep.eq({
type: 'GroupMember',
ciphertext: new Uint8Array([1, 2]),
namespace: 14,
seqno: Long.fromInt(pushResults.groupMember.seqno),
});
});
it('skips entry results if needsPush one of the wrapper has no changes', async () => {
const pushResults = {
groupInfo: {
seqno: 1,
data: new Uint8Array([1, 2, 3]),
hashes: ['123', '333'],
namespace: 12,
},
groupMember: null,
groupKeys: { data: new Uint8Array([3, 2, 1]), namespace: 13 },
};
Sinon.stub(MetaGroupWrapperActions, 'needsPush').resolves(true);
Sinon.stub(MetaGroupWrapperActions, 'push').resolves(pushResults);
const result = await LibSessionUtil.pendingChangesForGroup(groupPk);
expect(result.allOldHashes.size).to.be.equal(2);
expect(result.messages.length).to.be.equal(2);
});
});
describe('LibSessionUtil pendingChangesForUs', () => {
beforeEach(async () => {});
afterEach(() => {
Sinon.restore();
});
it('empty results if all needsPush is false', async () => {
Sinon.stub(GenericWrapperActions, 'needsPush').resolves(false);
const result = await LibSessionUtil.pendingChangesForUs();
expect(result.allOldHashes.size).to.be.equal(0);
expect(result.messages.length).to.be.equal(0);
});
it('valid results if ConvoVolatile needsPush only is true', async () => {
// this is what would be supposedly returned by libsession
const pushResultsConvo = {
data: randombytes_buf(300),
seqno: 123,
hashes: ['123'],
namespace: SnodeNamespaces.ConvoInfoVolatile,
};
const needsPush = Sinon.stub(GenericWrapperActions, 'needsPush');
needsPush.resolves(false).withArgs('ConvoInfoVolatileConfig').resolves(true);
const push = Sinon.stub(GenericWrapperActions, 'push')
.throws()
.withArgs('ConvoInfoVolatileConfig')
.resolves(pushResultsConvo);
Sinon.stub(GetNetworkTime, 'now').returns(1234);
const result = await LibSessionUtil.pendingChangesForUs();
expect(needsPush.callCount).to.be.eq(4);
expect(needsPush.getCalls().map(m => m.args)).to.be.deep.eq([
['UserConfig'],
['ContactsConfig'],
['UserGroupsConfig'],
['ConvoInfoVolatileConfig'],
]);
expect(push.callCount).to.be.eq(1);
expect(push.getCalls().map(m => m.args)).to.be.deep.eq([['ConvoInfoVolatileConfig']]);
// check that all of the hashes are there
expect(result.allOldHashes.size).to.be.equal(1);
expect([...result.allOldHashes]).to.have.members([...pushResultsConvo.hashes]);
// check for the messages to push are what we expect
expect(result.messages).to.be.deep.eq([
{
ciphertext: pushResultsConvo.data,
namespace: pushResultsConvo.namespace,
seqno: Long.fromNumber(pushResultsConvo.seqno),
},
]);
});
it('valid results if all wrappers needsPush only are true', async () => {
// this is what would be supposedly returned by libsession
const pushConvo = {
data: randombytes_buf(300),
seqno: 123,
hashes: ['123'],
namespace: SnodeNamespaces.ConvoInfoVolatile,
};
const pushContacts = {
data: randombytes_buf(300),
seqno: 321,
hashes: ['321', '4444'],
namespace: SnodeNamespaces.UserContacts,
};
const pushGroups = {
data: randombytes_buf(300),
seqno: 222,
hashes: ['222', '5555'],
namespace: SnodeNamespaces.UserGroups,
};
const pushUser = {
data: randombytes_buf(300),
seqno: 111,
hashes: ['111'],
namespace: SnodeNamespaces.UserProfile,
};
const needsPush = Sinon.stub(GenericWrapperActions, 'needsPush');
needsPush.resolves(true);
const push = Sinon.stub(GenericWrapperActions, 'push');
push
.throws()
.withArgs('ContactsConfig')
.resolves(pushContacts)
.withArgs('UserConfig')
.resolves(pushUser)
.withArgs('UserGroupsConfig')
.resolves(pushGroups)
.withArgs('ConvoInfoVolatileConfig')
.resolves(pushConvo);
Sinon.stub(GetNetworkTime, 'now').returns(1234);
const result = await LibSessionUtil.pendingChangesForUs();
expect(needsPush.callCount).to.be.eq(4);
expect(needsPush.getCalls().map(m => m.args)).to.be.deep.eq([
['UserConfig'],
['ContactsConfig'],
['UserGroupsConfig'],
['ConvoInfoVolatileConfig'],
]);
expect(push.callCount).to.be.eq(4);
expect(push.getCalls().map(m => m.args)).to.be.deep.eq([
['UserConfig'],
['ContactsConfig'],
['UserGroupsConfig'],
['ConvoInfoVolatileConfig'],
]);
// check that all of the hashes are there
expect(result.allOldHashes.size).to.be.equal(6);
expect([...result.allOldHashes]).to.have.members([
...pushContacts.hashes,
...pushConvo.hashes,
...pushGroups.hashes,
...pushUser.hashes,
]);
// check for the messages to push are what we expect
expect(result.messages).to.be.deep.eq(
[pushUser, pushContacts, pushGroups, pushConvo].map(m => ({
ciphertext: m.data,
namespace: m.namespace,
seqno: Long.fromNumber(m.seqno),
}))
);
});
});