session-desktop/ts/test/session/unit/sending/MessageQueue_test.ts

295 lines
10 KiB
TypeScript
Raw Normal View History

/* eslint-disable consistent-return */
/* eslint-disable no-unused-expressions */
/* eslint-disable more/no-then */
/* eslint-disable no-loop-func */
/* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-unreachable-loop */
/* eslint-disable no-restricted-syntax */
2023-08-02 06:50:35 +02:00
import { randomBytes } from 'crypto';
2020-06-19 02:33:02 +02:00
import chai from 'chai';
import Sinon, * as sinon from 'sinon';
2020-08-03 03:45:02 +02:00
import { describe } from 'mocha';
import chaiAsPromised from 'chai-as-promised';
2020-08-03 03:45:02 +02:00
import { GroupUtils, PromiseUtils, UserUtils } from '../../../../session/utils';
import { TestUtils } from '../../../test-utils';
2020-08-03 03:45:02 +02:00
import { MessageQueue } from '../../../../session/sending/MessageQueue';
import { ContentMessage } from '../../../../session/messages/outgoing';
import { PubKey, RawMessage } from '../../../../session/types';
2020-08-03 03:45:02 +02:00
import { MessageSender } from '../../../../session/sending';
import { PendingMessageCacheStub } from '../../../test-utils/stubs';
import { ClosedGroupMessage } from '../../../../session/messages/outgoing/controlMessage/group/ClosedGroupMessage';
2020-06-14 20:35:10 +02:00
import { MessageSentHandler } from '../../../../session/sending/MessageSentHandler';
import { stubData } from '../../../test-utils/utils';
import { SnodeNamespaces } from '../../../../session/apis/snode_api/namespaces';
chai.use(chaiAsPromised as any);
2021-02-09 23:30:56 +01:00
chai.should();
2020-06-19 02:33:02 +02:00
const { expect } = chai;
2020-06-18 03:14:03 +02:00
2020-06-11 00:19:44 +02:00
describe('MessageQueue', () => {
2020-06-16 00:33:53 +02:00
// Initialize new stubbed cache
2020-06-18 05:46:58 +02:00
const ourDevice = TestUtils.generateFakePubKey();
2020-06-17 05:12:20 +02:00
const ourNumber = ourDevice.key;
2020-06-15 23:44:37 +02:00
2020-06-16 08:53:10 +02:00
// Initialize new stubbed queue
2020-06-19 02:33:02 +02:00
let pendingMessageCache: PendingMessageCacheStub;
let messageSentHandlerFailedStub: sinon.SinonStub;
let messageSentHandlerSuccessStub: sinon.SinonStub;
let messageSentPublicHandlerSuccessStub: sinon.SinonStub;
2020-06-14 20:35:10 +02:00
let messageQueueStub: MessageQueue;
2020-06-16 08:53:10 +02:00
2020-06-15 23:44:37 +02:00
// Message Sender Stubs
let sendStub: sinon.SinonStub<[
RawMessage,
(number | undefined)?,
(number | undefined)?,
(boolean | undefined)?
]>;
2020-06-16 08:53:10 +02:00
beforeEach(() => {
2020-06-16 00:33:53 +02:00
// Utils Stubs
Sinon.stub(UserUtils, 'getOurPubKeyStrFromCache').returns(ourNumber);
2020-06-18 05:24:53 +02:00
2020-06-15 23:44:37 +02:00
// Message Sender Stubs
sendStub = Sinon.stub(MessageSender, 'send');
messageSentHandlerFailedStub = Sinon.stub(
MessageSentHandler,
'handleMessageSentFailure'
).resolves();
messageSentHandlerSuccessStub = Sinon.stub(
MessageSentHandler,
'handleMessageSentSuccess'
).resolves();
messageSentPublicHandlerSuccessStub = Sinon.stub(
MessageSentHandler,
'handlePublicMessageSentSuccess'
).resolves();
2020-06-14 23:00:02 +02:00
2020-06-16 08:53:10 +02:00
// Init Queue
2020-06-19 02:33:02 +02:00
pendingMessageCache = new PendingMessageCacheStub();
messageQueueStub = new MessageQueue(pendingMessageCache);
TestUtils.stubWindowLog();
2020-06-14 20:35:10 +02:00
});
afterEach(() => {
Sinon.restore();
2020-06-15 23:44:37 +02:00
});
2020-06-17 07:59:12 +02:00
describe('processPending', () => {
it('will send messages', done => {
2020-06-18 05:46:58 +02:00
const device = TestUtils.generateFakePubKey();
2020-06-14 20:35:10 +02:00
const waitForMessageSentEvent = new Promise(resolve => {
resolve(true);
done();
});
2021-02-19 05:13:43 +01:00
void pendingMessageCache
2021-04-22 10:03:58 +02:00
.add(device, TestUtils.generateVisibleMessage(), waitForMessageSentEvent as any)
2021-02-19 05:13:43 +01:00
.then(async () => {
return messageQueueStub.processPending(device);
})
.then(() => {
expect(waitForMessageSentEvent).to.be.fulfilled;
});
2020-06-19 02:33:02 +02:00
});
2020-06-16 08:53:10 +02:00
2020-06-19 02:33:02 +02:00
it('should remove message from cache', async () => {
const events = ['sendSuccess', 'sendFail'];
2020-06-19 02:33:02 +02:00
for (const event of events) {
if (event === 'sendSuccess') {
2020-06-19 02:33:02 +02:00
sendStub.resolves();
} else {
sendStub.throws(new Error('fail'));
}
const device = TestUtils.generateFakePubKey();
await pendingMessageCache.add(
device,
TestUtils.generateVisibleMessage(),
SnodeNamespaces.UserMessages
);
2020-06-19 02:33:02 +02:00
const initialMessages = await pendingMessageCache.getForDevice(device);
expect(initialMessages).to.have.length(1);
await messageQueueStub.processPending(device);
2020-06-19 08:46:42 +02:00
const promise = PromiseUtils.waitUntil(async () => {
2020-06-19 02:33:02 +02:00
const messages = await pendingMessageCache.getForDevice(device);
return messages.length === 0;
2021-03-03 23:55:44 +01:00
}, 100);
return promise.should.be.fulfilled;
2020-06-19 02:33:02 +02:00
}
2020-06-22 01:10:57 +02:00
}).timeout(15000);
2020-06-16 08:53:10 +02:00
2020-06-19 02:33:02 +02:00
describe('events', () => {
it('should send a success event if message was sent', done => {
stubData('getMessageById').resolves();
const message = TestUtils.generateVisibleMessage();
sendStub.resolves({ effectiveTimestamp: Date.now(), wrappedEnvelope: randomBytes(10) });
const device = TestUtils.generateFakePubKey();
Sinon.stub(MessageSender, 'getMinRetryTimeout').returns(10);
const waitForMessageSentEvent = async () =>
new Promise<void>(resolve => {
resolve();
try {
expect(messageSentHandlerSuccessStub.callCount).to.be.equal(1);
2021-04-22 10:03:58 +02:00
expect(messageSentHandlerSuccessStub.lastCall.args[0].identifier).to.be.equal(
message.identifier
);
done();
} catch (e) {
done(e);
}
});
2021-03-03 23:55:44 +01:00
void pendingMessageCache
.add(device, message, SnodeNamespaces.UserMessages, waitForMessageSentEvent)
2021-03-03 23:55:44 +01:00
.then(() => messageQueueStub.processPending(device));
2020-06-19 02:33:02 +02:00
});
2021-03-03 23:55:44 +01:00
it('should send a fail event if something went wrong while sending', async () => {
2020-06-19 02:33:02 +02:00
sendStub.throws(new Error('failure'));
const device = TestUtils.generateFakePubKey();
const message = TestUtils.generateVisibleMessage();
2021-02-19 05:13:43 +01:00
void pendingMessageCache
.add(device, message, SnodeNamespaces.UserMessages)
2021-03-03 23:55:44 +01:00
.then(() => messageQueueStub.processPending(device));
// The cb is only invoke is all reties fails. Here we poll until the messageSentHandlerFailed was invoked as this is what we want to do
return PromiseUtils.poll(done => {
if (messageSentHandlerFailedStub.callCount === 1) {
try {
expect(messageSentHandlerFailedStub.callCount).to.be.equal(1);
2021-04-22 10:03:58 +02:00
expect(messageSentHandlerFailedStub.lastCall.args[0].identifier).to.be.equal(
message.identifier
);
expect(messageSentHandlerFailedStub.lastCall.args[1].message).to.equal('failure');
2021-03-03 23:55:44 +01:00
done();
} catch (e) {
done(e);
}
}
});
2020-06-16 09:59:20 +02:00
});
2020-06-16 05:58:15 +02:00
});
2020-06-12 03:19:19 +02:00
});
2021-01-20 06:58:59 +01:00
describe('sendToPubKey', () => {
it('should send the message to the device', async () => {
const device = TestUtils.generateFakePubKey();
const stub = Sinon.stub(messageQueueStub as any, 'process').resolves();
2020-06-19 02:33:02 +02:00
const message = TestUtils.generateVisibleMessage();
await messageQueueStub.sendToPubKey(device, message, SnodeNamespaces.UserMessages);
2020-06-19 02:33:02 +02:00
const args = stub.lastCall.args as [Array<PubKey>, ContentMessage];
expect(args[0]).to.be.equal(device);
2020-06-19 02:33:02 +02:00
expect(args[1]).to.equal(message);
});
});
describe('sendToGroup', () => {
it('should throw an error if invalid non-group message was passed', async () => {
const chatMessage = TestUtils.generateVisibleMessage();
return expect(
messageQueueStub.sendToGroup({
message: chatMessage as any,
namespace: SnodeNamespaces.ClosedGroupMessage,
})
).to.be.rejectedWith('Invalid group message passed in sendToGroup.');
});
describe('closed groups', () => {
2020-06-19 02:33:02 +02:00
it('can send to closed group', async () => {
2021-04-22 10:03:58 +02:00
const members = TestUtils.generateFakePubKeys(4).map(p => new PubKey(p.key));
Sinon.stub(GroupUtils, 'getGroupMembers').returns(members);
2020-06-17 09:06:31 +02:00
const send = Sinon.stub(messageQueueStub, 'sendToPubKey').resolves();
2020-06-14 23:00:02 +02:00
2020-06-19 02:33:02 +02:00
const message = TestUtils.generateClosedGroupMessage();
await messageQueueStub.sendToGroup({
message,
namespace: SnodeNamespaces.ClosedGroupMessage,
});
2021-01-14 00:44:15 +01:00
expect(send.callCount).to.equal(1);
2020-06-14 23:00:02 +02:00
2021-01-14 00:44:15 +01:00
const arg = send.getCall(0).args;
expect(arg[1] instanceof ClosedGroupMessage).to.equal(
2020-06-19 02:33:02 +02:00
true,
'message sent to group member was not a ClosedGroupMessage'
2020-06-19 02:33:02 +02:00
);
});
2020-06-14 20:35:10 +02:00
describe('open groupsv2', () => {
let sendToOpenGroupV2Stub: sinon.SinonStub;
2021-01-20 06:58:59 +01:00
beforeEach(() => {
sendToOpenGroupV2Stub = Sinon.stub(MessageSender, 'sendToOpenGroupV2').resolves(
TestUtils.generateOpenGroupMessageV2()
);
2021-01-20 06:58:59 +01:00
});
2020-06-18 03:14:03 +02:00
2021-01-20 06:58:59 +01:00
it('can send to open group', async () => {
const message = TestUtils.generateOpenGroupVisibleMessage();
const roomInfos = TestUtils.generateOpenGroupV2RoomInfos();
await messageQueueStub.sendToOpenGroupV2({
message,
roomInfos,
blinded: false,
filesToLink: [],
});
expect(sendToOpenGroupV2Stub.callCount).to.equal(1);
2021-01-20 06:58:59 +01:00
});
2020-06-17 07:59:12 +02:00
2021-01-20 06:58:59 +01:00
it('should emit a success event when send was successful', async () => {
sendToOpenGroupV2Stub.resolves({
serverId: 5125,
sentTimestamp: 5127,
});
2020-07-31 03:03:52 +02:00
const message = TestUtils.generateOpenGroupVisibleMessage();
const roomInfos = TestUtils.generateOpenGroupV2RoomInfos();
await messageQueueStub.sendToOpenGroupV2({
message,
roomInfos,
blinded: false,
filesToLink: [],
});
expect(messageSentPublicHandlerSuccessStub.callCount).to.equal(1);
expect(messageSentPublicHandlerSuccessStub.lastCall.args[0]).to.equal(message.identifier);
2021-04-22 10:03:58 +02:00
expect(messageSentPublicHandlerSuccessStub.lastCall.args[1].serverId).to.equal(5125);
expect(messageSentPublicHandlerSuccessStub.lastCall.args[1].serverTimestamp).to.equal(
5127
2021-04-22 10:03:58 +02:00
);
2021-01-20 06:58:59 +01:00
});
2020-06-17 07:59:12 +02:00
2021-01-20 06:58:59 +01:00
it('should emit a fail event if something went wrong', async () => {
sendToOpenGroupV2Stub.resolves({ serverId: -1, serverTimestamp: -1 });
const message = TestUtils.generateOpenGroupVisibleMessage();
const roomInfos = TestUtils.generateOpenGroupV2RoomInfos();
2020-06-18 03:14:03 +02:00
await messageQueueStub.sendToOpenGroupV2({
message,
roomInfos,
blinded: false,
filesToLink: [],
});
expect(messageSentHandlerFailedStub.callCount).to.equal(1);
2021-04-22 10:03:58 +02:00
expect(messageSentHandlerFailedStub.lastCall.args[0].identifier).to.equal(
message.identifier
);
2021-01-20 06:58:59 +01:00
});
2020-06-19 02:33:02 +02:00
});
2020-06-17 08:49:20 +02:00
});
2020-06-17 07:59:12 +02:00
});
});