Skip to content

Commit f5be769

Browse files
committed
test: make three flaky tests deterministic
Fixes the failure mechanisms behind the intermittent CI failures observed on this PR (none of which were caused by its changes): - browser/modular 'is able to decrypt history messages': both client- class variants published to the same fixed channel with different random cipher keys, so the history wait could be satisfied by the other variant's message, which then failed to decrypt ('' !== 'Test message'). The channel name is now unique per variant. - uts presence_lifecycle 'bulk enterClient': members are now counted by clientId from both 'enter' and 'present' events - a mid-test connection blip delivers missed members via presence re-sync as 'present', so an enter-only count could undercount forever and burn the full poll timeout. Spec pseudo updated in lock-step (uts/realtime/integration/presence_lifecycle_test.md). - realtime/message 'subscribes to filtered channel': the 'end' sentinel only orders deliveries on the raw channel, but the assertions count deliveries on the derived (filtered) channel - a separate attachment that can lag it. A setTimeout(0) cannot bridge that; the test now waits for the filtered deliveries (bounded at 10s within the suite's 60s budget). The remaining presence flakes are event-driven tests with no identifiable race; their failures are sandbox latency and are a CI retry-policy question, not test bugs.
1 parent f8789a0 commit f5be769

3 files changed

Lines changed: 50 additions & 42 deletions

File tree

test/browser/modular.test.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,11 @@ function registerAblyModularTests(Helper) {
555555
? (op, realtime) => helper.monitorConnectionThenCloseAndFinishAsync(op, realtime)
556556
: async (op) => await op()
557557
)(async () => {
558-
const channelName = 'encrypted_history',
558+
// The channel name must be unique per client-class variant: both variants run
559+
// against the same app with different random cipher keys, and a shared channel
560+
// would let the history wait below be satisfied by the other variant's message,
561+
// which then fails to decrypt with this variant's key.
562+
const channelName = 'encrypted_history_' + clientClassConfig.clientClass.name,
559563
messageText = 'Test message';
560564

561565
const key = await generateRandomKey();

test/realtime/message.test.js

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,43 +1569,44 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async
15691569

15701570
// Subscription to check all messages were received as expected
15711571
rtUnfilteredChannel.subscribe('end', function (msg) {
1572-
// Ensure all pending I/O operations complete and messages are processed
1573-
// before running assertions to avoid race conditions
1574-
//
1575-
// Using setTimeout with 0 timeout as setImmediate is not available
1576-
// in browsers.
1577-
setTimeout(() => {
1578-
try {
1579-
expect(msg.data).to.equal(testData[testData.length - 1].data, 'Unexpected msg data received');
1580-
1581-
// Check that we receive expected messages on filtered channel
1582-
expect(filteredMessages.length).to.equal(2, 'Expect only two filtered message to be received');
1583-
expect(filteredMessages[0].data).to.equal(testData[0].data, 'Unexpected data received');
1584-
expect(filteredMessages[1].data).to.equal(testData[2].data, 'Unexpected data received');
1585-
expect(filteredMessages[0].extras.headers.name).to.equal(
1586-
testData[0].extras.headers.name,
1587-
'Unexpected header value received',
1588-
);
1589-
expect(filteredMessages[1].extras.headers.name).to.equal(
1590-
testData[2].extras.headers.name,
1591-
'Unexpected header value received',
1592-
);
1593-
// Check that message with header that doesn't meet filtering condition is not received.
1594-
for (const m of filteredMessages) {
1595-
expect(m.extras.headers.number).to.equal(26095, 'Unexpected header filtering value received');
1596-
}
1572+
// The 'end' sentinel only orders deliveries on the raw channel; the derived
1573+
// (filtered) channel is a separate attachment whose deliveries can lag it,
1574+
// so wait for the filtered deliveries instead of assuming one macrotask
1575+
// is enough.
1576+
helper
1577+
.waitFor(() => filteredMessages.length === 2, 10000)
1578+
.then(() => {
1579+
try {
1580+
expect(msg.data).to.equal(testData[testData.length - 1].data, 'Unexpected msg data received');
1581+
1582+
// Check that we receive expected messages on filtered channel
1583+
expect(filteredMessages.length).to.equal(2, 'Expect only two filtered message to be received');
1584+
expect(filteredMessages[0].data).to.equal(testData[0].data, 'Unexpected data received');
1585+
expect(filteredMessages[1].data).to.equal(testData[2].data, 'Unexpected data received');
1586+
expect(filteredMessages[0].extras.headers.name).to.equal(
1587+
testData[0].extras.headers.name,
1588+
'Unexpected header value received',
1589+
);
1590+
expect(filteredMessages[1].extras.headers.name).to.equal(
1591+
testData[2].extras.headers.name,
1592+
'Unexpected header value received',
1593+
);
1594+
// Check that message with header that doesn't meet filtering condition is not received.
1595+
for (const m of filteredMessages) {
1596+
expect(m.extras.headers.number).to.equal(26095, 'Unexpected header filtering value received');
1597+
}
15971598

1598-
// Check that we receive expected messages on unfiltered channel, including the `end` event message
1599-
expect(unFilteredMessages.length).to.equal(6, 'Expect only 6 unfiltered message to be received');
1600-
for (var i = 0; i < unFilteredMessages.length; i++) {
1601-
expect(unFilteredMessages[i].data).to.equal(testData[i].data, 'Unexpected data received');
1599+
// Check that we receive expected messages on unfiltered channel, including the `end` event message
1600+
expect(unFilteredMessages.length).to.equal(6, 'Expect only 6 unfiltered message to be received');
1601+
for (var i = 0; i < unFilteredMessages.length; i++) {
1602+
expect(unFilteredMessages[i].data).to.equal(testData[i].data, 'Unexpected data received');
1603+
}
1604+
} catch (err) {
1605+
helper.closeAndFinish(done, realtime, err);
1606+
return;
16021607
}
1603-
} catch (err) {
1604-
helper.closeAndFinish(done, realtime, err);
1605-
return;
1606-
}
1607-
helper.closeAndFinish(done, realtime);
1608-
}, 0);
1608+
helper.closeAndFinish(done, realtime);
1609+
});
16091610
});
16101611
var restChannel = rest.channels.get('chan');
16111612
restChannel.publish(testData);

test/uts/realtime/integration/presence/presence_lifecycle.test.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,13 @@ describeEachProtocol('uts/realtime/integration/presence/presence_lifecycle', fun
6161
const channelA = clientA.channels.get(channelName);
6262
const channelB = clientB.channels.get(channelName);
6363

64-
// Attach B and subscribe before A enters any members
65-
const receivedEnters: any[] = [];
66-
await channelB.presence.subscribe('enter', (msg: any) => {
67-
receivedEnters.push(msg);
64+
// Attach B and subscribe before A enters any members. Members are counted by
65+
// clientId from both 'enter' and 'present' events: if B's connection blips
66+
// mid-test, members it missed arrive via the presence re-sync as 'present'
67+
// events rather than 'enter', and an enter-only count would undercount forever.
68+
const enteredClientIds = new Set<string>();
69+
await channelB.presence.subscribe(['enter', 'present'], (msg: any) => {
70+
enteredClientIds.add(msg.clientId);
6871
});
6972

7073
await channelA.attach();
@@ -74,12 +77,12 @@ describeEachProtocol('uts/realtime/integration/presence/presence_lifecycle', fun
7477
await channelA.presence.enterClient(`user-${i}`, `data-${i}`);
7578
}
7679

77-
await pollUntil(() => (receivedEnters.length >= memberCount ? true : null), {
80+
await pollUntil(() => (enteredClientIds.size >= memberCount ? true : null), {
7881
interval: 200,
7982
timeout: 30000,
8083
});
8184

82-
expect(receivedEnters).to.have.length(memberCount);
85+
expect(enteredClientIds.size).to.equal(memberCount);
8386

8487
const members = await channelB.presence.get();
8588
expect(members).to.have.length(memberCount);

0 commit comments

Comments
 (0)