Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/common/lib/client/realtimechannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,18 +392,22 @@ class RealtimeChannel extends EventEmitter {

async detach(): Promise<void> {
const connectionManager = this.connectionManager;
if (!connectionManager.activeState()) {
throw connectionManager.getError();
}
switch (this.state) {
// RTL5j
case 'suspended':
this.notifyState('detached');
return;
case 'detached':
return;
// RTL5b
case 'failed':
throw new ErrorInfo('Unable to detach; channel state = failed', 90001, 400);
default:
// RTL5l: if connection is not connected, immediately transition to detached
if (connectionManager.state.state !== 'connected') {
this.notifyState('detached');
return;
}
this.requestState('detaching');
// eslint-disable-next-line no-fallthrough
case 'detaching':
Expand Down
25 changes: 25 additions & 0 deletions test/realtime/channel.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1652,6 +1652,31 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, Helper, async
});
});

/** @spec RTL5l */
it('detaching when connection is not connected immediately transitions channel to detached', async function () {
const helper = this.test.helper;
const realtime = helper.AblyRealtime({ transports: [helper.bestTransport] });
const channelName = 'detach_when_disconnected';
const channel = realtime.channels.get(channelName);

try {
await realtime.connection.once('connected');
await channel.attach();
expect(channel.state).to.equal('attached', 'channel should be attached');

// Simulate connection becoming disconnected
realtime.connection.connectionManager.requestState({ state: 'disconnected' });
await realtime.connection.once('disconnected');
expect(realtime.connection.state).to.equal('disconnected', 'connection should be disconnected');

// Detach should succeed immediately without waiting for reconnection
await channel.detach();
expect(channel.state).to.equal('detached', 'channel should immediately transition to detached');
} finally {
helper.closeAndFinish(helper.noop, realtime);
}
});

/** @spec RTL5b */
it('detaching from failed channel results in error', function (done) {
const helper = this.test.helper;
Expand Down
Loading