Skip to content

Commit b41cadc

Browse files
Implement CHA-RL4a2
Have used the modified “has been attached” criterion from 8b5aa47, for consistency.
1 parent c828645 commit b41cadc

File tree

2 files changed

+93
-7
lines changed

2 files changed

+93
-7
lines changed

Sources/AblyChat/RoomLifecycleManager.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,11 @@ internal actor DefaultRoomLifecycleManager<Contributor: RoomLifecycleContributor
440440
break
441441
}
442442

443+
// CHA-RL4a2 — if contributor has not yet been attached then no-op
444+
guard contributorAnnotations[contributor].hasBeenAttached else {
445+
break
446+
}
447+
443448
let reason = stateChange.reason
444449

445450
if hasOperationInProgress {

Tests/AblyChatTests/DefaultRoomLifecycleManagerTests.swift

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,11 +1354,25 @@ struct DefaultRoomLifecycleManagerTests {
13541354
// @spec CHA-RL4a1
13551355
@Test
13561356
func contributorUpdate_withResumedTrue_doesNothing() async throws {
1357-
// Given: A DefaultRoomLifecycleManager
1357+
// Given: A DefaultRoomLifecycleManager, which has a contributor for which it has previously received an ATTACHED state change (so that we get through the CHA-RL4a2 check)
13581358
let contributor = createContributor()
13591359
let manager = await createManager(contributors: [contributor])
13601360

1361-
// When: A contributor emits an UPDATE event with `resumed` flag set to true
1361+
// This is to satisfy "for which it has previously received an ATTACHED state change"
1362+
let previousContributorStateChange = ARTChannelStateChange(
1363+
// `previous`, `reason`, and `resumed` are arbitrary, but for realism let’s simulate an initial ATTACHED
1364+
current: .attached,
1365+
previous: .attaching,
1366+
event: .attached,
1367+
reason: nil,
1368+
resumed: false
1369+
)
1370+
1371+
await waitForManager(manager, toHandleContributorStateChange: previousContributorStateChange) {
1372+
await contributor.channel.emitStateChange(previousContributorStateChange)
1373+
}
1374+
1375+
// When: This contributor emits an UPDATE event with `resumed` flag set to true
13621376
let contributorStateChange = ARTChannelStateChange(
13631377
current: .attached, // arbitrary
13641378
previous: .attached, // arbitrary
@@ -1376,17 +1390,56 @@ struct DefaultRoomLifecycleManagerTests {
13761390
#expect(await contributor.emitDiscontinuityArguments.isEmpty)
13771391
}
13781392

1393+
// @specPartial CHA-RL4a2 - TODO: I have changed the criteria for deciding whether an ATTACHED status change represents a discontinuity, to be based on whether there was a previous ATTACHED state change instead of whether the `attach()` call has completed; see https://github.com/ably/specification/issues/239 and change this to @spec once we’re aligned with spec again
1394+
@Test
1395+
func contributorUpdate_withContributorNotPreviouslyAttached_doesNothing() async throws {
1396+
// Given: A DefaultRoomLifecycleManager, which has a contributor for which it has not previously received an ATTACHED state change
1397+
let contributor = createContributor()
1398+
let manager = await createManager(contributors: [contributor])
1399+
1400+
// When: This contributor emits an UPDATE event with `resumed` flag set to false (so that we get through the CHA-RL4a1 check)
1401+
let contributorStateChange = ARTChannelStateChange(
1402+
current: .attached, // arbitrary
1403+
previous: .attached, // arbitrary
1404+
event: .update,
1405+
reason: ARTErrorInfo(domain: "SomeDomain", code: 123), // arbitrary
1406+
resumed: false
1407+
)
1408+
1409+
await waitForManager(manager, toHandleContributorStateChange: contributorStateChange) {
1410+
await contributor.channel.emitStateChange(contributorStateChange)
1411+
}
1412+
1413+
// Then: The manager does not record a pending discontinuity event for this contributor, nor does it call `emitDiscontinuity` on the contributor; this shows us that the actions described in CHA-RL4a3 and CHA-RL4a4 haven’t been performed
1414+
#expect(await manager.testsOnly_pendingDiscontinuityEvent(for: contributor) == nil)
1415+
#expect(await contributor.emitDiscontinuityArguments.isEmpty)
1416+
}
1417+
13791418
// @specOneOf(1/2) CHA-RL4a3
13801419
@Test
13811420
func contributorUpdate_withResumedFalse_withOperationInProgress_recordsPendingDiscontinuityEvent() async throws {
1382-
// Given: A DefaultRoomLifecycleManager, with a room lifecycle operation in progress
1421+
// Given: A DefaultRoomLifecycleManager, with a room lifecycle operation in progress, and with a contributor for which it has previously received an ATTACHED state change
13831422
let contributor = createContributor()
13841423
let manager = await createManager(
13851424
forTestingWhatHappensWhenCurrentlyIn: .attachingDueToAttachOperation(attachOperationID: UUID()), // case and ID arbitrary, just care that an operation is in progress
13861425
contributors: [contributor]
13871426
)
13881427

1389-
// When: A contributor emits an UPDATE event with `resumed` flag set to false
1428+
// This is to satisfy "for which it has previously received an ATTACHED state change"
1429+
let previousContributorStateChange = ARTChannelStateChange(
1430+
// `previous`, `reason`, and `resumed` are arbitrary, but for realism let’s simulate an initial ATTACHED
1431+
current: .attached,
1432+
previous: .attaching,
1433+
event: .attached,
1434+
reason: nil,
1435+
resumed: false
1436+
)
1437+
1438+
await waitForManager(manager, toHandleContributorStateChange: previousContributorStateChange) {
1439+
await contributor.channel.emitStateChange(previousContributorStateChange)
1440+
}
1441+
1442+
// When: This contributor emits an UPDATE event with `resumed` flag set to false
13901443
let contributorStateChange = ARTChannelStateChange(
13911444
current: .attached, // arbitrary
13921445
previous: .attached, // arbitrary
@@ -1407,7 +1460,7 @@ struct DefaultRoomLifecycleManagerTests {
14071460
// @specOneOf(2/2) CHA-RL4a3 - tests the “though it must not overwrite any existing discontinuity event” part of the spec point
14081461
@Test
14091462
func contributorUpdate_withResumedFalse_withOperationInProgress_doesNotOverwriteExistingPendingDiscontinuityEvent() async throws {
1410-
// Given: A DefaultRoomLifecycleManager, with a room lifecycle operation in progress and with an existing pending discontinuity event for a given contributor
1463+
// Given: A DefaultRoomLifecycleManager, with a room lifecycle operation in progress, with a contributor for which it has previously received an ATTACHED state change, and with an existing pending discontinuity event for this contributor
14111464
let contributor = createContributor()
14121465
let existingPendingDiscontinuityEvent = DiscontinuityEvent(error: .createUnknownError())
14131466
let manager = await createManager(
@@ -1418,6 +1471,20 @@ struct DefaultRoomLifecycleManagerTests {
14181471
contributors: [contributor]
14191472
)
14201473

1474+
// This is to satisfy "for which it has previously received an ATTACHED state change"
1475+
let previousContributorStateChange = ARTChannelStateChange(
1476+
// `previous`, `reason`, and `resumed` are arbitrary, but for realism let’s simulate an initial ATTACHED
1477+
current: .attached,
1478+
previous: .attaching,
1479+
event: .attached,
1480+
reason: nil,
1481+
resumed: false
1482+
)
1483+
1484+
await waitForManager(manager, toHandleContributorStateChange: previousContributorStateChange) {
1485+
await contributor.channel.emitStateChange(previousContributorStateChange)
1486+
}
1487+
14211488
// When: The aforementioned contributor emits an UPDATE event with `resumed` flag set to false
14221489
let contributorStateChange = ARTChannelStateChange(
14231490
current: .attached, // arbitrary
@@ -1439,14 +1506,28 @@ struct DefaultRoomLifecycleManagerTests {
14391506
// @spec CHA-RL4a4
14401507
@Test
14411508
func contributorUpdate_withResumedTrue_withNoOperationInProgress_emitsDiscontinuityEvent() async throws {
1442-
// Given: A DefaultRoomLifecycleManager, with no room lifecycle operation in progress
1509+
// Given: A DefaultRoomLifecycleManager, with no room lifecycle operation in progress, and with a contributor for which it has previously received an ATTACHED state change
14431510
let contributor = createContributor()
14441511
let manager = await createManager(
14451512
forTestingWhatHappensWhenCurrentlyIn: .initialized, // case arbitrary, just care that no operation is in progress
14461513
contributors: [contributor]
14471514
)
14481515

1449-
// When: A contributor emits an UPDATE event with `resumed` flag set to false
1516+
// This is to satisfy "for which it has previously received an ATTACHED state change"
1517+
let previousContributorStateChange = ARTChannelStateChange(
1518+
// `previous`, `reason`, and `resumed` are arbitrary, but for realism let’s simulate an initial ATTACHED
1519+
current: .attached,
1520+
previous: .attaching,
1521+
event: .attached,
1522+
reason: nil,
1523+
resumed: false
1524+
)
1525+
1526+
await waitForManager(manager, toHandleContributorStateChange: previousContributorStateChange) {
1527+
await contributor.channel.emitStateChange(previousContributorStateChange)
1528+
}
1529+
1530+
// When: This contributor emits an UPDATE event with `resumed` flag set to false
14501531
let contributorStateChange = ARTChannelStateChange(
14511532
current: .attached, // arbitrary
14521533
previous: .attached, // arbitrary

0 commit comments

Comments
 (0)