diff --git a/src/tests/backend/common.js b/src/tests/backend/common.js index 89f635012..1787ace7d 100644 --- a/src/tests/backend/common.js +++ b/src/tests/backend/common.js @@ -1,6 +1,8 @@ 'use strict'; +const AttributePool = require('../../static/js/AttributePool'); const apiHandler = require('../../node/handler/APIHandler'); +const assert = require('assert').strict; const io = require('socket.io-client'); const log4js = require('log4js'); const process = require('process'); @@ -185,6 +187,58 @@ exports.handshake = async (socket, padId) => { return msg; }; +/** + * Convenience wrapper around `socket.send()` that waits for acknowledgement. + */ +exports.sendMessage = async (socket, message) => await new Promise((resolve, reject) => { + socket.send(message, (errInfo) => { + if (errInfo != null) { + const {name, message} = errInfo; + const err = new Error(message); + err.name = name; + reject(err); + return; + } + resolve(); + }); +}); + +/** + * Convenience function to send a USER_CHANGES message. Waits for acknowledgement. + */ +exports.sendUserChanges = async (socket, data) => await exports.sendMessage(socket, { + type: 'COLLABROOM', + component: 'pad', + data: { + type: 'USER_CHANGES', + apool: new AttributePool(), + ...data, + }, +}); + +/** + * Convenience function that waits for an ACCEPT_COMMIT message. Asserts that the new revision + * matches the expected revision. + * + * Note: To avoid a race condition, this should be called before the USER_CHANGES message is sent. + * For example: + * + * await Promise.all([ + * common.waitForAcceptCommit(socket, rev + 1), + * common.sendUserChanges(socket, {baseRev: rev, changeset}), + * ]); + */ +exports.waitForAcceptCommit = async (socket, wantRev) => { + const msg = await exports.waitForSocketEvent(socket, 'message'); + assert.deepEqual(msg, { + type: 'COLLABROOM', + data: { + type: 'ACCEPT_COMMIT', + newRev: wantRev, + }, + }); +}; + const alphabet = 'abcdefghijklmnopqrstuvwxyz'; /** diff --git a/src/tests/backend/specs/messages.js b/src/tests/backend/specs/messages.js index 4c9f7e66c..2d5546d6c 100644 --- a/src/tests/backend/specs/messages.js +++ b/src/tests/backend/specs/messages.js @@ -36,27 +36,10 @@ describe(__filename, function () { }); describe('USER_CHANGES', function () { - const sendUserChanges = (changeset, apool = new AttributePool()) => { - socket.json.send({ - type: 'COLLABROOM', - component: 'pad', - data: { - type: 'USER_CHANGES', - baseRev: rev, - changeset, - apool: new AttributePool(), - }, - }); - }; + const sendUserChanges = + async (changeset) => await common.sendUserChanges(socket, {baseRev: rev, changeset}); const assertAccepted = async (wantRev) => { - const msg = await common.waitForSocketEvent(socket, 'message'); - assert.deepEqual(msg, { - type: 'COLLABROOM', - data: { - type: 'ACCEPT_COMMIT', - newRev: wantRev, - }, - }); + await common.waitForAcceptCommit(socket, wantRev); rev = wantRev; }; const assertRejected = async () => { @@ -65,38 +48,55 @@ describe(__filename, function () { }; it('changes are applied', async function () { - sendUserChanges('Z:1>5+5$hello'); - await assertAccepted(rev + 1); + await Promise.all([ + assertAccepted(rev + 1), + sendUserChanges('Z:1>5+5$hello'), + ]); assert.equal(pad.text(), 'hello\n'); }); it('bad changeset is rejected', async function () { - sendUserChanges('this is not a valid changeset'); - await assertRejected(); + await Promise.all([ + assertRejected(), + sendUserChanges('this is not a valid changeset'), + ]); }); it('retransmission is accepted, has no effect', async function () { - sendUserChanges('Z:1>5+5$hello'); - await assertAccepted(rev + 1); + const cs = 'Z:1>5+5$hello'; + await Promise.all([ + assertAccepted(rev + 1), + sendUserChanges(cs), + ]); --rev; - sendUserChanges('Z:1>5+5$hello'); - await assertAccepted(rev + 1); + await Promise.all([ + assertAccepted(rev + 1), + sendUserChanges(cs), + ]); assert.equal(pad.text(), 'hello\n'); }); it('identity changeset is accepted, has no effect', async function () { - sendUserChanges('Z:1>5+5$hello'); - await assertAccepted(rev + 1); - sendUserChanges('Z:6>0$'); - await assertAccepted(rev); + await Promise.all([ + assertAccepted(rev + 1), + sendUserChanges('Z:1>5+5$hello'), + ]); + await Promise.all([ + assertAccepted(rev), + sendUserChanges('Z:6>0$'), + ]); assert.equal(pad.text(), 'hello\n'); }); it('non-identity changeset with no net change is accepted, has no effect', async function () { - sendUserChanges('Z:1>5+5$hello'); - await assertAccepted(rev + 1); - sendUserChanges('Z:6>0-5+5$hello'); - await assertAccepted(rev); + await Promise.all([ + assertAccepted(rev + 1), + sendUserChanges('Z:1>5+5$hello'), + ]); + await Promise.all([ + assertAccepted(rev), + sendUserChanges('Z:6>0-5+5$hello'), + ]); assert.equal(pad.text(), 'hello\n'); }); });