Skip to content

Commit

Permalink
Add test getRoot() while STATE_SYNC is in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
VeskeR committed Oct 17, 2024
1 parent f1af439 commit c1144ca
Showing 1 changed file with 113 additions and 7 deletions.
120 changes: 113 additions & 7 deletions test/realtime/live_objects.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ define(['ably', 'shared_helper', 'chai', 'live_objects', 'live_objects_helper'],
});

/** @nospec */
it('getRoot() returns empty root when no state exist on a channel', async function () {
it('getRoot() returns LiveMap instance', async function () {
const helper = this.test.helper;
const client = RealtimeWithLiveObjects(helper);

Expand All @@ -116,12 +116,12 @@ define(['ably', 'shared_helper', 'chai', 'live_objects', 'live_objects_helper'],
await channel.attach();
const root = await liveObjects.getRoot();

expect(root.size()).to.equal(0, 'Check root has no keys');
expect(root.constructor.name).to.equal('LiveMap');
}, client);
});

/** @nospec */
it('getRoot() returns LiveMap instance', async function () {
it('getRoot() returns live object with id "root"', async function () {
const helper = this.test.helper;
const client = RealtimeWithLiveObjects(helper);

Expand All @@ -132,12 +132,13 @@ define(['ably', 'shared_helper', 'chai', 'live_objects', 'live_objects_helper'],
await channel.attach();
const root = await liveObjects.getRoot();

expect(root.constructor.name).to.equal('LiveMap');
helper.recordPrivateApi('call.LiveObject.getObjectId');
expect(root.getObjectId()).to.equal('root');
}, client);
});

/** @nospec */
it('getRoot() returns live object with id "root"', async function () {
it('getRoot() returns empty root when no state exist on a channel', async function () {
const helper = this.test.helper;
const client = RealtimeWithLiveObjects(helper);

Expand All @@ -148,8 +149,35 @@ define(['ably', 'shared_helper', 'chai', 'live_objects', 'live_objects_helper'],
await channel.attach();
const root = await liveObjects.getRoot();

helper.recordPrivateApi('call.LiveObject.getObjectId');
expect(root.getObjectId()).to.equal('root');
expect(root.size()).to.equal(0, 'Check root has no keys');
}, client);
});

/** @nospec */
it('getRoot() waits for initial STATE_SYNC to be completed before resolving', async function () {
const helper = this.test.helper;
const client = RealtimeWithLiveObjects(helper);

await helper.monitorConnectionThenCloseAndFinish(async () => {
const channel = client.channels.get('channel', channelOptionsWithLiveObjects());
const liveObjects = channel.liveObjects;

const getRootPromise = liveObjects.getRoot();

let getRootResolved = false;
getRootPromise.then(() => {
getRootResolved = true;
});

// give a chance for getRoot() to resolve and proc its handler. it should not
helper.recordPrivateApi('call.Platform.nextTick');
await new Promise((res) => nextTick(res));
expect(getRootResolved, 'Check getRoot() is not resolved until STATE_SYNC sequence is completed').to.be.false;

await channel.attach();

// should resolve eventually after attach
await getRootPromise;
}, client);
});

Expand Down Expand Up @@ -179,6 +207,84 @@ define(['ably', 'shared_helper', 'chai', 'live_objects', 'live_objects_helper'],
}, client);
});

/** @nospec */
it('getRoot() waits for subsequent STATE_SYNC to finish before resolving', async function () {
const helper = this.test.helper;
const client = RealtimeWithLiveObjects(helper);

await helper.monitorConnectionThenCloseAndFinish(async () => {
const channel = client.channels.get('channel', channelOptionsWithLiveObjects());
const liveObjects = channel.liveObjects;

await channel.attach();
// wait for initial STATE_SYNC sequence to complete
await liveObjects.getRoot();

// inject STATE_SYNC message to emulate start of new sequence
helper.recordPrivateApi('call.channel.processMessage');
helper.recordPrivateApi('call.makeProtocolMessageFromDeserialized');
await channel.processMessage(
createPM({
action: 20,
channel: 'channel',
// have cursor so client awaits for additional STATE_SYNC messages
channelSerial: 'serial:cursor',
state: [],
}),
);

let getRootResolved = false;
let newRoot;
liveObjects.getRoot().then((value) => {
getRootResolved = true;
newRoot = value;
});

// wait for next tick to check that getRoot() promise handler didn't proc
helper.recordPrivateApi('call.Platform.nextTick');
await new Promise((res) => nextTick(res));

expect(getRootResolved, 'Check getRoot() is not resolved while STATE_SYNC is in progress').to.be.false;

// inject next STATE_SYNC message
helper.recordPrivateApi('call.channel.processMessage');
helper.recordPrivateApi('call.makeProtocolMessageFromDeserialized');
await channel.processMessage(
createPM({
action: 20,
channel: 'channel',
// no cursor to indicate the end of STATE_SYNC messages
channelSerial: 'serial:',
state: [
{
object: {
objectId: 'root',
regionalTimeserial: '@0-0',
map: {
entries: {
key: {
timeserial: '@0-0',
data: {
value: 1,
},
},
},
},
},
},
],
}),
);

// wait for next tick for getRoot() handler to process
helper.recordPrivateApi('call.Platform.nextTick');
await new Promise((res) => nextTick(res));

expect(getRootResolved, 'Check getRoot() is resolved when STATE_SYNC sequence has ended').to.be.true;
expect(newRoot.get('key')).to.equal(1, 'Check new root after STATE_SYNC sequence has expected key');
}, client);
});

it('builds state object tree from STATE_SYNC sequence on channel attachment', async function () {
const helper = this.test.helper;
const client = RealtimeWithLiveObjects(helper);
Expand Down

0 comments on commit c1144ca

Please sign in to comment.