Skip to content

Conversation

@maratal
Copy link
Collaborator

@maratal maratal commented Mar 2, 2025

Closes #124

Summary by CodeRabbit

  • New Features

    • Enhanced debugging capabilities for connection status changes, allowing for better monitoring of transient disconnect timer states.
  • Bug Fixes

    • Improved handling of connection status updates during various state transitions to ensure accurate event emissions.
  • Tests

    • Introduced a comprehensive test suite for validating connection handling and status management, ensuring robustness in various scenarios.
    • Added multiple asynchronous tests to verify connection status management and transitions.

@coderabbitai
Copy link

coderabbitai bot commented Mar 2, 2025

Walkthrough

This pull request enhances the debugging functionality in the connection management system by introducing transient disconnect timer debug events. The changes include new structures, methods, and variables in the connection class to handle and emit debug events when connection states change. Additionally, the pull request adds comprehensive tests and updates the mock connection to support asynchronous state transitions and callback management for improved testing.

Changes

File Path Change Summary
Sources/.../DefaultConnection.swift Enhanced debugging for transient disconnect timers: added TransientDisconnectTimerEvent, subscription method (testsOnly_subscribeToTransientDisconnectTimerEvents), and a subscriptions array; removed an outdated commented-out debug event emission.
Tests/.../DefaultConnectionTests.swift Introduced new test suite DefaultConnectionTests with asynchronous methods to assert correct connection state transitions and transient disconnect timer behaviors.
Tests/.../Mocks/MockConnection.swift Updated class to conform to @unchecked Sendable; added stateCallback; modified on/off methods; and added transitionToState to simulate and trigger state changes.

Sequence Diagram(s)

sequenceDiagram
    actor Tester as Test Suite
    participant DC as DefaultConnection
    participant MC as MockConnection

    Tester->>DC: testsOnly_subscribeToTransientDisconnectTimerEvents()
    MC->>DC: transitionToState(CONNECTED)
    DC->>Tester: Emit debug event (timer inactive)
    MC->>DC: transitionToState(DISCONNECTED)
    DC->>Tester: Emit debug event (timer active)
Loading

Possibly related PRs

Suggested reviewers

  • umair-ably

Poem

In the code garden where bugs once did hide,
I, a rabbit, now merrily hop with great pride.
Transient timers dance as connections perform,
Debug events sing, in each state they transform.
Carrots and code—a joyful, debug-filled ride! 🐇

Happy hopping through flawless, refined stride!


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36f77e5 and 3b2f333.

📒 Files selected for processing (3)
  • Sources/AblyChat/DefaultConnection.swift (4 hunks)
  • Tests/AblyChatTests/DefaultConnectionTests.swift (1 hunks)
  • Tests/AblyChatTests/Mocks/MockConnection.swift (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • Tests/AblyChatTests/Mocks/MockConnection.swift
  • Sources/AblyChat/DefaultConnection.swift
  • Tests/AblyChatTests/DefaultConnectionTests.swift
⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: Example app, tvOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, tvOS (Xcode 16)
  • GitHub Check: Example app, iOS (Xcode 16)
  • GitHub Check: Example app, macOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, iOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, macOS (Xcode 16)
  • GitHub Check: Xcode, tvOS (Xcode 16)
  • GitHub Check: SPM, release configuration (Xcode 16)
  • GitHub Check: Xcode, iOS (Xcode 16)
  • GitHub Check: SPM (Xcode 16)
  • GitHub Check: Xcode, macOS (Xcode 16)
  • GitHub Check: check-documentation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@maratal maratal requested a review from umair-ably March 2, 2025 14:55
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (16)
Tests/AblyChatTests/Mocks/MockHTTPPaginatedResponse.swift (1)

43-43: Clarify the negation in isLast.
Using !_hasNext is conceptually straightforward. Consider renaming _hasNext to _hasMorePages for improved readability.

Tests/AblyChatTests/DefaultRoomReactionsTests.swift (1)

79-96: Add edge-case validation for malformed messages.
Rejecting malformed messages is covered here, which is excellent. Consider adding more complex malformation scenarios (e.g., missing keys) to ensure robust error handling.

Tests/AblyChatTests/DefaultRoomTypingTests.swift (1)

295-300: Pending implementation for multiple presence events.
This is marked TODO. If needed, we can offer a test approach to ensure only the most recent event is dispatched.

Tests/AblyChatTests/Mocks/MockConnection.swift (2)

71-80: Fix SwiftLint style issues with multiline parameters/arguments

The implementation of transitionToState is good, but there are some style issues flagged by SwiftLint.

-func transitionToState(_ state: ARTRealtimeConnectionState,
-                           event: ARTRealtimeConnectionEvent,
-                           error: ARTErrorInfo? = nil) {
-        let stateChange = ARTConnectionStateChange(current: state,
-                                                   previous: self.state,
-                                                   event: event,
-                                                   reason: error)
+func transitionToState(
+    _ state: ARTRealtimeConnectionState,
+    event: ARTRealtimeConnectionEvent,
+    error: ARTErrorInfo? = nil
+) {
+        let stateChange = ARTConnectionStateChange(
+            current: state,
+            previous: self.state,
+            event: event,
+            reason: error
+        )
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 75-75: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 78-78: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 72-72: Multiline parameters should have their surrounding brackets in a new line

(multiline_parameters_brackets)


[Error] 74-74: Multiline parameters should have their surrounding brackets in a new line

(multiline_parameters_brackets)


[Error] 71-71: Lines should not have trailing whitespace

(trailing_whitespace)


71-71: Remove trailing whitespace

There's trailing whitespace on this line.

-    
+
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 71-71: Lines should not have trailing whitespace

(trailing_whitespace)

Sources/AblyChat/DefaultConnection.swift (2)

110-110: Remove or properly comment the commented-out code

There's a commented line with an explanation that could be clearer or removed entirely.

-//            subscription.emit(statusChange) // this call shouldn't be here - "Not withstanding CHA-CS5a" means just that I guess.
+// Note: The emit call was removed to properly respect CHA-CS5a requirement about transient disconnect suppression

125-139: Fix SwiftLint attribute placement and whitespace issues

The debug-only functionality looks good, but there are style issues.

    #if DEBUG
        internal struct TransientDisconnectTimerEvent: Equatable {
            internal let active: Bool
        }
        /// Subscription of transient disconnect timer events for testing purposes.
-        @MainActor
-        private var transientDisconnectTimerSubscriptions: [Subscription<TransientDisconnectTimerEvent>] = []
+        @MainActor private var transientDisconnectTimerSubscriptions: [Subscription<TransientDisconnectTimerEvent>] = []

        /// Returns a subscription which emits transient disconnect timer events for testing purposes.
        @MainActor
        internal func testsOnly_subscribeToTransientDisconnectTimerEvents() -> Subscription<TransientDisconnectTimerEvent> {
            let subscription = Subscription<TransientDisconnectTimerEvent>(bufferingPolicy: .unbounded)
            transientDisconnectTimerSubscriptions.append(subscription)
            return subscription
        }
-    #endif
+    #endif
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 130-130: Attributes should be on their own lines in functions and types, but on the same line as variables and imports

(attributes)

Tests/AblyChatTests/DefaultConnectionTests.swift (2)

41-44: Fix bracket placement in multiline argument lists.

The static analysis detected multiline argument bracket placement issues. Consider updating these to follow Swift style guidelines.

-        subscription.emit(.init(current: .disconnected,
-                                previous: .connecting,
-                                error: connectionError,
-                                retryIn: 1)) // arbitrary values
+        subscription.emit(.init(
+            current: .disconnected,
+            previous: .connecting,
+            error: connectionError,
+            retryIn: 1
+        )) // arbitrary values

Also applies to: 88-91, 137-140

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 41-41: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 44-44: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


65-65: Consider avoiding force casts.

The static analysis flagged force casts to DefaultConnection. While these are likely safe in test code, consider using as? with a guard statement or assertion for better safety and to follow best practices.

-        let defaultConnection = client.connection as! DefaultConnection
+        guard let defaultConnection = client.connection as? DefaultConnection else {
+            XCTFail("Expected DefaultConnection instance")
+            return
+        }

Also applies to: 114-114, 160-160, 198-198, 235-235

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 65-65: Force casts should be avoided

(force_cast)

Tests/AblyChatTests/Mocks/MockRealtimePresence.swift (4)

4-8: Consider documenting the @unchecked Sendable usage

The @unchecked Sendable annotation indicates you're guaranteeing thread safety without compiler verification. It would be helpful to add a comment explaining why this is safe in this context, similar to how it's documented in the MockRealtimeChannel class.


53-56: Force unwrapping could lead to crashes

The currentMember! force unwrapping might crash if there's an issue with member creation. Consider using optional chaining or guard statements to handle the case where currentMember might be nil.

-    func enterClient(_ clientId: String, data: Any?) {
-        currentMember = ARTPresenceMessage(clientId: clientId, data: data)
-        members.append(currentMember!)
-        currentMember!.action = .enter
-        subscribeCallback?(currentMember!)
+    func enterClient(_ clientId: String, data: Any?) {
+        currentMember = ARTPresenceMessage(clientId: clientId, data: data)
+        guard let member = currentMember else { return }
+        members.append(member)
+        member.action = .enter
+        subscribeCallback?(member)

59-65: Force unwrapping could lead to crashes

Similar to the previous method, this implementation also contains force unwrapping that could be made safer.

-    func enterClient(_ clientId: String, data: Any?, callback: ARTCallback? = nil) {
-        currentMember = ARTPresenceMessage(clientId: clientId, data: data)
-        members.append(currentMember!)
-        callback?(nil)
-        currentMember!.action = .enter
-        subscribeCallback?(currentMember!)
+    func enterClient(_ clientId: String, data: Any?, callback: ARTCallback? = nil) {
+        currentMember = ARTPresenceMessage(clientId: clientId, data: data)
+        guard let member = currentMember else {
+            callback?(ARTErrorInfo.create(withCode: 0, message: "Failed to create presence message"))
+            return
+        }
+        members.append(member)
+        callback?(nil)
+        member.action = .enter
+        subscribeCallback?(member)

26-32: Consider implementing unimplemented methods

Several methods throw fatalError("Not implemented"). While this works for the current tests, it could lead to unexpected crashes if any tests call these methods in the future. Consider implementing basic functionality or at least providing stubs that don't crash.

Also applies to: 43-50, 110-118

Tests/AblyChatTests/DefaultMessagesTests.swift (1)

358-359: Incorrect spec reference in the comment

The comment indicates the spec reference should be CHA-M4k but the code uses CHA-M5k. This should be corrected to maintain consistency with the rest of the documentation.

-    // Wrong name, should be CHA-M4k
-    // @spec CHA-M5k
+    // @spec CHA-M4k
Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (2)

137-139: Incorrect error message in performStateDetachCallbacks

The error message refers to "attachResult" when it should refer to "detachResult".

-            fatalError("attachResult must be set before attach is called")
+            fatalError("detachResult must be set before detach is called")

200-217: Potentially unsafe type casting in JSON parsing

The JSON message parsing contains several unsafe operations, including force unwrapping of optional values and unchecked type casting. Consider adding more robust error handling to prevent potential runtime crashes.

             if let ts = json["timestamp"] as? String {
-                message.timestamp = Date(timeIntervalSince1970: TimeInterval(ts)!)
+                if let timeInterval = TimeInterval(ts) {
+                    message.timestamp = Date(timeIntervalSince1970: timeInterval)
+                }
             }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb01d60 and 37a0d7a.

📒 Files selected for processing (21)
  • AblyChat-Package.xctestplan (1 hunks)
  • Sources/AblyChat/DefaultConnection.swift (4 hunks)
  • Sources/AblyChat/DefaultMessages.swift (4 hunks)
  • Sources/AblyChat/DefaultRoomReactions.swift (2 hunks)
  • Sources/AblyChat/DefaultTyping.swift (5 hunks)
  • Sources/AblyChat/Dependencies.swift (1 hunks)
  • Sources/AblyChat/RoomFeature.swift (1 hunks)
  • Tests/AblyChatTests/ChatAPITests.swift (0 hunks)
  • Tests/AblyChatTests/DefaultConnectionTests.swift (1 hunks)
  • Tests/AblyChatTests/DefaultMessagesTests.swift (5 hunks)
  • Tests/AblyChatTests/DefaultRoomOccupancyTests.swift (1 hunks)
  • Tests/AblyChatTests/DefaultRoomPresenceTests.swift (1 hunks)
  • Tests/AblyChatTests/DefaultRoomReactionsTests.swift (1 hunks)
  • Tests/AblyChatTests/DefaultRoomTypingTests.swift (1 hunks)
  • Tests/AblyChatTests/Helpers/Helpers.swift (1 hunks)
  • Tests/AblyChatTests/Mocks/MockConnection.swift (4 hunks)
  • Tests/AblyChatTests/Mocks/MockHTTPPaginatedResponse.swift (4 hunks)
  • Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (8 hunks)
  • Tests/AblyChatTests/Mocks/MockRealtimePresence.swift (1 hunks)
  • Tests/AblyChatTests/Mocks/MockRoomLifecycleContributor.swift (2 hunks)
  • Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift (7 hunks)
💤 Files with no reviewable changes (1)
  • Tests/AblyChatTests/ChatAPITests.swift
✅ Files skipped from review due to trivial changes (1)
  • AblyChat-Package.xctestplan
🧰 Additional context used
🪛 SwiftLint (0.57.0)
Tests/AblyChatTests/Mocks/MockConnection.swift

[Error] 75-75: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 78-78: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 72-72: Multiline parameters should have their surrounding brackets in a new line

(multiline_parameters_brackets)


[Error] 74-74: Multiline parameters should have their surrounding brackets in a new line

(multiline_parameters_brackets)


[Error] 71-71: Lines should not have trailing whitespace

(trailing_whitespace)

Sources/AblyChat/DefaultConnection.swift

[Error] 130-130: Attributes should be on their own lines in functions and types, but on the same line as variables and imports

(attributes)


[Error] 140-140: Don't include vertical whitespace (empty line) before closing braces

(vertical_whitespace_closing_braces)

Tests/AblyChatTests/DefaultConnectionTests.swift

[Error] 65-65: Force casts should be avoided

(force_cast)


[Error] 114-114: Force casts should be avoided

(force_cast)


[Error] 160-160: Force casts should be avoided

(force_cast)


[Error] 198-198: Force casts should be avoided

(force_cast)


[Error] 235-235: Force casts should be avoided

(force_cast)


[Error] 41-41: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 44-44: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 88-88: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 91-91: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 137-137: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 140-140: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 14-14: Lines should not have trailing whitespace

(trailing_whitespace)


[Error] 18-18: Lines should not have trailing whitespace

(trailing_whitespace)


[Error] 6-6: Don't include vertical whitespace (empty line) after opening braces

(vertical_whitespace_opening_braces)

🔇 Additional comments (57)
Sources/AblyChat/Dependencies.swift (1)

28-28: New protocol added for presence functionality.

The new RealtimePresenceProtocol appears to be properly defined, conforming to both ARTRealtimePresenceProtocol and Sendable. This follows the existing pattern of other protocols in this file and prepares the groundwork for connection testing.

Tests/AblyChatTests/Mocks/MockRoomLifecycleContributor.swift (2)

4-4: Added EmitsDiscontinuities protocol conformance.

Correctly updated the MockRoomLifecycleContributor to conform to both protocols, which aligns with the changes in RoomFeature.swift.


19-21: Implementation needed for onDiscontinuity method.

The fatalError implementation is appropriate for mocks that don't expect this method to be called during tests. However, if you plan to test connection discontinuities, you'll need to implement this method properly.

Do you plan to test this functionality? If so, consider implementing a proper mock that returns a configurable subscription instead of using fatalError.

Sources/AblyChat/RoomFeature.swift (1)

66-66: Good refactoring to use protocol composition.

Changing from a concrete type to any RoomLifecycleContributor & EmitsDiscontinuities improves flexibility and testability. This allows for easier mocking in tests while maintaining the required functionality.

Sources/AblyChat/DefaultRoomReactions.swift (2)

81-85: Added debugging support for malformed messages.

Good enhancement for testing - emitting malformed messages to special test subscriptions while properly guarding with #if DEBUG to keep this out of production code.


106-116: Well-designed test functionality for malformed messages.

The implementation for test subscriptions is clean and follows best practices:

  • Clear naming with testsOnly_ prefix
  • Proper documentation comments
  • Conditional compilation with #if DEBUG
  • Simple implementation that integrates with the existing error handling

This will be valuable for connection testing scenarios.

Tests/AblyChatTests/Mocks/MockHTTPPaginatedResponse.swift (3)

13-13: Ensure consistent usage of the hasNext flag in all invocations.
Removing the isLast parameter simplifies the constructor. Just confirm that places expecting the old parameter have been updated accordingly.


117-118: Confirm that hasNext: true matches pagination expectations.
Defining hasNext: true here is logical if the test expects subsequent pages. This looks correct.


128-149: Validate mock responses for next-page items.
The structure of next-page items (clientId, serial, action, text) appears consistent. Just ensure these item fields align with any real or expected data schema in dependent tests.

Tests/AblyChatTests/DefaultRoomReactionsTests.swift (1)

46-77: Test name and logic readability improvements.
Renaming the method to subscriptionCanBeRegisteredToReceiveReactionEvents() is more descriptive and aligns well with the tested functionality. The concurrency usage appears correct.

Tests/AblyChatTests/DefaultRoomTypingTests.swift (11)

1-4: Imports and test struct setup.
The initial setup is straightforward. Marking the test struct with @testable import AblyChat is appropriate.


5-21: Check concurrency usage in retrieveCurrentlyTypingClientIDs.
This test method is clearly organized and uses async/await correctly to fetch typing client IDs.


23-52: Attach-state synchronization looks good.
Waiting for the ATTACHING state before calling get() ensures proper lifecycle sequencing. Great approach.


54-95: Robust error handling for attach failures.
Catching and verifying the attach error is handled well. Confirm that non-attach errors also propagate as expected.


97-118: Validate presence operation preconditions.
Correctly throws an error if the room is in an invalid state. This aligns with the specification for presence operations.


120-149: Timeout configuration for typing events.
The test effectively confirms that a shorter timeout triggers a stop event promptly. Nice approach with a tolerance to handle CI timing fluctuations.


151-189: Starting and stopping typing is clearly tested.
Ensuring idempotency when stopping typing multiple times is beneficial. Great coverage.


191-217: Extending typing timeout logic is verified.
The re-invocation of start() resets the timer. The test checks the final stop time accurately.


219-237: Typing event subscription is validated.
This covers the main success path for receiving typing updates. Looks solid.


239-293: Presence event triggers presence.get()
Good verification that the presence retrieval is invoked upon presence events. The backoff approach is tested thoroughly.


301-319: Discontinuity coverage.
Verifying that a discontinuity is propagated ensures resilience and consistency across feature channels.

Tests/AblyChatTests/Mocks/MockConnection.swift (4)

4-4: Good use of @unchecked Sendable to support concurrency

Adding @unchecked Sendable to the class declaration is appropriate since this mock class will be used in concurrent contexts during testing.


17-17: Good addition of the stateCallback property

This property is essential for storing the connection state change callback and will enable mock state transitions in tests.


47-50: LGTM: Properly implemented callback registration

The implementation correctly stores the callback and returns an event listener, which is necessary for simulating state changes in tests.


64-66: Callback cleanup is implemented correctly

Setting the callback to nil when unsubscribing is the right approach.

Sources/AblyChat/DefaultMessages.swift (3)

9-11: Good use of conditional conformance to Sendable for ARTMessage

Using @retroactive @unchecked Sendable for ARTMessage in DEBUG builds enables better testing while keeping it isolated to non-production code.


61-116: Improved error handling with structured do-catch

Refactoring the error handling to use a structured do-catch block makes the code more maintainable and enables proper handling of malformed messages.

The addition of emitting malformed messages to debug subscriptions is a good approach for testing purposes.


135-145: Good implementation of test-only debugging functionality

The DEBUG-conditional implementation of malformed message subscriptions provides valuable testing capabilities without affecting production code.

Tests/AblyChatTests/DefaultRoomOccupancyTests.swift (3)

5-31: Well-structured test for occupancy information retrieval

This test correctly validates the ability to fetch occupancy information with proper mocking and assertions.

The test clearly follows the Given-When-Then pattern and includes references to specifications via @spec annotations.


33-54: Good test for real-time occupancy subscription

This test effectively validates that users can subscribe to real-time occupancy updates, which is an important part of the connection testing functionality.


56-74: Thorough test for discontinuity event handling

The test properly validates that discontinuity events are propagated correctly through the occupancy system.

Sources/AblyChat/DefaultConnection.swift (4)

62-66: Good implementation of debug event emission for disconnect timer cancellation

Emitting debug events when the transient disconnect timer is canceled improves testability.


75-79: Proper debug event for timer activation

Emitting debug events when the transient disconnect timer starts allows for better testing of connection state transitions.


84-88: Consistent timer expiry debug event emission

The debug event for timer expiry properly notifies subscribers that the timer is no longer active.


101-105: Complete coverage of timer cancellation scenarios

All scenarios where the timer could be canceled are properly covered with debug events.

Tests/AblyChatTests/Mocks/MockRoomLifecycleContributorChannel.swift (3)

8-8: Great enhancement for mocking network interactions.

The addition of the underlyingChannel property and related initialization parameters allows for more realistic testing by connecting mock objects in a hierarchy. This provides better simulation capabilities for real-world network scenarios.

Also applies to: 18-25, 51-51


98-101: Nice addition of network delay simulation capability.

The simulated network delay feature with configurable milliseconds is a great enhancement for testing timeout and race conditions. This allows for more realistic testing of asynchronous behavior.


131-133: Proper state change propagation.

Using a separate Task to perform the state change callbacks on the underlying channel ensures that the state propagation happens asynchronously, which better simulates real-world behavior. This is a good pattern for handling event propagation in testing code.

Sources/AblyChat/DefaultTyping.swift (4)

9-9: Good improvement: Parameterized retry duration.

Making the maximum presence get retry duration configurable rather than hardcoded increases flexibility and testability of the component. This aligns well with the spec requirement (CHA-T6c1).

Also applies to: 12-12, 18-18


43-43: Updated retry logic to use configurable parameter.

The retry loop now correctly uses the configurable parameter instead of a hardcoded value, and the log message has been updated to reflect this change. This enhances consistency and makes the code more maintainable.

Also applies to: 82-82


47-51: Well-structured test event emission.

The introduction of test event subscriptions with proper DEBUG conditional compilation directives is a good approach. These event points will allow for comprehensive testing without affecting production code.

Also applies to: 75-79, 173-177, 227-231


237-282: Comprehensive test infrastructure.

The addition of test-specific structures and subscription mechanisms, all properly isolated with DEBUG directives, demonstrates good separation of concerns between production and test code. The @unchecked Sendable extension is appropriately guarded to prevent production impact.

Also applies to: 285-287

Tests/AblyChatTests/Helpers/Helpers.swift (4)

25-32: Useful convenience initializer for test data.

This convenience initializer for ARTPresenceMessage simplifies test code by reducing boilerplate when creating test instances. Good choice of defaults for optional parameters.


34-41: Handy shortcut for comprehensive presence event testing.

The static .all property provides a convenient way to access all presence event types, making it easier to write exhaustive tests that cover all possible events.


43-93: Excellent test helper implementation.

The RoomLifecycleHelper enum provides a comprehensive set of factory methods and utilities that will significantly reduce test code duplication and improve maintainability. The comment on line 85 about replacing duplicates elsewhere should be addressed in a future PR.

Is there an issue tracking the TODO on line 85 to replace duplicates of the waitForManager function elsewhere in the codebase?


95-99: Useful numeric comparison extension.

This extension on Double adds a tolerance-based equality check, which is essential for reliable floating-point comparisons in tests, especially when dealing with time-based operations.

Tests/AblyChatTests/DefaultConnectionTests.swift (5)

26-55: Well-structured test for connection status observation.

The test correctly verifies that clients can register for connection status updates and that the emitted status change contains the expected information as per spec requirements CHA-CS4a, CHA-CS4b, and CHA-CS4c.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 41-41: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 44-44: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


57-105: Good test for transient disconnect timer behavior.

This test properly validates that when connection transitions from connected to disconnected, a transient disconnect timer is initiated (CHA-CS5a1) and that the appropriate status changes are emitted upon timer expiration (CHA-CS5a4).

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 65-65: Force casts should be avoided

(force_cast)


[Error] 88-88: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 91-91: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


107-151: Thorough test for status change during connecting state.

The test effectively validates spec requirement CHA-CS5a2, confirming that no status change is emitted when connection goes from disconnected to connecting while a transient disconnect timer is active.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 114-114: Force casts should be avoided

(force_cast)


[Error] 137-137: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 140-140: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


153-189: Comprehensive tests for various connection state transitions.

These tests thoroughly validate spec requirement CHA-CS5a3 for different scenarios (connected, suspended, failed), ensuring that the transient disconnect timer is canceled and appropriate status changes are emitted as expected.

Also applies to: 191-227, 228-264

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 160-160: Force casts should be avoided

(force_cast)


5-24:

✅ Verification successful

Comprehensive connection status tests with good spec references.

The test properly validates the initial connection status exposure (CHA-CS2a, CHA-CS2b, CHA-CS3) with clear expectations. The TODO comment on line 21 correctly identifies a potential issue that needs to be revisited.

The test references GitHub issue #49. Please check if this issue is still open and relevant to the TODO comment on line 21:


🏁 Script executed:

#!/bin/bash
gh issue view 49 --repo ably/ably-chat-swift

Length of output: 1572


Attention: Confirmed Concurrency Issue Relevance

The test correctly verifies the initial connection status (and error) as expected, given the asynchronous firing of actor events. Our check confirmed that GitHub issue #49 ("Address concurrency-related issues not addressed by feature spec") is still open—which reinforces the validity of the TODO comment on line 21. No immediate changes are required, but please be aware that this area should be revisited when addressing the concurrency concerns discussed in the issue.

  • Verified that issue #49 remains open and relevant.
  • The asynchronous nature of DefaultConnection is acknowledged, and the test expectations are appropriate for now.
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 14-14: Lines should not have trailing whitespace

(trailing_whitespace)


[Error] 18-18: Lines should not have trailing whitespace

(trailing_whitespace)


[Error] 6-6: Don't include vertical whitespace (empty line) after opening braces

(vertical_whitespace_opening_braces)

Tests/AblyChatTests/DefaultRoomPresenceTests.swift (1)

5-486: Well-structured comprehensive test suite for presence functionality!

This test file thoroughly covers all the necessary presence-related functionality in a chat room context, with clear test cases for entering, leaving, updating presence states, and handling error conditions. The test structure consistently follows the Given/When/Then pattern, making the tests readable and maintainable.

Tests/AblyChatTests/DefaultMessagesTests.swift (3)

218-219: Review iOS/tvOS version constraints

The @available(iOS 16.0.0, tvOS 16.0.0, *) annotations limit these tests to newer OS versions. Consider if there's a way to restructure these tests to support earlier OS versions, especially if your app targets those versions.

Also applies to: 275-276


13-24: Tests have been properly updated to use more specific mocks

The tests now use more specific mock implementations and have better validation of expected behavior, which improves the reliability of the test suite.


317-320: Document untestable specs

The comments indicate some specs are currently untestable. Consider creating a dedicated section in your documentation listing these cases and explaining why they can't be tested, to ensure they're not overlooked in the future.

Tests/AblyChatTests/Mocks/MockRealtimeChannel.swift (2)

8-9: Good improvement in mocking presence functionality

The addition of the mockPresence property and implementation of the presence property is a significant improvement, replacing a fatalError with actual functionality that can be used in tests.

Also applies to: 13-14


117-163: Well-structured state management refactoring

The refactoring of state management with the addition of callback handling methods improves the maintainability and clarity of the code.

@maratal maratal force-pushed the 124-connection-tests branch from 37a0d7a to acb8dfd Compare March 2, 2025 16:09
@maratal maratal changed the base branch from main to 86-messages-tests March 2, 2025 16:10
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
Tests/AblyChatTests/Mocks/MockConnection.swift (1)

71-80: Fix formatting issues in the new transitionToState method.

The method provides a crucial functionality for testing connection state transitions, but there are some formatting issues flagged by SwiftLint.

Apply these formatting fixes to follow the project style:

-    
-    func transitionToState(_ state: ARTRealtimeConnectionState,
-                           event: ARTRealtimeConnectionEvent,
-                           error: ARTErrorInfo? = nil) {
-        let stateChange = ARTConnectionStateChange(current: state,
-                                                   previous: self.state,
-                                                   event: event,
-                                                   reason: error)
-        stateCallback?(stateChange)
-    }
+    func transitionToState(
+        _ state: ARTRealtimeConnectionState,
+        event: ARTRealtimeConnectionEvent,
+        error: ARTErrorInfo? = nil
+    ) {
+        let stateChange = ARTConnectionStateChange(
+            current: state,
+            previous: self.state,
+            event: event,
+            reason: error
+        )
+        stateCallback?(stateChange)
+    }
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 75-75: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 78-78: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 72-72: Multiline parameters should have their surrounding brackets in a new line

(multiline_parameters_brackets)


[Error] 74-74: Multiline parameters should have their surrounding brackets in a new line

(multiline_parameters_brackets)


[Error] 71-71: Lines should not have trailing whitespace

(trailing_whitespace)

Tests/AblyChatTests/DefaultConnectionTests.swift (2)

5-6: Good test structure setup - consider removing extra blank line.

The test struct is well organized with clear spacing between test methods, but there's an extra blank line after the opening brace that could be removed according to SwiftLint.

 struct DefaultConnectionTests {
-
     // @spec CHA-CS2a
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 6-6: Don't include vertical whitespace (empty line) after opening braces

(vertical_whitespace_opening_braces)


65-65: Consider avoiding force casts.

Force casts (as!) are used multiple times to access the DefaultConnection implementation. While this works in a controlled test environment, consider using a safer approach.

-        let defaultConnection = client.connection as! DefaultConnection
+        guard let defaultConnection = client.connection as? DefaultConnection else {
+            XCTFail("Expected DefaultConnection instance")
+            return
+        }

Alternatively, if you're certain about the type and want to keep the tests concise, add a helper function to the test struct that encapsulates this pattern.

Also applies to: 114-114, 160-160, 198-198, 235-235

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 65-65: Force casts should be avoided

(force_cast)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37a0d7a and acb8dfd.

📒 Files selected for processing (3)
  • Sources/AblyChat/DefaultConnection.swift (4 hunks)
  • Tests/AblyChatTests/DefaultConnectionTests.swift (1 hunks)
  • Tests/AblyChatTests/Mocks/MockConnection.swift (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/AblyChat/DefaultConnection.swift
🧰 Additional context used
🪛 SwiftLint (0.57.0)
Tests/AblyChatTests/DefaultConnectionTests.swift

[Error] 65-65: Force casts should be avoided

(force_cast)


[Error] 114-114: Force casts should be avoided

(force_cast)


[Error] 160-160: Force casts should be avoided

(force_cast)


[Error] 198-198: Force casts should be avoided

(force_cast)


[Error] 235-235: Force casts should be avoided

(force_cast)


[Error] 41-41: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 44-44: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 88-88: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 91-91: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 137-137: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 140-140: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 14-14: Lines should not have trailing whitespace

(trailing_whitespace)


[Error] 18-18: Lines should not have trailing whitespace

(trailing_whitespace)


[Error] 6-6: Don't include vertical whitespace (empty line) after opening braces

(vertical_whitespace_opening_braces)

Tests/AblyChatTests/Mocks/MockConnection.swift

[Error] 75-75: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 78-78: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 72-72: Multiline parameters should have their surrounding brackets in a new line

(multiline_parameters_brackets)


[Error] 74-74: Multiline parameters should have their surrounding brackets in a new line

(multiline_parameters_brackets)


[Error] 71-71: Lines should not have trailing whitespace

(trailing_whitespace)

🔇 Additional comments (12)
Tests/AblyChatTests/Mocks/MockConnection.swift (4)

4-4: Good addition of Sendable conformance.

Adding @unchecked Sendable conformance allows this mock to be safely used across concurrency domains in the tests. This aligns well with the PR's objective of implementing connection tests.


17-17: LGTM - well-encapsulated callback mechanism.

The addition of this private property provides a clean way to store the connection state callback.


47-50: Good implementation of the on callback method.

This implementation properly stores the callback and returns an event listener, making the mock more functional for testing.


65-65: LGTM - properly clears the callback.

Appropriate implementation to remove the callback when off is called.

Tests/AblyChatTests/DefaultConnectionTests.swift (8)

11-24: Well-structured test for basic connection status.

This test thoroughly verifies that the chat client correctly exposes its initial connection status. Good use of comments to explain the test's Given-When-Then structure and specification references.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 14-14: Lines should not have trailing whitespace

(trailing_whitespace)


[Error] 18-18: Lines should not have trailing whitespace

(trailing_whitespace)


32-55: Good test for connection status observation.

The test effectively verifies that clients can register for connection status events and receive updates with the correct information. The use of #require makes failure cases clear.

Fix the multiline formatting for better style consistency:

-        subscription.emit(.init(current: .disconnected,
-                                previous: .connecting,
-                                error: connectionError,
-                                retryIn: 1)) // arbitrary values
+        subscription.emit(.init(
+            current: .disconnected,
+            previous: .connecting,
+            error: connectionError,
+            retryIn: 1
+        )) // arbitrary values
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 41-41: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 44-44: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


60-105: Thorough test for transient disconnect timer behavior.

Excellent test implementation that verifies when a connection goes from connected to disconnected, the transient disconnect timer starts appropriately. The test contains clear sections and validations.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 65-65: Force casts should be avoided

(force_cast)


[Error] 88-88: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 91-91: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


109-151: Good test for verifying no status change during specific transitions.

This test effectively verifies that no status change is emitted when the connection goes from disconnected to connecting while a transient disconnect timer is active.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 114-114: Force casts should be avoided

(force_cast)


[Error] 137-137: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 140-140: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


155-189: Well-structured test for connection status transitioning to connected.

This test properly verifies that when the connection transitions to connected state, the transient disconnect timer is canceled and the correct status change is emitted.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 160-160: Force casts should be avoided

(force_cast)


193-226: Good test for connection status transitioning to suspended.

This test properly verifies that when the connection transitions to suspended state, the transient disconnect timer is canceled and the correct status change is emitted.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 198-198: Force casts should be avoided

(force_cast)


230-264: Well-implemented test for connection status transitioning to failed.

This test thoroughly validates that when the connection transitions to failed state, the transient disconnect timer is canceled and the correct status change with error information is emitted.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 235-235: Force casts should be avoided

(force_cast)


1-265: Overall excellent test implementation for connection state handling.

This comprehensive test suite thoroughly covers the connection status requirements specified in the PR objectives. The tests methodically verify different connection state transitions and their effects on the transient disconnect timer. The tests are well-structured with clear comments referencing specifications.

Consider addressing the formatting issues throughout the file for multiline argument brackets and the force casts, but otherwise, this is a solid implementation.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 65-65: Force casts should be avoided

(force_cast)


[Error] 114-114: Force casts should be avoided

(force_cast)


[Error] 160-160: Force casts should be avoided

(force_cast)


[Error] 198-198: Force casts should be avoided

(force_cast)


[Error] 235-235: Force casts should be avoided

(force_cast)


[Error] 41-41: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 44-44: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 88-88: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 91-91: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 137-137: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 140-140: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 14-14: Lines should not have trailing whitespace

(trailing_whitespace)


[Error] 18-18: Lines should not have trailing whitespace

(trailing_whitespace)


[Error] 6-6: Don't include vertical whitespace (empty line) after opening braces

(vertical_whitespace_opening_braces)

@maratal maratal force-pushed the 124-connection-tests branch from acb8dfd to 7db847b Compare March 2, 2025 16:13
@github-actions github-actions bot temporarily deployed to staging/pull/231/AblyChat March 2, 2025 16:15 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/231/AblyChat March 2, 2025 16:24 Inactive
@maratal maratal force-pushed the 124-connection-tests branch from 07c17e5 to 7db847b Compare March 2, 2025 16:27
@github-actions github-actions bot temporarily deployed to staging/pull/231/AblyChat March 2, 2025 16:28 Inactive
@maratal maratal force-pushed the 124-connection-tests branch from 7db847b to a749b70 Compare March 2, 2025 16:46
@github-actions github-actions bot temporarily deployed to staging/pull/231/AblyChat March 2, 2025 16:48 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
Sources/AblyChat/DefaultConnection.swift (2)

125-132: Move the @mainactor attribute to its own line.

SwiftLint is flagging an issue with the attribute placement.

-    @MainActor
-    private var transientDisconnectTimerSubscriptions: [Subscription<TransientDisconnectTimerEvent>] = []
+    @MainActor
+    private var transientDisconnectTimerSubscriptions: [Subscription<TransientDisconnectTimerEvent>] = []
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 132-132: Attributes should be on their own lines in functions and types, but on the same line as variables and imports

(attributes)


125-141: New debug events mechanism enhances testability.

The new TransientDisconnectTimerEvent structure and subscription system provides a clean way to test transient disconnect timer behavior, which is properly limited to DEBUG builds only.

However, there's a code style issue with the @MainActor attribute placement:

-    @MainActor
-    private var transientDisconnectTimerSubscriptions: [Subscription<TransientDisconnectTimerEvent>] = []
+    @MainActor private var transientDisconnectTimerSubscriptions: [Subscription<TransientDisconnectTimerEvent>] = []
🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 132-132: Attributes should be on their own lines in functions and types, but on the same line as variables and imports

(attributes)

Tests/AblyChatTests/DefaultConnectionTests.swift (4)

64-64: Consider using conditional unwrapping instead of force casting.

There are multiple instances of force casting with as! which could be replaced with conditional unwrapping for safer code. While this may be acceptable in tests, it's generally better to use safer alternatives.

For example:

-let defaultConnection = client.connection as! DefaultConnection
+guard let defaultConnection = client.connection as? DefaultConnection else {
+    #fail("Expected DefaultConnection type")
+    return
+}

Also applies to: 113-113, 159-159, 197-197, 234-234

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 64-64: Force casts should be avoided

(force_cast)


40-43: Fix multiline arguments brackets formatting.

SwiftLint is flagging issues with the multiline arguments formatting. Consider reformatting to match the project style guidelines.

For example:

-subscription.emit(.init(current: .disconnected,
-                        previous: .connecting,
-                        error: connectionError,
-                        retryIn: 1)) // arbitrary values
+subscription.emit(
+    .init(
+        current: .disconnected,
+        previous: .connecting,
+        error: connectionError,
+        retryIn: 1
+    )
+) // arbitrary values

Also applies to: 87-90, 136-139

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 40-40: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 43-43: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


64-64: Consider avoiding force casts in tests.

The tests use force casts (as!) to access the DefaultConnection instance. While this is common in test code, it's generally better to use conditional casts with appropriate error handling.

-let defaultConnection = client.connection as! DefaultConnection
+guard let defaultConnection = client.connection as? DefaultConnection else {
+    throw TestError("Expected connection to be DefaultConnection")
+}

Also applies to: 113-113, 159-159, 197-197, 234-234

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 64-64: Force casts should be avoided

(force_cast)


40-43: Fix multiline arguments formatting.

The multiline arguments in these method calls should have their surrounding brackets on new lines according to the style guidelines:

-subscription.emit(.init(current: .disconnected,
-                       previous: .connecting,
-                       error: connectionError,
-                       retryIn: 1)) // arbitrary values
+subscription.emit(
+    .init(
+        current: .disconnected,
+        previous: .connecting,
+        error: connectionError,
+        retryIn: 1
+    )
+) // arbitrary values

Also applies to: 87-90, 136-139

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 40-40: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 43-43: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between acb8dfd and a749b70.

📒 Files selected for processing (3)
  • Sources/AblyChat/DefaultConnection.swift (4 hunks)
  • Tests/AblyChatTests/DefaultConnectionTests.swift (1 hunks)
  • Tests/AblyChatTests/Mocks/MockConnection.swift (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Tests/AblyChatTests/Mocks/MockConnection.swift
🧰 Additional context used
🪛 SwiftLint (0.57.0)
Tests/AblyChatTests/DefaultConnectionTests.swift

[Error] 64-64: Force casts should be avoided

(force_cast)


[Error] 113-113: Force casts should be avoided

(force_cast)


[Error] 159-159: Force casts should be avoided

(force_cast)


[Error] 197-197: Force casts should be avoided

(force_cast)


[Error] 234-234: Force casts should be avoided

(force_cast)


[Error] 40-40: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 43-43: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 87-87: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 90-90: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 136-136: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 139-139: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)

Sources/AblyChat/DefaultConnection.swift

[Error] 132-132: Attributes should be on their own lines in functions and types, but on the same line as variables and imports

(attributes)

⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: Example app, tvOS (Xcode 16)
  • GitHub Check: Example app, iOS (Xcode 16)
  • GitHub Check: Example app, macOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, tvOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, iOS (Xcode 16)
  • GitHub Check: Xcode, tvOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, macOS (Xcode 16)
  • GitHub Check: Xcode, iOS (Xcode 16)
  • GitHub Check: SPM, release configuration (Xcode 16)
  • GitHub Check: Xcode, macOS (Xcode 16)
  • GitHub Check: SPM (Xcode 16)
🔇 Additional comments (18)
Sources/AblyChat/DefaultConnection.swift (5)

62-66: Good addition of debug events for the transient disconnect timer.

The code adds debug events to track when the transient disconnect timer is activated or deactivated, which improves testability without affecting production code.

Also applies to: 75-79, 84-88, 101-105


110-110: Removing unnecessary commented-out code improves readability.

Good cleanup of the commented-out code. The comment itself explains why this function call shouldn't be there.


125-141: Well-structured test-only functionality.

The addition of TransientDisconnectTimerEvent and the test-only subscription method is well-designed:

  1. It's properly encapsulated within #if DEBUG to keep it out of production code
  2. The method is clearly named with testsOnly_ prefix
  3. The subscription management is handled appropriately

This enables effective testing of timer state without affecting production code.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 132-132: Attributes should be on their own lines in functions and types, but on the same line as variables and imports

(attributes)


110-110: Removed commented code improves clarity.

Good cleanup of the commented-out line that was causing confusion. The comment correctly points out that "Not withstanding CHA-CS5a" doesn't imply emitting the status change here.


62-66: Well-placed debug events for comprehensive timer state tracking.

The new debug events are strategically placed at all timer state transition points, making it possible to track the complete lifecycle of the transient disconnect timer. Wrapping in #if DEBUG ensures these don't affect production code.

Also applies to: 75-79, 84-88, 101-105

Tests/AblyChatTests/DefaultConnectionTests.swift (13)

1-4: Good test setup with proper imports.

The test file has the proper imports, including the @testable import AblyChat to access internal components.


10-23: Well-structured test for CHA-CS2a, CHA-CS2b, and CHA-CS3 specifications.

The test properly verifies that the chat client exposes its current connection status and error. The comments provide good context about the expected behavior.


31-54: Good test for connection status observation.

This test properly verifies that clients can register for connection status events and receive them with the correct information.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 40-40: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 43-43: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


59-104: Comprehensive test for transient disconnect timer behavior.

This test thoroughly verifies the behavior when a connection goes from connected to disconnected, ensuring that:

  1. The transient disconnect timer starts correctly
  2. The timer duration is appropriate (5 seconds)
  3. The connection status and error information is handled correctly

The test demonstrates good use of the new timer event subscription functionality.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 64-64: Force casts should be avoided

(force_cast)


[Error] 87-87: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 90-90: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


108-150: Good test for CHA-CS5a2 specification.

This test properly verifies that no status change is emitted when the connection goes from disconnected to connecting while a transient disconnect timer is active.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 113-113: Force casts should be avoided

(force_cast)


[Error] 136-136: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 139-139: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


154-188: Good test for CHA-CS5a3 specification when connection goes to connected.

This test effectively verifies that the transient disconnect timer is canceled and the correct status change is emitted when the connection transitions to connected.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 159-159: Force casts should be avoided

(force_cast)


192-225: Good test for CHA-CS5a3 specification when connection goes to suspended.

This test properly verifies that the transient disconnect timer is canceled and the correct status change is emitted when the connection transitions to suspended.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 197-197: Force casts should be avoided

(force_cast)


229-263: Good test for CHA-CS5a3 specification when connection goes to failed.

This test thoroughly verifies that the transient disconnect timer is canceled and the correct status change (including error information) is emitted when the connection transitions to failed.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 234-234: Force casts should be avoided

(force_cast)


5-23: Comprehensive tests for connection status functionality.

The test verifies that the chat client correctly exposes its connection status as required by specs CHA-CS2a, CHA-CS2b, and CHA-CS3.

Note the TODO comment on line 20 correctly identifies that there might be timing issues due to the asynchronous nature of the initialization that should be revisited with issue #49.


25-54: Good test coverage for connection status observation.

This test ensures that clients can register listeners for connection status events and receive all required information in those events, covering specs CHA-CS4a through CHA-CS4d.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 40-40: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 43-43: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


56-104: Important timer state verification.

This test verifies that when connection transitions from connected to disconnected, the transient disconnect timer starts correctly and expires after 5 seconds as specified in CHA-CS5a1 and CHA-CS5a4.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 64-64: Force casts should be avoided

(force_cast)


[Error] 87-87: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 90-90: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


106-150: Proper verification of status change suppression.

The test confirms that when a transient disconnect timer is active, no status change is emitted if the connection changes to connecting or disconnected states, per spec CHA-CS5a2.

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 113-113: Force casts should be avoided

(force_cast)


[Error] 136-136: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


[Error] 139-139: Multiline arguments should have their surrounding brackets in a new line

(multiline_arguments_brackets)


152-188: Effective tests for timer cancellation scenarios.

These tests thoroughly verify that the transient disconnect timer is properly cancelled when the connection status changes to connected, suspended, or failed states, and that the appropriate status change events are emitted, per spec CHA-CS5a3.

Also applies to: 190-225, 227-263

🧰 Tools
🪛 SwiftLint (0.57.0)

[Error] 159-159: Force casts should be avoided

(force_cast)

@maratal maratal force-pushed the 124-connection-tests branch from a749b70 to 4e9f70f Compare March 2, 2025 16:59
@github-actions github-actions bot temporarily deployed to staging/pull/231/AblyChat March 2, 2025 17:01 Inactive
@maratal maratal force-pushed the 124-connection-tests branch from 4e9f70f to c4a2750 Compare March 2, 2025 19:25
@github-actions github-actions bot temporarily deployed to staging/pull/231/AblyChat March 2, 2025 19:26 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
Tests/AblyChatTests/DefaultConnectionTests.swift (3)

94-94: Potential flaky test with time-dependent assertion

This assertion depends on timing and assumes the timer will have expired after the preceding code completes. This could lead to occasional test failures if execution is faster than expected.

Consider using a more deterministic approach, such as mocking the timer or using a callback approach to verify timer completion. Alternatively, you could add a small buffer to account for timing inconsistencies:

- #expect(Date().timeIntervalSince1970 - timerStartedAt >= 5)
+ #expect(Date().timeIntervalSince1970 - timerStartedAt >= 4.9)  // slightly less than 5 to account for timing variations

86-88: Consider refactoring the artificial status change emission pattern

Several tests emit artificial status changes to unblock the test flow. While this works, it creates a somewhat convoluted test flow that might be difficult to understand.

Consider refactoring the status observation mechanism in tests to provide better control over the test flow. This could involve:

  1. Using a completion handler pattern
  2. Creating a more testable interface that doesn't require artificial events
  3. Implementing a test-specific mechanism to wait for status changes

179-179: Potential flaky assertion with strict timing check

The assertion #expect(Date().timeIntervalSince1970 - timerStartedAt < 1) could be flaky in slower execution environments.

Consider a more flexible approach:

- #expect(Date().timeIntervalSince1970 - timerStartedAt < 1)
+ #expect(Date().timeIntervalSince1970 - timerStartedAt < 2)  // Allow more time for slower execution environments
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a749b70 and c4a2750.

📒 Files selected for processing (3)
  • Sources/AblyChat/DefaultConnection.swift (4 hunks)
  • Tests/AblyChatTests/DefaultConnectionTests.swift (1 hunks)
  • Tests/AblyChatTests/Mocks/MockConnection.swift (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • Sources/AblyChat/DefaultConnection.swift
  • Tests/AblyChatTests/Mocks/MockConnection.swift
⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: Example app, tvOS (Xcode 16)
  • GitHub Check: Example app, iOS (Xcode 16)
  • GitHub Check: Example app, macOS (Xcode 16)
  • GitHub Check: Xcode, tvOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, tvOS (Xcode 16)
  • GitHub Check: Xcode, iOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, iOS (Xcode 16)
  • GitHub Check: Xcode, macOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, macOS (Xcode 16)
  • GitHub Check: SPM (Xcode 16)
  • GitHub Check: SPM, release configuration (Xcode 16)
🔇 Additional comments (4)
Tests/AblyChatTests/DefaultConnectionTests.swift (4)

1-262: Well-structured test suite for connection management

This new test suite provides comprehensive coverage for the DefaultChatClient connection functionality. The tests follow a clear "Given-When-Then" structure and include good documentation referencing specs.


21-21: Remember to address the TODO comment

There's a TODO comment referencing issue #49 that should be revisited once that issue is resolved.


58-101: Excellent specification-driven test coverage

The test provides detailed coverage of the transient disconnect timer behavior, ensuring it starts correctly when connection transitions from connected to disconnected and emits the expected status changes.


147-184: Good test for connection status transitions

This test thoroughly validates that the transient disconnect timer is canceled when the connection goes to connected state and that the correct status change is emitted.

@umair-ably
Copy link
Collaborator

just the one comment... also worth checking the failing tests (are they flaky or is it down to the parallel runs you mentioned in standup?)

@maratal
Copy link
Collaborator Author

maratal commented Mar 4, 2025

just the one comment... also worth checking the failing tests (are they flaky or is it down to the parallel runs you mentioned in standup?)

Sometimes they fail (up to 1/5 of the time), but I don't want to touch it while changes in that code are still ongoing. Without parallel run, yes, most likely they succeed.

@maratal maratal force-pushed the 86-messages-tests branch from 2aeac57 to 5bf2afd Compare March 4, 2025 19:40
@maratal maratal force-pushed the 124-connection-tests branch from c4a2750 to 36f77e5 Compare March 4, 2025 22:28
@github-actions github-actions bot temporarily deployed to staging/pull/231/AblyChat March 4, 2025 22:29 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
Tests/AblyChatTests/DefaultConnectionTests.swift (3)

58-101: Consider addressing potential test flakiness with timer-based assertions.

This test relies on an actual 5-second timer to complete, which could make it flaky in CI environments. The assertion on line 94 checks if time has passed, but doesn't directly verify that the timer fired correctly.

Consider refactoring the test to use a controlled time source or mock the timer functionality to make the test more reliable and faster. You could:

  1. Inject a mock timer that can be advanced programmatically
  2. Or use a dependency that allows controlling the timing without waiting

This would also reduce the test execution time significantly, as you wouldn't need to wait the full 5 seconds.


167-183: Improve time-dependent assertion reliability.

The assertion on line 179 checks if less than 1 second has passed, but it doesn't directly verify that the timer was canceled. This approach is time-dependent and could be flaky.

Instead of relying on elapsed time to verify timer cancellation, consider adding explicit cancellation detection. For example, check for a cancellation event or verify the timer's state directly.

-// The library shall cancel the transient disconnect timer (less than 5 seconds -> was cancelled)
-#expect(Date().timeIntervalSince1970 - timerStartedAt < 1)
+// Verify the transient disconnect timer was canceled
+let timerCancellationEvent = try #require(await transientTimerSubscription.first { !$0.active })
+#expect(!timerCancellationEvent.active)

5-262: Consider extracting common test setup to reduce duplication.

Several test methods share similar setup code for creating mock connections and subscriptions. This pattern is repeated across multiple tests.

Consider extracting the common setup into a helper method to reduce duplication and make the tests more maintainable:

private func setupTestEnvironment() async throws -> (
    MockConnection,
    DefaultChatClient,
    DefaultConnection,
    Subscription<TransientDisconnectTimerEvent>,
    Subscription<ConnectionStatusChange>
) {
    let realtimeConnection = MockConnection(state: .connected)
    let realtime = MockRealtime(createWrapperSDKProxyReturnValue: .init(connection: realtimeConnection))
    let client = DefaultChatClient(realtime: realtime, clientOptions: nil)
    let defaultConnection = try #require(client.connection as? DefaultConnection)
    
    let transientTimerSubscription = await defaultConnection.testsOnly_subscribeToTransientDisconnectTimerEvents()
    let statusSubscription = defaultConnection.onStatusChange()
    
    return (realtimeConnection, client, defaultConnection, transientTimerSubscription, statusSubscription)
}

Then use it in your tests:

@Test
func whenConnectionGoesToConnectedStatusChangeShouldBeEmitted() async throws {
    // Given:
    let (realtimeConnection, _, _, transientTimerSubscription, statusSubscription) = try await setupTestEnvironment()
    
    // When:
    let timerStartedAt = Date().timeIntervalSince1970
    realtimeConnection.transitionToState(.disconnected, event: .disconnected)
    // Rest of the test...
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4a2750 and 36f77e5.

📒 Files selected for processing (3)
  • Sources/AblyChat/DefaultConnection.swift (4 hunks)
  • Tests/AblyChatTests/DefaultConnectionTests.swift (1 hunks)
  • Tests/AblyChatTests/Mocks/MockConnection.swift (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • Tests/AblyChatTests/Mocks/MockConnection.swift
  • Sources/AblyChat/DefaultConnection.swift
⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: Example app, tvOS (Xcode 16)
  • GitHub Check: Example app, iOS (Xcode 16)
  • GitHub Check: Xcode, tvOS (Xcode 16)
  • GitHub Check: Example app, macOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, tvOS (Xcode 16)
  • GitHub Check: Xcode, iOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, iOS (Xcode 16)
  • GitHub Check: SPM (Xcode 16)
  • GitHub Check: Xcode, macOS (Xcode 16)
  • GitHub Check: Xcode, release configuration, macOS (Xcode 16)
  • GitHub Check: SPM, release configuration (Xcode 16)
🔇 Additional comments (9)
Tests/AblyChatTests/DefaultConnectionTests.swift (9)

26-26: Consider using the @specUntested annotation for clarity.

For consistency with other spec annotations, consider using the @specUntested annotation for the CHA-CS4e and CHA-CS4f specifications that are currently untestable.

-// CHA-CS4e, CHA-CS4f - currently untestable due to subscription is removed once the object is removed from memory
+// @specUntested CHA-CS4e, CHA-CS4f // Untestable due to subscription being removed once the object is removed from memory

94-94: Make timer verification more robust.

This assertion verifies that time has passed but doesn't directly confirm that the timer fired correctly. This could lead to false positives if the test is simply slow.

Consider adding an explicit verification that the timer completed as expected, perhaps by checking for a completion event or state change that's directly caused by the timer expiration.


1-4: LGTM - Appropriate imports.

The imports are appropriate for the test file, including the required framework imports and the testable import for the module under test.


5-9: Good use of spec annotations to document test coverage.

The spec annotations clearly link test cases to their corresponding specifications, which is excellent for traceability.


10-24: Well-structured initial test with clear Given-When-Then pattern.

This test effectively verifies the initial connection status and error state with good comments explaining the test flow.


32-53: Good test for connection status observation.

The test properly verifies that clients can register for and receive connection status updates with appropriate assertions for all required properties.


103-145: Comprehensive test for connected-to-disconnected transition.

This test effectively verifies that no status change is emitted when the connection goes from disconnected to connecting while a transient disconnect timer is active. The test is well-structured and includes appropriate assertions.


186-222: Robust test for suspension state transition.

The test properly verifies that the transient disconnect timer is canceled and the appropriate status change is emitted when the connection transitions to suspended.


224-261: Thorough test for connection failure handling.

This test properly verifies that the connection failure is handled correctly, with the timer being canceled and appropriate error information being included in the status update.

@maratal maratal force-pushed the 124-connection-tests branch from 36f77e5 to 3b2f333 Compare March 5, 2025 00:37
@github-actions github-actions bot temporarily deployed to staging/pull/231/AblyChat March 5, 2025 00:38 Inactive
Base automatically changed from 86-messages-tests to main March 10, 2025 13:46
Copy link
Collaborator

@umair-ably umair-ably left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have 2 flaking on my local branch when running them all concurrently, however they now always fail for the same reason (they seem to have attachSerial instead of channelSerial for the first serial validation checks we do across both tests)...

I've removed these assertions as we don't really care about the initial state of the fromSerial for the purpose of these tests (we only care they're reset appropriately to attachSerial - this part of the tests always works now)...

Happy for this to be merged and I'll follow up with mine shortly :)

Copy link
Collaborator

@lawrence-forooghian lawrence-forooghian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was surprised to find that we've been introducing flaky tests into the Chat unit tests. We need to address the existing flaky ones and not write more.

Anything that involves a timeout is going to:

  • be a potential source of flakiness
  • increase our test execution time

I don't think we should be writing tests that actually have a 5-second wait, or any wait for that matter. Can we follow the approach taken for the room lifecycle manager, where we use a mock clock to perform the wait?

@maratal
Copy link
Collaborator Author

maratal commented Mar 12, 2025

I was surprised to find that we've been introducing flaky tests into the Chat unit tests. We need to address the existing flaky ones and not write more.

Anything that involves a timeout is going to:

* be a potential source of flakiness

* increase our test execution time

I don't think we should be writing tests that actually have a 5-second wait, or any wait for that matter. Can we follow the approach taken for the room lifecycle manager, where we use a mock clock to perform the wait?

Should I revert messages tests PR for that matter? @lawrence-forooghian FYI @umair-ably

@lawrence-forooghian
Copy link
Collaborator

I think we need to revisit the recently-added tests (#197, #217, #219, this PR) to make sure that they do not do any of the following:

  • use an instance of DefaultRoomLifecycleManager
  • perform any actual timeouts

If there are flaky tests on main I'd prefer that we remove them and re-add them once they're working properly.

@lawrence-forooghian
Copy link
Collaborator

Opened #236 for reverting.

@maratal
Copy link
Collaborator Author

maratal commented Apr 6, 2025

See #263

@maratal maratal closed this Apr 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Add unit tests for Connection

4 participants