mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
Merge branch 'clearnet' of https://github.com/loki-project/session-desktop into utils-tests
This commit is contained in:
commit
e2e867d5c1
|
@ -1640,6 +1640,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
libsession.Protocols.SessionProtocol.checkSessionRequestExpiry().catch(
|
||||
e => {
|
||||
window.log.error(
|
||||
'Error occured which checking for session request expiry',
|
||||
e
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
storage.onready(async () => {
|
||||
idleDetector.start();
|
||||
});
|
||||
|
|
2
js/modules/data.d.ts
vendored
2
js/modules/data.d.ts
vendored
|
@ -1,5 +1,5 @@
|
|||
import { ConversationType } from '../../ts/state/ducks/conversations';
|
||||
import { Mesasge } from '../../ts/types/Message';
|
||||
import { Message } from '../../ts/types/Message';
|
||||
|
||||
export type IdentityKey = {
|
||||
id: string;
|
||||
|
|
|
@ -17,6 +17,7 @@ interface SessionRequestParams extends MessageParams {
|
|||
}
|
||||
|
||||
export class SessionRequestMessage extends ContentMessage {
|
||||
public static readonly ttl = 4 * 24 * 60 * 60 * 1000; // 4 days
|
||||
private readonly preKeyBundle: PreKeyBundleType;
|
||||
|
||||
constructor(params: SessionRequestParams) {
|
||||
|
@ -25,7 +26,7 @@ export class SessionRequestMessage extends ContentMessage {
|
|||
}
|
||||
|
||||
public ttl(): number {
|
||||
return 4 * 24 * 60 * 60 * 1000; // 4 days
|
||||
return SessionRequestMessage.ttl;
|
||||
}
|
||||
|
||||
protected getPreKeyBundleMessage(): SignalService.PreKeyBundleMessage {
|
||||
|
|
|
@ -12,7 +12,7 @@ interface StringToNumberMap {
|
|||
export class SessionProtocol {
|
||||
private static dbLoaded: Boolean = false;
|
||||
/**
|
||||
* This map olds the sent session timestamps, i.e. session requests message effectively sent to the recipient.
|
||||
* This map holds the sent session timestamps, i.e. session requests message effectively sent to the recipient.
|
||||
* It is backed by a database entry so it's loaded from db on startup.
|
||||
* This map should not be used directly, but instead through
|
||||
* `updateSendSessionTimestamp()`, or `hasSendSessionRequest()`
|
||||
|
@ -73,6 +73,29 @@ export class SessionProtocol {
|
|||
return pendingSend || hasSent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if any outgoing session requests have expired and re-sends them again if they have.
|
||||
*/
|
||||
public static async checkSessionRequestExpiry(): Promise<any> {
|
||||
await this.fetchFromDBIfNeeded();
|
||||
|
||||
const now = Date.now();
|
||||
const sentTimestamps = Object.entries(this.sentSessionsTimestamp);
|
||||
const promises = sentTimestamps.map(async ([device, sent]) => {
|
||||
const expireTime = sent + SessionRequestMessage.ttl;
|
||||
// Check if we need to send a session request
|
||||
if (now < expireTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Unset the timestamp, so that if it fails to send in this function, it will be guaranteed to send later on.
|
||||
await this.updateSentSessionTimestamp(device, undefined);
|
||||
await this.sendSessionRequestIfNeeded(new PubKey(device));
|
||||
});
|
||||
|
||||
return Promise.all(promises) as Promise<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a SessionRequestMessage to be sent if:
|
||||
* - we do not already have a session and
|
||||
|
|
|
@ -11,7 +11,7 @@ import { PubKey } from '../../../session/types';
|
|||
// tslint:disable-next-line: max-func-body-length
|
||||
describe('SessionProtocol', () => {
|
||||
const sandbox = sinon.createSandbox();
|
||||
const ourNumber = 'ourNumber';
|
||||
const ourNumber = TestUtils.generateFakePubKey();
|
||||
const pubkey = TestUtils.generateFakePubKey();
|
||||
let getItemById: sinon.SinonStub;
|
||||
let send: sinon.SinonStub;
|
||||
|
@ -51,7 +51,7 @@ describe('SessionProtocol', () => {
|
|||
|
||||
getItemById = TestUtils.stubData('getItemById').resolves({ value: {} });
|
||||
|
||||
sandbox.stub(UserUtil, 'getCurrentDevicePubKey').resolves(ourNumber);
|
||||
sandbox.stub(UserUtil, 'getCurrentDevicePubKey').resolves(ourNumber.key);
|
||||
send = sandbox.stub(MessageSender, 'send' as any);
|
||||
SessionProtocol.reset();
|
||||
});
|
||||
|
@ -99,6 +99,80 @@ describe('SessionProtocol', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('checkSessionRequestExpiry', () => {
|
||||
let clock: sinon.SinonFakeTimers;
|
||||
let now: number;
|
||||
let sendSessionRequestStub: sinon.SinonStub<
|
||||
[SessionRequestMessage, PubKey],
|
||||
Promise<void>
|
||||
>;
|
||||
beforeEach(() => {
|
||||
now = Date.now();
|
||||
clock = sandbox.useFakeTimers(now);
|
||||
|
||||
sendSessionRequestStub = sandbox
|
||||
.stub(SessionProtocol, 'sendSessionRequest')
|
||||
.resolves();
|
||||
});
|
||||
|
||||
it('should not send a session request if none have expired', async () => {
|
||||
getItemById.withArgs('sentSessionsTimestamp').resolves({
|
||||
id: 'sentSessionsTimestamp',
|
||||
value: {
|
||||
[pubkey.key]: now,
|
||||
},
|
||||
});
|
||||
|
||||
// Set the time just before expiry
|
||||
clock.tick(SessionRequestMessage.ttl - 100);
|
||||
|
||||
await SessionProtocol.checkSessionRequestExpiry();
|
||||
expect(getItemById.calledWith('sentSessionsTimestamp'));
|
||||
expect(sendSessionRequestStub.callCount).to.equal(0);
|
||||
});
|
||||
|
||||
it('should send a session request if expired', async () => {
|
||||
getItemById.withArgs('sentSessionsTimestamp').resolves({
|
||||
id: 'sentSessionsTimestamp',
|
||||
value: {
|
||||
[pubkey.key]: now,
|
||||
},
|
||||
});
|
||||
|
||||
// Expire the request
|
||||
clock.tick(SessionRequestMessage.ttl + 100);
|
||||
|
||||
await SessionProtocol.checkSessionRequestExpiry();
|
||||
expect(getItemById.calledWith('sentSessionsTimestamp'));
|
||||
expect(sendSessionRequestStub.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('should remove the old sent timestamp when expired', async () => {
|
||||
getItemById.withArgs('sentSessionsTimestamp').resolves({
|
||||
id: 'sentSessionsTimestamp',
|
||||
value: {
|
||||
[pubkey.key]: now,
|
||||
},
|
||||
});
|
||||
|
||||
// Remove this call from the equation
|
||||
sandbox.stub(SessionProtocol, 'sendSessionRequestIfNeeded').resolves();
|
||||
|
||||
// Expire the request
|
||||
clock.tick(SessionRequestMessage.ttl + 100);
|
||||
|
||||
await SessionProtocol.checkSessionRequestExpiry();
|
||||
expect(getItemById.calledWith('sentSessionsTimestamp'));
|
||||
expect(await SessionProtocol.hasSentSessionRequest(pubkey)).to.equal(
|
||||
false,
|
||||
'hasSentSessionRequest should return false.'
|
||||
);
|
||||
expect(SessionProtocol.getSentSessionsTimestamp()).to.not.have.property(
|
||||
pubkey.key
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSessionEstablished', () => {
|
||||
beforeEach(async () => {
|
||||
// add an existing entry in the sentMap
|
||||
|
|
|
@ -9,12 +9,23 @@ chai.use(chaiAsPromised);
|
|||
|
||||
const { expect } = chai;
|
||||
|
||||
|
||||
describe('Promise Utils', () => {
|
||||
const sandbox = sinon.createSandbox();
|
||||
let pollSpy: sinon.SinonSpy<[(done: (arg: any) => void) => Promise<void> | void, (Partial<PromiseUtils.PollOptions> | undefined)?], Promise<void>>;
|
||||
let waitForTaskSpy: sinon.SinonSpy<[(done: (arg: any) => void) => Promise<void> | void, (number | undefined)?], Promise<unknown>>;
|
||||
let waitUntilSpy: sinon.SinonSpy<[() => Promise<boolean> | boolean, (number | undefined)?], Promise<void>>;
|
||||
let pollSpy: sinon.SinonSpy<
|
||||
[
|
||||
(done: (arg: any) => void) => Promise<void> | void,
|
||||
(Partial<PromiseUtils.PollOptions> | undefined)?
|
||||
],
|
||||
Promise<void>
|
||||
>;
|
||||
let waitForTaskSpy: sinon.SinonSpy<
|
||||
[(done: (arg: any) => void) => Promise<void> | void, (number | undefined)?],
|
||||
Promise<unknown>
|
||||
>;
|
||||
let waitUntilSpy: sinon.SinonSpy<
|
||||
[() => Promise<boolean> | boolean, (number | undefined)?],
|
||||
Promise<void>
|
||||
>;
|
||||
|
||||
beforeEach(() => {
|
||||
pollSpy = sandbox.spy(PromiseUtils, 'poll');
|
||||
|
|
|
@ -51,10 +51,10 @@ describe('Sync Message Utils', () => {
|
|||
const numConversations = 20;
|
||||
const primaryConversations = new Array(numConversations / 2)
|
||||
.fill({})
|
||||
.map(() => new TestUtils.MockPrivateConversation({ isPrimary: true }));
|
||||
.map(() => new TestUtils.MockConversation({ type: 'primary' }));
|
||||
const secondaryConversations = new Array(numConversations / 2)
|
||||
.fill({})
|
||||
.map(() => new TestUtils.MockPrivateConversation({ isPrimary: false }));
|
||||
.map(() => new TestUtils.MockConversation({ type: 'secondary' }));
|
||||
const conversations = [...primaryConversations, ...secondaryConversations];
|
||||
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
"use strict";
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||
result["default"] = mod;
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const sinon = __importStar(require("sinon"));
|
||||
const crypto = __importStar(require("crypto"));
|
||||
const uuid_1 = require("uuid");
|
||||
const types_1 = require("../../../ts/session/types");
|
||||
const outgoing_1 = require("../../session/messages/outgoing");
|
||||
const _1 = require(".");
|
||||
const globalAny = global;
|
||||
const sandbox = sinon.createSandbox();
|
||||
// We have to do this in a weird way because Data uses module.exports
|
||||
// which doesn't play well with sinon or ImportMock
|
||||
// tslint:disable-next-line: no-require-imports no-var-requires
|
||||
const Data = require('../../../js/modules/data');
|
||||
/**
|
||||
* Stub a function inside Data.
|
||||
*
|
||||
* Note: This uses a custom sandbox.
|
||||
* Please call `restoreStubs()` or `stub.restore()` to restore original functionality.
|
||||
*/
|
||||
function stubData(fn) {
|
||||
return sandbox.stub(Data, fn);
|
||||
}
|
||||
exports.stubData = stubData;
|
||||
/**
|
||||
* Stub a window object.
|
||||
*
|
||||
* Note: This uses a custom sandbox.
|
||||
* Please call `restoreStubs()` or `stub.restore()` to restore original functionality.
|
||||
*/
|
||||
function stubWindow(fn, value) {
|
||||
// tslint:disable-next-line: no-typeof-undefined
|
||||
if (typeof globalAny.window === 'undefined') {
|
||||
globalAny.window = {};
|
||||
}
|
||||
const set = (newValue) => {
|
||||
globalAny.window[fn] = newValue;
|
||||
};
|
||||
const get = () => {
|
||||
return globalAny.window[fn];
|
||||
};
|
||||
globalAny.window[fn] = value;
|
||||
return {
|
||||
get,
|
||||
set,
|
||||
};
|
||||
}
|
||||
exports.stubWindow = stubWindow;
|
||||
function restoreStubs() {
|
||||
globalAny.window = undefined;
|
||||
sandbox.restore();
|
||||
}
|
||||
exports.restoreStubs = restoreStubs;
|
||||
function generateFakePubKey() {
|
||||
// Generates a mock pubkey for testing
|
||||
const numBytes = types_1.PubKey.PUBKEY_LEN / 2 - 1;
|
||||
const hexBuffer = crypto.randomBytes(numBytes).toString('hex');
|
||||
const pubkeyString = `05${hexBuffer}`;
|
||||
return new types_1.PubKey(pubkeyString);
|
||||
}
|
||||
exports.generateFakePubKey = generateFakePubKey;
|
||||
function generateFakePubKeys(amount) {
|
||||
const numPubKeys = amount > 0 ? Math.floor(amount) : 0;
|
||||
// tslint:disable-next-line: no-unnecessary-callback-wrapper
|
||||
return new Array(numPubKeys).fill(0).map(() => generateFakePubKey());
|
||||
}
|
||||
exports.generateFakePubKeys = generateFakePubKeys;
|
||||
function generateChatMessage(identifier) {
|
||||
return new outgoing_1.ChatMessage({
|
||||
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
|
||||
identifier: (identifier !== null && identifier !== void 0 ? identifier : uuid_1.v4()),
|
||||
timestamp: Date.now(),
|
||||
attachments: undefined,
|
||||
quote: undefined,
|
||||
expireTimer: undefined,
|
||||
lokiProfile: undefined,
|
||||
preview: undefined,
|
||||
});
|
||||
}
|
||||
exports.generateChatMessage = generateChatMessage;
|
||||
function generateOpenGroupMessage() {
|
||||
const group = new types_1.OpenGroup({
|
||||
server: 'chat.example.server',
|
||||
channel: 0,
|
||||
conversationId: '0',
|
||||
});
|
||||
return new outgoing_1.OpenGroupMessage({
|
||||
timestamp: Date.now(),
|
||||
group,
|
||||
attachments: undefined,
|
||||
preview: undefined,
|
||||
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
|
||||
quote: undefined,
|
||||
});
|
||||
}
|
||||
exports.generateOpenGroupMessage = generateOpenGroupMessage;
|
||||
function generateClosedGroupMessage(groupId) {
|
||||
return new outgoing_1.ClosedGroupChatMessage({
|
||||
identifier: uuid_1.v4(),
|
||||
groupId: (groupId !== null && groupId !== void 0 ? groupId : generateFakePubKey().key),
|
||||
chatMessage: generateChatMessage(),
|
||||
});
|
||||
}
|
||||
exports.generateClosedGroupMessage = generateClosedGroupMessage;
|
||||
class MockPrivateConversation {
|
||||
constructor(params) {
|
||||
var _a;
|
||||
const dayInSeconds = 86400;
|
||||
this.isPrimary = params.isPrimary;
|
||||
this.id = (_a = params.id, (_a !== null && _a !== void 0 ? _a : _1.TestUtils.generateFakePubKey().key));
|
||||
this.attributes = {
|
||||
members: [],
|
||||
left: false,
|
||||
expireTimer: dayInSeconds,
|
||||
profileSharing: true,
|
||||
mentionedUs: false,
|
||||
unreadCount: 99,
|
||||
isArchived: false,
|
||||
active_at: Date.now(),
|
||||
timestamp: Date.now(),
|
||||
secondaryStatus: !this.isPrimary,
|
||||
};
|
||||
}
|
||||
isPrivate() {
|
||||
return true;
|
||||
}
|
||||
isOurLocalDevice() {
|
||||
return false;
|
||||
}
|
||||
isBlocked() {
|
||||
return false;
|
||||
}
|
||||
getPrimaryDevicePubKey() {
|
||||
return this.isPrimary ? this.id : _1.TestUtils.generateFakePubKey().key;
|
||||
}
|
||||
}
|
||||
exports.MockPrivateConversation = MockPrivateConversation;
|
||||
//# sourceMappingURL=testUtils.js.map
|
Loading…
Reference in a new issue