Skip to content

[AIT-846] Universal Test Suite (UTS) groundwork#2213

Merged
maratal merged 2 commits into
mainfrom
AIT-846-uts-groundwork
Jun 23, 2026
Merged

[AIT-846] Universal Test Suite (UTS) groundwork#2213
maratal merged 2 commits into
mainfrom
AIT-846-uts-groundwork

Conversation

@maratal

@maratal maratal commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Second of two PRs split out from the original combined PR #2210. Stacked on top of #2210 (base branch AIT-846-mock-for-time) — review/merge that one first.

This PR adds the Universal Test Suite (UTS) groundwork: the Test/UTS target (harness + mocks + the first translated example tests) and the /uts-to-swift translation skill. It builds on the ARTTimeProvider abstraction from #2210 to drive deterministic time in tests.

Contents

  • Harness (Test/UTS/Harness/): UTSTestCase base, MockWebSocket/MockWebSocketProvider, MockHTTPClient, MockTimeProvider, NoOpReachability, CapturingLog, Captured<T>, Sendable ProtocolMessage builders.
  • Tests (Test/UTS/Tests/): mirror the spec's uts/ layout — realtime/unit/ConnectionRecoveryTests.swift, rest/unit/TimeTests.swift.
  • Skill (.claude/skills/uts-to-swift/): translates a UTS pseudocode spec into Swift tests.
  • A few small SDK test-injection seams on ARTTestClientOptions (reachabilityClass, httpExecutor).

Builds: SDK + tests ✓ · swift test --filter UTS 11/11 ✓.

Review

Addresses the comments from review 4485155284; per-comment commit links are posted as replies on #2210.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced Universal Test Suite (UTS) framework with deterministic testing capabilities via mock network and controllable timers.
  • Tests

    • Added UTS test suites for REST and Realtime functionality.
  • Documentation

    • Added UTS setup guides and testing infrastructure documentation.
  • Chores

    • Configured build system and Swift package for UTS test target.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Introduces a new UTS Swift 6 test target under Test/UTS with a complete mock harness (MockWebSocket, MockHTTPClient, MockTimeProvider, CapturingLog, Captured<T>, ProtocolMessage, NoOpReachability), a UTSTestCase orchestrator base class, initial spec-derived test suites (ConnectionRecoveryTests, TimeTests), and extends SDK internals (ARTTestClientOptions, ARTRealtimeInternal, ARTRestInternal) to accept injectable reachability and HTTP executor overrides.

Changes

UTS Swift Testing Infrastructure

Layer / File(s) Summary
SDK injectable test hooks
Source/PrivateHeaders/Ably/ARTTestClientOptions.h, Source/ARTTestClientOptions.m, Source/ARTRealtime.m, Source/ARTRest.m
Adds reachabilityClass and httpExecutor properties to ARTTestClientOptions, wires them into ARTRealtimeInternal and ARTRestInternal initialization, and propagates both in copyWithZone:.
UTS target registration
Package.swift, .swiftpm/xcode/xcshareddata/xcschemes/ably-cocoa.xcscheme, Test/Ably.xctestplan
Declares the UTS Swift 6 SPM test target (Swift 6 via unsafeFlags, path: "Test/UTS"), registers it in the Xcode scheme's TestAction Testables, and adds it to the xctestplan.
Harness primitives
Test/UTS/Harness/Captured.swift, Test/UTS/Harness/CapturingLog.swift, Test/UTS/Harness/NoOpReachability.swift, Test/UTS/Harness/ProtocolMessage.swift
Adds Captured<Element> (NSLock-guarded cross-queue collector), CapturingLog (ARTLog subclass with level/substring query), NoOpReachability (silent reachability stub), and ProtocolMessage (factory for ARTProtocolMessage test fixtures).
MockWebSocket transport seam
Test/UTS/Harness/MockWebSocket.swift
Implements MockWebSocketProvider, MockWebSocket (full ARTWebSocket mock with server-side send/close/refuse APIs and async delegate dispatch), WSCloseCode enum, MockWebSocketFactory (deferred connection-attempt callback), and MockWebSocketTransportFactory.
MockHTTPClient HTTP executor mock
Test/UTS/Harness/MockHTTPClient.swift
Adds MockHTTPClient (ARTHTTPExecutor) with two-phase connection-attempt/request handlers, PendingHTTPConnection (URL-derived host/port/TLS with NSError helpers), PendingHTTPRequest (inspectable fields and synchronous response completion), and NoopCancellable.
MockTimeProvider deterministic clock
Test/UTS/Harness/MockTimeProvider.swift
Implements MockTimeProvider (TimeProvider-conforming) with dual-clock deterministic state, per-block cancellable ScheduledBlock/Handle, advanceTime loop that fires due blocks and drains dispatch queue cascades, and cancelAllScheduled teardown.
UTSTestCase harness orchestrator
Test/UTS/Harness/UTSTestCase.swift
Orchestrates harness setup: enableFakeTimers, installMock overloads, makeRealtime/makeRest factories, awaitConnectionState/awaitChannelState/poll helpers, advanceTime, closeClient, and deinit cleanup.
UTS test suites
Test/UTS/Tests/realtime/unit/connection/ConnectionRecoveryTests.swift, Test/UTS/Tests/rest/unit/TimeTests.swift
Adds ConnectionRecoveryTests (RTN16: recovery key structure, nil in inactive states, recover/resume query params, msgSerial init, malformed-key rejection, channelSerial recovery) and TimeTests (RSC16: time value, request shape, Authorization omission, TLS scheme, error propagation) with async continuation helpers.
UTS documentation
Test/UTS/README.md, Test/UTS/deviations.md, .claude/skills/uts-to-swift/SKILL.md
Adds target README (layout, primitive mapping, run commands), deviation tracking template (RUN_DEVIATIONS-gated), and an AI translation skill guide (Steps 0–7: argument validation, spec parsing, path mapping, harness translation rules, compile/run, failure decision tree, checklist).

Sequence Diagram(s)

sequenceDiagram
    participant Test as UTSTestCase (test body)
    participant UTSTestCase as UTSTestCase (makeRealtime)
    participant MockWebSocketTransportFactory
    participant MockWebSocketFactory
    participant MockWebSocketProvider
    participant ARTWebSocketTransport
    participant MockWebSocket

    Test->>UTSTestCase: installMock(wsProvider)
    Test->>UTSTestCase: makeRealtime { configure options }
    UTSTestCase->>ARTWebSocketTransport: init with MockWebSocketTransportFactory
    ARTWebSocketTransport->>MockWebSocketFactory: create(request:workQueue:)
    MockWebSocketFactory->>MockWebSocket: init
    MockWebSocketFactory->>MockWebSocketProvider: register(ws)
    MockWebSocketFactory-->>MockWebSocketProvider: onConnectionAttempt(ws) async on workQueue
    MockWebSocketProvider-->>Test: handler receives MockWebSocket

    Test->>MockWebSocket: respondWithSuccess()
    MockWebSocket->>ARTWebSocketTransport: delegate.webSocketDidOpen (async)

    Test->>MockWebSocket: send(ProtocolMessage.connected(...))
    MockWebSocket->>ARTWebSocketTransport: delegate.didReceiveMessage (async)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • lawrence-forooghian

Poem

🐇 Hop hop, the mocks are set,
WebSockets fake and timers met,
Recovery keys and REST time calls,
The harness bounds through every hall.
Swift 6 strict, no data race,
UTS runs at bunny pace! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the PR: introducing Universal Test Suite (UTS) groundwork with foundational infrastructure and test harness components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch AIT-846-uts-groundwork

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

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 15, 2026 00:25 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 15, 2026 00:29 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 15, 2026 00:29 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 15, 2026 01:25 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 15, 2026 01:29 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 15, 2026 01:29 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 15, 2026 02:36 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 15, 2026 02:40 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 15, 2026 02:40 Inactive
@maratal maratal marked this pull request as ready for review June 15, 2026 16:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/skills/uts-to-swift/SKILL.md:
- Around line 23-54: Add language tags to plain-text code fences to resolve
markdownlint issues. In .claude/skills/uts-to-swift/SKILL.md at lines 23-54, add
the text language tag to each of the usage and error-message fences (the three
backtick blocks containing Usage and Error messages). In Test/UTS/README.md at
lines 8-23, add the text language tag to the directory-tree fence. In
.claude/skills/uts-to-swift/SKILL.md at lines 493-505, add the text language tag
to the decision-tree fence. For each fence, change the opening backticks from
``` to ```text.

In `@Test/UTS/Harness/MockTimeProvider.swift`:
- Around line 149-159: The fireBlocksDue function extracts blocks by checking
isCancelled under lock but then dispatches them asynchronously without
re-verifying the isCancelled flag. If cancel() is called between extraction and
execution, the block will still run despite being cancelled. Fix this by adding
an isCancelled check in the for loop where scheduled blocks are executed (the
loop that calls scheduled.queue.async) to ensure cancelled blocks are skipped
even after async dispatch. Additionally, add a regression test that schedules a
block, advances time to trigger extraction, calls cancel() before the async
queue drains, and verifies the block does not execute.

In `@Test/UTS/Tests/rest/unit/TimeTests.swift`:
- Line 34: The test in TimeTests.swift at line 34 uses truncation-based
conversion with Int(date.timeIntervalSince1970 * 1000) which can cause
off-by-1ms failures due to floating-point rounding inconsistencies. Replace the
truncating Int() conversion with explicit rounding (such as using round()
function) before converting to Int to ensure deterministic millisecond
comparison. This will make the equality check against serverTimeMs consistent
and reliable across different system floating-point representations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d42e36ee-9dad-4541-9b9f-620340489177

📥 Commits

Reviewing files that changed from the base of the PR and between 28214a8 and 3f262a2.

📒 Files selected for processing (20)
  • .claude/skills/uts-to-swift/SKILL.md
  • .swiftpm/xcode/xcshareddata/xcschemes/ably-cocoa.xcscheme
  • Package.swift
  • Source/ARTRealtime.m
  • Source/ARTRest.m
  • Source/ARTTestClientOptions.m
  • Source/PrivateHeaders/Ably/ARTTestClientOptions.h
  • Test/Ably.xctestplan
  • Test/UTS/Harness/Captured.swift
  • Test/UTS/Harness/CapturingLog.swift
  • Test/UTS/Harness/MockHTTPClient.swift
  • Test/UTS/Harness/MockTimeProvider.swift
  • Test/UTS/Harness/MockWebSocket.swift
  • Test/UTS/Harness/NoOpReachability.swift
  • Test/UTS/Harness/ProtocolMessage.swift
  • Test/UTS/Harness/UTSTestCase.swift
  • Test/UTS/README.md
  • Test/UTS/Tests/realtime/unit/ConnectionRecoveryTests.swift
  • Test/UTS/Tests/rest/unit/TimeTests.swift
  • Test/UTS/deviations.md

Comment thread .claude/skills/uts-to-swift/SKILL.md
Comment thread Test/UTS/Harness/MockTimeProvider.swift
Comment thread Test/UTS/Tests/rest/unit/TimeTests.swift Outdated
@maratal maratal force-pushed the AIT-846-uts-groundwork branch from 3f262a2 to d5621d2 Compare June 18, 2026 12:16
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 18, 2026 12:17 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 18, 2026 12:20 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 18, 2026 12:20 Inactive
@lawrence-forooghian lawrence-forooghian force-pushed the AIT-846-mock-for-time branch 2 times, most recently from 209f1e9 to d43144e Compare June 18, 2026 15:47
@maratal maratal force-pushed the AIT-846-mock-for-time branch from d43144e to 24f15a0 Compare June 18, 2026 21:09
@maratal maratal force-pushed the AIT-846-uts-groundwork branch from d5621d2 to d0c4888 Compare June 18, 2026 21:10
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 18, 2026 21:10 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 18, 2026 21:16 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 18, 2026 21:16 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 22, 2026 13:36 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 22, 2026 13:40 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 22, 2026 13:40 Inactive
@maratal maratal force-pushed the AIT-846-uts-groundwork branch from d1148f6 to 5247491 Compare June 22, 2026 15:08
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 22, 2026 15:08 Inactive
@maratal maratal force-pushed the AIT-846-uts-groundwork branch from d2f5443 to 280a570 Compare June 22, 2026 19:23
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 22, 2026 19:24 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 22, 2026 19:27 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 22, 2026 19:27 Inactive
@maratal maratal force-pushed the AIT-846-uts-groundwork branch from 280a570 to 73fe058 Compare June 22, 2026 20:07
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 22, 2026 20:08 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 22, 2026 20:14 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 22, 2026 20:14 Inactive
@maratal maratal force-pushed the AIT-846-uts-groundwork branch from 73fe058 to d692e32 Compare June 22, 2026 21:42
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 22, 2026 21:43 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 22, 2026 21:47 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 22, 2026 21:47 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
Test/UTS/Tests/realtime/unit/connection/ConnectionRecoveryTests.swift (1)

489-492: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider using failable String initializer for defensive coding.

SwiftLint flags the use of String(decoding:as:) on line 491, suggesting the failable String(bytes:encoding:) initializer. In this test helper context where we just serialized valid JSON, the data is guaranteed to be valid UTF-8, so the non-failable API is safe. However, using the failable initializer would be more defensive against future changes.

♻️ Proposed defensive fix
 func toJSONString(_ object: [String: Any]) throws -> String {
     let data = try JSONSerialization.data(withJSONObject: object)
-    return String(decoding: data, as: UTF8.self)
+    guard let string = String(bytes: data, encoding: .utf8) else {
+        throw NSError(domain: "ConnectionRecoveryTests", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to decode JSON data as UTF-8"])
+    }
+    return string
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Test/UTS/Tests/realtime/unit/connection/ConnectionRecoveryTests.swift` around
lines 489 - 492, The toJSONString method currently uses the non-failable
String(decoding:as:) initializer on line 491. Replace it with the failable
String(bytes:encoding:) initializer to be more defensive. Since the function
already throws, use a guard statement to unwrap the optional String result and
throw an appropriate error if the UTF-8 decoding fails, ensuring the function
maintains its error handling contract.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/skills/uts-to-swift/SKILL.md:
- Around line 410-418: The awaitTime function has a control flow issue where the
guard let date else block does not exit, allowing execution to continue to
continuation.resume(returning: date) with a potentially nil date value, which
violates the CheckedContinuation<Date, Never> contract that requires a non-nil
Date. Add a return statement inside the guard-else block after the
Issue.record() call to exit the closure when date is nil, ensuring that
continuation.resume is only reached and called with a valid non-nil date value.

---

Nitpick comments:
In `@Test/UTS/Tests/realtime/unit/connection/ConnectionRecoveryTests.swift`:
- Around line 489-492: The toJSONString method currently uses the non-failable
String(decoding:as:) initializer on line 491. Replace it with the failable
String(bytes:encoding:) initializer to be more defensive. Since the function
already throws, use a guard statement to unwrap the optional String result and
throw an appropriate error if the UTF-8 decoding fails, ensuring the function
maintains its error handling contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 76216180-73bd-46af-a3c3-927d63f57990

📥 Commits

Reviewing files that changed from the base of the PR and between 5247491 and d692e32.

📒 Files selected for processing (3)
  • .claude/skills/uts-to-swift/SKILL.md
  • Test/UTS/Tests/realtime/unit/connection/ConnectionRecoveryTests.swift
  • Test/UTS/Tests/rest/unit/TimeTests.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Test/UTS/Tests/rest/unit/TimeTests.swift

Comment thread .claude/skills/uts-to-swift/SKILL.md
ARTTestClientOptions is the place to inject test dependencies into clients.
Add a reachabilityClass property (defaulting to ARTOSReachability) and have
ARTRealtimeInternal read it at init, so tests can install an alternative
ARTReachability implementation through testOptions instead of the
post-construction setReachabilityClass setter (which remains for existing
tests that still use it).

Addresses #2210 (comment)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lawrence-forooghian

Copy link
Copy Markdown
Collaborator

@maratal i'm not sure how the contents of 016d347 disappeared from #2210, but anyway I've moved that line into that PR and rebased this one to drop that commit

@github-actions github-actions Bot temporarily deployed to staging/pull/2213/features June 23, 2026 10:29 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/jazzydoc June 23, 2026 10:32 Inactive
@github-actions github-actions Bot temporarily deployed to staging/pull/2213/markdown-api-reference June 23, 2026 10:32 Inactive
@lawrence-forooghian

Copy link
Copy Markdown
Collaborator

@maratal my comments from the previous review have been addressed, thanks. As mentioned in the previous review, there's a lot of detail here that I didn't get into (comparing the translated tests, looking at some of the mock implementations in detail). I think that what we have here is a good start and hopefully any issues in it will be revealed once we start writing a broader set of LiveObjects tests.

Happy to approve once commits sorted out.

Standalone Swift Testing target (Test/UTS) derived from the language-neutral specs in ably/specification (uts/):
mocked WebSocket (real ARTWebSocketTransport over a faked ARTWebSocket), mocked HTTP, MockTimeProvider
and Captured/CapturingLog/NoOpReachability/ProtocolMessage helpers.

Tests: ConnectionRecoveryTests (RTN16) and TimeTests (RSC16). Built in the Swift 6 language mode.
Also adds the /uts-to-swift translation skill and registers UTS in the SwiftPM scheme and xctestplan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Base automatically changed from AIT-846-mock-for-time to main June 23, 2026 18:46
@maratal maratal merged commit 026fb72 into main Jun 23, 2026
10 checks passed
@maratal maratal deleted the AIT-846-uts-groundwork branch June 23, 2026 20:58
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.

2 participants