Skip to content

Conversation

@AndyTWF
Copy link
Contributor

@AndyTWF AndyTWF commented Dec 16, 2025

Add centralized mock helpers for unit tests that provide consistent mocking of Ably Realtime, REST, Chat, and Spaces SDKs. Each mock:

  • Uses vi.fn() for all methods to allow assertions and customization
  • Provides _emit helpers with proper SDK types for simulating events
  • Uses Ably's internal EventEmitter for consistent event handling
  • Auto-initializes and resets via test/unit/setup.ts

New files:

  • test/helpers/ably-event-emitter.ts - Shared EventEmitter
  • test/helpers/mock-ably-realtime.ts - Realtime client mock
  • test/helpers/mock-ably-rest.ts - REST client mock
  • test/helpers/mock-ably-chat.ts - Chat SDK mock
  • test/helpers/mock-ably-spaces.ts - Spaces SDK mock

Bonuses:

  • Also migrated remaining command tests from "testableX" style to "runCommand"

FTF-132

Summary by CodeRabbit

Release Notes

No user-facing changes. This release contains internal testing infrastructure improvements, including centralized mock utilities and test suite refactoring to enhance maintainability and consistency across the test codebase.

✏️ Tip: You can customize this high-level summary in your review settings.

Add centralized mock helpers for unit tests that provide consistent
mocking of Ably Realtime, REST, Chat, and Spaces SDKs. Each mock:

- Uses vi.fn() for all methods to allow assertions and customization
- Provides _emit helpers with proper SDK types for simulating events
- Uses Ably's internal EventEmitter for consistent event handling
- Auto-initializes and resets via test/unit/setup.ts

New files:
- test/helpers/ably-event-emitter.ts - Shared EventEmitter
- test/helpers/mock-ably-realtime.ts - Realtime client mock
- test/helpers/mock-ably-rest.ts - REST client mock
- test/helpers/mock-ably-chat.ts - Chat SDK mock
- test/helpers/mock-ably-spaces.ts - Spaces SDK mock

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Dec 16, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
cli-web-cli Ready Ready Preview, Comment Dec 17, 2025 0:34am

@coderabbitai
Copy link

coderabbitai bot commented Dec 16, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Introduces centralized mock helpers for Ably SDK services (Realtime, REST, Chat, Spaces) to reduce test boilerplate. Updates 30+ test files to use these helpers instead of ad-hoc mocking, removing manual mock construction and improving consistency.

Changes

Cohort / File(s) Summary
New Mock Infrastructure
test/helpers/ably-event-emitter.ts, test/helpers/mock-ably-realtime.ts, test/helpers/mock-ably-rest.ts, test/helpers/mock-ably-chat.ts, test/helpers/mock-ably-spaces.ts
Introduces comprehensive mock implementations for Ably SDK services with singleton accessors (getMock*), initialization, and reset helpers. Each provides typed interfaces, factory functions, and internal event emitters to simulate SDK behaviors.
Mock Infrastructure Update
test/helpers/mock-config-manager.ts
Refactors initialization to merge existing global test mocks and align with centralized global state pattern.
Test Setup
test/unit/setup.ts
Extends beforeAll/beforeEach to initialize and reset all Ably service mocks alongside config mock.
Apps Logs Tests
test/unit/commands/apps/logs/history.test.ts, test/unit/commands/apps/logs/subscribe.test.ts
Replaces manual mock setup with centralized helpers (getMockAblyRest/getMockAblyRealtime); removes global test mocks and uses channel-level mocks from helpers.
Auth Tests
test/unit/commands/auth/issue-ably-token.test.ts, test/unit/commands/auth/revoke-token.test.ts
Replaces per-test Ably mock construction with getMockAblyRest/getMockAblyRealtime helpers; removes direct globalThis manipulation.
Bench Tests
test/unit/commands/bench/bench.test.ts, test/unit/commands/bench/benchmarking.test.ts
Refactors from custom TestableBenchPublisher class to real CLI invocation (runCommand) backed by centralized mock helpers; removes internal timing stubs and app/key bypass logic.
Channels Tests
test/unit/commands/channels/batch-publish.test.ts, test/unit/commands/channels/history.test.ts, test/unit/commands/channels/list.test.ts, test/unit/commands/channels/occupancy/get.test.ts, test/unit/commands/channels/occupancy/subscribe.test.ts, test/unit/commands/channels/presence/enter.test.ts, test/unit/commands/channels/presence/subscribe.test.ts, test/unit/commands/channels/publish.test.ts, test/unit/commands/channels/subscribe.test.ts
Replaces global mock declarations and manual setup with getMockAblyRest/getMockAblyRealtime; removes TestableChannels* subclasses; uses helper-provided mocks for channel operations and assertions.
Connections Tests
test/unit/commands/connections/stats.test.ts, test/unit/commands/connections/test.test.ts
Removes TestableConnections* subclasses and heavy mock scaffolding; introduces getMockAblyRest for mocking REST operations and runCommand for CLI invocation; replaces internal mock assertions with stdout/stderr verification.
Logs Lifecycle Tests
test/unit/commands/logs/app/subscribe.test.ts, test/unit/commands/logs/channel-lifecycle.test.ts, test/unit/commands/logs/channel-lifecycle/subscribe.test.ts, test/unit/commands/logs/connection-lifecycle/history.test.ts, test/unit/commands/logs/connection-lifecycle/subscribe.test.ts, test/unit/commands/logs/connection/subscribe.test.ts, test/unit/commands/logs/push/history.test.ts
Consolidates manual Ably mock setup to centralized getMockAblyRealtime/getMockAblyRest helpers; removes global TEST_MOCKS injection; simplifies lifecycle event management via mock implementations.
Rooms Tests
test/unit/commands/rooms/features.test.ts, test/unit/commands/rooms/messages.test.ts, test/unit/commands/rooms/messages/reactions/remove.test.ts, test/unit/commands/rooms/messages/reactions/send.test.ts, test/unit/commands/rooms/messages/reactions/subscribe.test.ts, test/unit/commands/rooms/presence/subscribe.test.ts, test/unit/commands/rooms/reactions/subscribe.test.ts, test/unit/commands/rooms/typing/subscribe.test.ts
Replaces extensive test doubles and TestableRoom* subclasses with getMockAblyChat helper; removes manual room/message/presence mocking; uses mock.rooms._getRoom to access per-test room instances.
Spaces Tests
test/unit/commands/spaces/cursors/get-all.test.ts, test/unit/commands/spaces/cursors/subscribe.test.ts, test/unit/commands/spaces/locations/get-all.test.ts, test/unit/commands/spaces/locations/subscribe.test.ts, test/unit/commands/spaces/locks/get-all.test.ts, test/unit/commands/spaces/locks/get.test.ts, test/unit/commands/spaces/locks/subscribe.test.ts, test/unit/commands/spaces/spaces.test.ts
Consolidates manual mock graphs to getMockAblyRealtime/getMockAblySpaces helpers; removes inline mock construction for spaces, members, locks, locations, cursors; uses helper-provided space instances and mocks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25–35 minutes

Areas requiring extra attention:

  • Mock module implementations (mock-ably-*.ts): Verify each mock correctly mirrors the corresponding Ably SDK surface, including all event emitter behaviors, state transitions, and factory functions.
  • Mock reset/initialization patterns: Ensure all mocks follow a consistent lifecycle (global singleton, per-test reset, proper cleanup).
  • Test assertion validity: Spot-check that mock calls referenced in assertions remain correctly mapped after refactoring (e.g., channel.history → channel.history.mock.calls).
  • Event emission timing: Verify tests that simulate async Ably events (attach, connected, etc.) trigger callbacks correctly via the mock implementations.
  • Integration across helpers: Confirm that dependent mocks (e.g., chat mocks using eventEmitter) are properly wired through shared helpers.

Possibly related PRs

  • PR #108: Introduces Ably EventEmitter helper and comprehensive Ably SDK mock modules that directly implement the test-mocking infrastructure foundation referenced in this PR.
  • PR #120: Adds MockConfigManager and factory functions for creating mocked configuration managers; this PR extends that pattern to centralize Ably service mocks alongside config mocks in the global test state.

Suggested reviewers

  • jamiehenson
  • denissellu

Poem

🐇 Mocks once scattered, now they're tidy,
Helpers wired up so fine—each day, Friday!
No more copy-paste, no more dread,
Tests run cleaner, shipped with thread.

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add centralized mock helpers for Ably SDKs' directly and clearly summarizes the main change—introducing centralized mock helpers for Ably services.
Linked Issues check ✅ Passed The PR successfully implements Phase 2 objectives from FTF-132: adds centralized mock helpers (Realtime, REST, Chat, Spaces), eliminates per-test boilerplate, reduces setup code, and migrates tests from heavy TestableX pattern to runCommand with shared mocks.
Out of Scope Changes check ✅ Passed All changes align with the linked issue scope: new helper modules, test migrations to use centralized mocks, and setup.ts integration. No unrelated refactoring or features detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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 and usage tips.

AndyTWF and others added 4 commits December 16, 2025 23:43
Update channel command tests to use the centralized mock helpers:
- channels/subscribe.test.ts
- channels/occupancy/subscribe.test.ts
- channels/presence/subscribe.test.ts
- channels/presence/enter.test.ts
- channels/batch-publish.test.ts
- channels/list.test.ts
- channels/history.test.ts (remove unused vi import)

Tests now use getMockAblyRealtime() and getMockAblyRest() instead of
manually creating mocks, reducing code duplication and ensuring
consistent mock behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Migrate logs history tests (connection-lifecycle, push) to use getMockAblyRest()
- Migrate logs subscribe tests (app, channel-lifecycle) to use getMockAblyRealtime()
- Migrate all spaces tests (locks, locations, cursors) to use getMockAblySpaces()
- Add cursor channel mock to mock-ably-spaces for subscribe test support
- Fix assertion handling for undefined error messages

Reduces test code by ~1,750 lines through centralized mock pattern.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…pers

- Replace globalThis.__TEST_MOCKS__ pattern with getMockAblyRest()
- Use mock.channels._getChannel() for channel-specific mock setup
- Simplify beforeEach/afterEach with automatic mock reset

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Migrate all remaining unit tests to use the centralized mock helpers:
- auth/issue-ably-token, revoke-token tests
- bench/benchmarking tests
- logs/channel-lifecycle tests
- rooms/messages/reactions/send, remove, subscribe tests
- rooms/presence/subscribe tests
- rooms/reactions/subscribe tests

Also fix mock helpers to restore globalThis mocks on reset, ensuring
tests that delete mocks don't break subsequent tests.

Fix mock-ably-chat to use correct Chat SDK method names (send/delete
instead of add/remove for message reactions).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Migrate bench, channels/publish, channels/occupancy/get, connections/stats,
  connections/test, logs/connection/subscribe, logs/connection-lifecycle/subscribe,
  rooms/features, and rooms/messages tests to use centralized mock helpers
- Add presence.get mock to satisfy checkAndWaitForSubscribers in bench tests
- Simplify connections/test to flag validation tests (command bypasses mock injection)
- Remove live mode tests from connections/stats (command runs indefinitely)
- Add mock helper improvements for chat and realtime

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@AndyTWF AndyTWF marked this pull request as ready for review December 17, 2025 10:04
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: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/unit/commands/logs/channel-lifecycle/subscribe.test.ts (1)

98-111: Inconsistent mock pattern: Direct globalThis manipulation across multiple tests.

This test directly manipulates globalThis.__TEST_MOCKS__.ablyRealtimeMock, which bypasses the centralized mock reset mechanism. The same pattern appears in at least 6 other test files (channel-lifecycle, app/subscribe, connection/subscribe, connection-lifecycle/subscribe, config/show, channels/occupancy/subscribe).

The centralized infrastructure in test/unit/setup.ts provides resetMockAblyRealtime() which handles mock restoration to globalThis, and mock-ably-realtime.ts explicitly supports this use case. However, tests should not directly manipulate globalThis to achieve this.

Either:

  • Add a centralized helper method (e.g., clearMockAblyRealtime() following the pattern in mock-config-manager.ts with clearAccounts()) to properly disable the mock
  • Or remove this test entirely if the scenario is no longer relevant, since setup.ts ensures mocks are always initialized before each test
🧹 Nitpick comments (15)
test/unit/commands/auth/revoke-token.test.ts (1)

13-14: Remove unused mock initialization from test setup.

The getMockAblyRealtime() call is initialized but never used. The command creates a Realtime client that is stored but not used for HTTP operations; instead, it makes direct HTTPS requests to rest.ably.io via Node's https module, which nock mocks at the HTTP level. Removing this initialization will improve test clarity without affecting test functionality.

test/unit/commands/bench/benchmarking.test.ts (1)

7-36: Consider preserving existing auth mock properties when adding clientId.

Line 23 overwrites the entire auth object with just { clientId: "test-client-id" }, which may remove other mocked auth methods (like createTokenRequest, requestToken) that the centralized mock provides. If the benchmarking command uses other auth methods, this could cause issues.

Consider using object spread to preserve existing properties:

-    realtimeMock.auth = { clientId: "test-client-id" };
+    realtimeMock.auth = { ...realtimeMock.auth, clientId: "test-client-id" };

Alternatively, if only clientId is needed, verify the centralized mock already provides it or set it directly:

-    realtimeMock.auth = { clientId: "test-client-id" };
+    realtimeMock.auth.clientId = "test-client-id";
test/unit/commands/apps/logs/subscribe.test.ts (1)

42-56: Optional: Redundant mock retrieval.

Line 43 calls getMockAblyRealtime() again after it was already initialized in beforeEach. While this works (returns the same singleton), it's redundant. Consider storing the mock reference in a test-scoped variable if you need to access it in multiple tests, or retrieve it once per test only when needed.

Apply this pattern if you want to avoid redundant calls:

 describe("alias behavior", () => {
   it("should delegate to logs:app:subscribe with --rewind flag", async () => {
-    const mock = getMockAblyRealtime();
-
     const { stdout } = await runCommand(
       ["apps:logs:subscribe", "--rewind", "5"],
       import.meta.url,
     );
 
+    const mock = getMockAblyRealtime();
     // Should delegate to logs:app:subscribe and show subscription message
     expect(stdout).toContain("Subscribing to app logs");
test/unit/commands/channels/presence/subscribe.test.ts (1)

72-91: Optional: Reduce redundant mock retrievals.

Lines 73-74 and 130-131 retrieve the mock again after it was initialized in beforeEach. While functional, this pattern is redundant. Consider retrieving the mock only when needed for assertions, or storing it in a test-scoped variable if used across multiple tests.

Also applies to: 129-148

test/unit/commands/logs/channel-lifecycle/subscribe.test.ts (1)

72-94: Optional: Redundant mock retrieval.

Lines 72 and 81 retrieve the mock again after beforeEach initialization. This is redundant but functional.

test/unit/commands/channels/list.test.ts (1)

102-151: Optional: Reduce redundant mock retrievals.

Multiple tests retrieve the mock via getMockAblyRest() after it's already initialized in beforeEach (lines 102, 114, 127, 139, 198). While functional, this is redundant.

Also applies to: 198-211

test/unit/commands/channels/subscribe.test.ts (1)

87-102: Optional: Redundant mock retrievals.

Lines 88, 193, and 216 retrieve the mock after beforeEach initialization. This pattern is redundant but functional.

Also applies to: 193-235

test/unit/commands/logs/app/subscribe.test.ts (1)

78-80: Consider removing redundant mock retrieval.

The mocks are already initialized in beforeEach (lines 7-8). Re-fetching them in individual tests is redundant, though harmless since they return the same singleton instance.

Apply this pattern if you prefer cleaner tests:

 it("should subscribe to specific log types", async () => {
-  const mock = getMockAblyRealtime();
-  const channel = mock.channels._getChannel("[meta]log");
+  // Mock already initialized in beforeEach
+  const channel = getMockAblyRealtime().channels._getChannel("[meta]log");

Also applies to: 94-95

test/unit/commands/bench/bench.test.ts (1)

1-1: Remove unused vi import.

Static analysis correctly identifies that vi is imported but never used in this file.

-import { describe, it, expect, beforeEach, vi } from "vitest";
+import { describe, it, expect, beforeEach } from "vitest";
test/unit/commands/rooms/messages.test.ts (2)

281-281: SIGINT emission may leak across tests.

Using process.emit("SIGINT", "SIGINT") to terminate subscription commands could inadvertently affect other parts of the test process or subsequent tests if handlers aren't properly cleaned up. This is a global side effect.

Consider using a more isolated approach, such as mocking the signal handler or using a timeout-based test termination strategy that doesn't emit process-level signals.


129-133: Timing assertions may be flaky in CI environments.

Tests that assert on elapsed time (e.g., expect(totalTime).toBeGreaterThanOrEqual(100)) can be flaky in CI due to system load, CPU scheduling, or slow runners. Consider adding a tolerance margin or using a different approach to verify delay behavior (e.g., verifying mock timers were called with correct delay values).

test/helpers/mock-ably-realtime.ts (1)

417-427: vi.clearAllMocks() affects all mocks globally.

Using vi.clearAllMocks() in _reset() clears call history for all mocks in the test suite, not just this Realtime mock instance. This could inadvertently reset mocks from other helpers (Chat, REST, Spaces) or test-specific mocks.

Consider clearing only the mocks owned by this instance:

     _reset: () => {
       // Clear all channels
       channels._channels.clear();
       // Reset connection state
       connection.state = "connected";
       connection.errorReason = null;
       // Reset auth
       auth.clientId = "mock-client-id";
-      // Clear all mock call history
-      vi.clearAllMocks();
+      // Clear only this instance's mock call history
+      channels.get.mockClear();
+      channels.release.mockClear();
+      connection.on.mockClear();
+      connection.off.mockClear();
+      connection.once.mockClear();
+      connection.connect.mockClear();
+      connection.close.mockClear();
+      connection.ping.mockClear();
+      mock.close.mockClear();
+      mock.connect.mockClear();
+      mock.time.mockClear();
+      mock.stats.mockClear();
+      // ... and auth mocks
     },
test/helpers/mock-ably-chat.ts (1)

382-411: Minor redundancy: attach/detach are defined twice.

attach and detach are first assigned as bare vi.fn() on lines 382-383, then immediately overwritten with implementations on lines 406-411. This is harmless but slightly confusing—consider defining them once with the implementation inline.

-    attach: vi.fn(),
-    detach: vi.fn(),
+    attach: vi.fn().mockImplementation(async () => {
+      room.status = RoomStatus.Attached;
+    }),
+    detach: vi.fn().mockImplementation(async () => {
+      room.status = RoomStatus.Detached;
+    }),
     onStatusChange: vi.fn((callback) => {
       // ...
     }),
     // ...
   };
-
-  // Bind attach/detach to room so they use the auto-emitting setter
-  room.attach = vi.fn().mockImplementation(async () => {
-    room.status = RoomStatus.Attached;
-  });
-  room.detach = vi.fn().mockImplementation(async () => {
-    room.status = RoomStatus.Detached;
-  });

   return room;
test/helpers/mock-ably-spaces.ts (1)

139-154: Consider extracting duplicated subscribe/unsubscribe logic.

The same subscribe/unsubscribe pattern handling multiple overloads (eventOrCallback, callback?) is repeated in createMockSpaceMembers, createMockSpaceLocations, createMockSpaceLocks, and createMockSpaceCursors. Consider extracting a helper:

function createSubscribeMock(emitter: AblyEventEmitter): Mock {
  return vi.fn((eventOrCallback, callback?) => {
    const cb = callback ?? eventOrCallback;
    const event = callback ? eventOrCallback : null;
    emitter.on(event, cb);
  });
}

function createUnsubscribeMock(emitter: AblyEventEmitter): Mock {
  return vi.fn((eventOrCallback?, callback?) => {
    if (!eventOrCallback) {
      emitter.off();
    } else if (typeof eventOrCallback === "function") {
      emitter.off(null, eventOrCallback);
    } else if (callback) {
      emitter.off(eventOrCallback, callback);
    }
  });
}

This reduces duplication and ensures consistent behavior. However, if explicitness is preferred for test helpers, the current approach is acceptable.

test/helpers/mock-ably-rest.ts (1)

62-67: MockRequest interface appears unused.

The MockRequest interface is defined but not used anywhere—MockAblyRest.request is typed directly as Mock (line 95), not MockRequest. Consider removing this unused interface or using it for consistency.

Either remove the unused interface:

-/**
- * Mock request type for REST API calls.
- */
-export interface MockRequest {
-  request: Mock;
-}

Or use it in MockAblyRest:

 export interface MockAblyRest {
   channels: MockRestChannels;
   auth: MockRestAuth;
-  request: Mock;
+  request: MockRequest["request"];
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 83559e5 and 2d9aba4.

📒 Files selected for processing (47)
  • test/helpers/ably-event-emitter.ts (1 hunks)
  • test/helpers/mock-ably-chat.ts (1 hunks)
  • test/helpers/mock-ably-realtime.ts (1 hunks)
  • test/helpers/mock-ably-rest.ts (1 hunks)
  • test/helpers/mock-ably-spaces.ts (1 hunks)
  • test/helpers/mock-config-manager.ts (2 hunks)
  • test/unit/commands/apps/logs/history.test.ts (7 hunks)
  • test/unit/commands/apps/logs/subscribe.test.ts (3 hunks)
  • test/unit/commands/auth/issue-ably-token.test.ts (16 hunks)
  • test/unit/commands/auth/revoke-token.test.ts (1 hunks)
  • test/unit/commands/bench/bench.test.ts (1 hunks)
  • test/unit/commands/bench/benchmarking.test.ts (8 hunks)
  • test/unit/commands/channels/batch-publish.test.ts (14 hunks)
  • test/unit/commands/channels/history.test.ts (11 hunks)
  • test/unit/commands/channels/list.test.ts (8 hunks)
  • test/unit/commands/channels/occupancy/get.test.ts (3 hunks)
  • test/unit/commands/channels/occupancy/subscribe.test.ts (4 hunks)
  • test/unit/commands/channels/presence/enter.test.ts (8 hunks)
  • test/unit/commands/channels/presence/subscribe.test.ts (5 hunks)
  • test/unit/commands/channels/publish.test.ts (2 hunks)
  • test/unit/commands/channels/subscribe.test.ts (4 hunks)
  • test/unit/commands/connections/stats.test.ts (2 hunks)
  • test/unit/commands/connections/test.test.ts (1 hunks)
  • test/unit/commands/logs/app/subscribe.test.ts (4 hunks)
  • test/unit/commands/logs/channel-lifecycle.test.ts (4 hunks)
  • test/unit/commands/logs/channel-lifecycle/subscribe.test.ts (4 hunks)
  • test/unit/commands/logs/connection-lifecycle/history.test.ts (5 hunks)
  • test/unit/commands/logs/connection-lifecycle/subscribe.test.ts (1 hunks)
  • test/unit/commands/logs/connection/subscribe.test.ts (1 hunks)
  • test/unit/commands/logs/push/history.test.ts (5 hunks)
  • test/unit/commands/rooms/features.test.ts (1 hunks)
  • test/unit/commands/rooms/messages.test.ts (1 hunks)
  • test/unit/commands/rooms/messages/reactions/remove.test.ts (5 hunks)
  • test/unit/commands/rooms/messages/reactions/send.test.ts (4 hunks)
  • test/unit/commands/rooms/messages/reactions/subscribe.test.ts (5 hunks)
  • test/unit/commands/rooms/presence/subscribe.test.ts (5 hunks)
  • test/unit/commands/rooms/reactions/subscribe.test.ts (5 hunks)
  • test/unit/commands/rooms/typing/subscribe.test.ts (6 hunks)
  • test/unit/commands/spaces/cursors/get-all.test.ts (6 hunks)
  • test/unit/commands/spaces/cursors/subscribe.test.ts (7 hunks)
  • test/unit/commands/spaces/locations/get-all.test.ts (3 hunks)
  • test/unit/commands/spaces/locations/subscribe.test.ts (8 hunks)
  • test/unit/commands/spaces/locks/get-all.test.ts (3 hunks)
  • test/unit/commands/spaces/locks/get.test.ts (3 hunks)
  • test/unit/commands/spaces/locks/subscribe.test.ts (9 hunks)
  • test/unit/commands/spaces/spaces.test.ts (10 hunks)
  • test/unit/setup.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.test.ts

📄 CodeRabbit inference engine (.cursor/rules/AI-Assistance.mdc)

**/*.test.ts: When testing, mock the Ably SDK properly by stubbing the constructor (e.g., sinon.stub(Ably, 'Rest').returns(mockClient)).
Always ensure resources are properly closed/cleaned up in tests (e.g., call await client.close() and sinon.restore() in afterEach).

Files:

  • test/unit/commands/spaces/cursors/subscribe.test.ts
  • test/unit/commands/channels/list.test.ts
  • test/unit/commands/auth/issue-ably-token.test.ts
  • test/unit/commands/logs/app/subscribe.test.ts
  • test/unit/commands/spaces/locations/get-all.test.ts
  • test/unit/commands/logs/channel-lifecycle.test.ts
  • test/unit/commands/rooms/messages/reactions/remove.test.ts
  • test/unit/commands/apps/logs/history.test.ts
  • test/unit/commands/rooms/typing/subscribe.test.ts
  • test/unit/commands/logs/push/history.test.ts
  • test/unit/commands/spaces/locks/subscribe.test.ts
  • test/unit/commands/spaces/cursors/get-all.test.ts
  • test/unit/commands/channels/occupancy/get.test.ts
  • test/unit/commands/rooms/reactions/subscribe.test.ts
  • test/unit/commands/rooms/messages.test.ts
  • test/unit/commands/logs/connection-lifecycle/subscribe.test.ts
  • test/unit/commands/rooms/features.test.ts
  • test/unit/commands/spaces/spaces.test.ts
  • test/unit/commands/logs/connection-lifecycle/history.test.ts
  • test/unit/commands/channels/presence/enter.test.ts
  • test/unit/commands/spaces/locks/get.test.ts
  • test/unit/commands/connections/test.test.ts
  • test/unit/commands/channels/occupancy/subscribe.test.ts
  • test/unit/commands/connections/stats.test.ts
  • test/unit/commands/rooms/messages/reactions/send.test.ts
  • test/unit/commands/spaces/locks/get-all.test.ts
  • test/unit/commands/auth/revoke-token.test.ts
  • test/unit/commands/logs/connection/subscribe.test.ts
  • test/unit/commands/channels/presence/subscribe.test.ts
  • test/unit/commands/spaces/locations/subscribe.test.ts
  • test/unit/commands/apps/logs/subscribe.test.ts
  • test/unit/commands/channels/batch-publish.test.ts
  • test/unit/commands/rooms/presence/subscribe.test.ts
  • test/unit/commands/logs/channel-lifecycle/subscribe.test.ts
  • test/unit/commands/channels/history.test.ts
  • test/unit/commands/channels/subscribe.test.ts
  • test/unit/commands/bench/benchmarking.test.ts
  • test/unit/commands/bench/bench.test.ts
  • test/unit/commands/rooms/messages/reactions/subscribe.test.ts
  • test/unit/commands/channels/publish.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/Development.mdc)

**/*.{ts,tsx}: Use TypeScript and follow best practice naming conventions
ESLint is used to ensure all code adheres best practices; ensure you check that all code passes the linter before concluding your work.

Files:

  • test/unit/commands/spaces/cursors/subscribe.test.ts
  • test/helpers/ably-event-emitter.ts
  • test/unit/commands/channels/list.test.ts
  • test/helpers/mock-config-manager.ts
  • test/unit/commands/auth/issue-ably-token.test.ts
  • test/unit/commands/logs/app/subscribe.test.ts
  • test/unit/commands/spaces/locations/get-all.test.ts
  • test/unit/commands/logs/channel-lifecycle.test.ts
  • test/unit/setup.ts
  • test/unit/commands/rooms/messages/reactions/remove.test.ts
  • test/unit/commands/apps/logs/history.test.ts
  • test/unit/commands/rooms/typing/subscribe.test.ts
  • test/unit/commands/logs/push/history.test.ts
  • test/unit/commands/spaces/locks/subscribe.test.ts
  • test/unit/commands/spaces/cursors/get-all.test.ts
  • test/unit/commands/channels/occupancy/get.test.ts
  • test/unit/commands/rooms/reactions/subscribe.test.ts
  • test/unit/commands/rooms/messages.test.ts
  • test/unit/commands/logs/connection-lifecycle/subscribe.test.ts
  • test/helpers/mock-ably-realtime.ts
  • test/unit/commands/rooms/features.test.ts
  • test/unit/commands/spaces/spaces.test.ts
  • test/unit/commands/logs/connection-lifecycle/history.test.ts
  • test/unit/commands/channels/presence/enter.test.ts
  • test/unit/commands/spaces/locks/get.test.ts
  • test/unit/commands/connections/test.test.ts
  • test/unit/commands/channels/occupancy/subscribe.test.ts
  • test/unit/commands/connections/stats.test.ts
  • test/unit/commands/rooms/messages/reactions/send.test.ts
  • test/unit/commands/spaces/locks/get-all.test.ts
  • test/unit/commands/auth/revoke-token.test.ts
  • test/unit/commands/logs/connection/subscribe.test.ts
  • test/unit/commands/channels/presence/subscribe.test.ts
  • test/unit/commands/spaces/locations/subscribe.test.ts
  • test/unit/commands/apps/logs/subscribe.test.ts
  • test/helpers/mock-ably-chat.ts
  • test/helpers/mock-ably-spaces.ts
  • test/unit/commands/channels/batch-publish.test.ts
  • test/unit/commands/rooms/presence/subscribe.test.ts
  • test/unit/commands/logs/channel-lifecycle/subscribe.test.ts
  • test/unit/commands/channels/history.test.ts
  • test/unit/commands/channels/subscribe.test.ts
  • test/unit/commands/bench/benchmarking.test.ts
  • test/unit/commands/bench/bench.test.ts
  • test/unit/commands/rooms/messages/reactions/subscribe.test.ts
  • test/helpers/mock-ably-rest.ts
  • test/unit/commands/channels/publish.test.ts
{test/**/*.{ts,js},**/*.{test,spec}.{ts,js}}

📄 CodeRabbit inference engine (.cursor/rules/Development.mdc)

When new features are added or changes made, tests must be updated or added, and it is your responsibility to ensure the tests pass before deeming your work complete.

Files:

  • test/unit/commands/spaces/cursors/subscribe.test.ts
  • test/helpers/ably-event-emitter.ts
  • test/unit/commands/channels/list.test.ts
  • test/helpers/mock-config-manager.ts
  • test/unit/commands/auth/issue-ably-token.test.ts
  • test/unit/commands/logs/app/subscribe.test.ts
  • test/unit/commands/spaces/locations/get-all.test.ts
  • test/unit/commands/logs/channel-lifecycle.test.ts
  • test/unit/setup.ts
  • test/unit/commands/rooms/messages/reactions/remove.test.ts
  • test/unit/commands/apps/logs/history.test.ts
  • test/unit/commands/rooms/typing/subscribe.test.ts
  • test/unit/commands/logs/push/history.test.ts
  • test/unit/commands/spaces/locks/subscribe.test.ts
  • test/unit/commands/spaces/cursors/get-all.test.ts
  • test/unit/commands/channels/occupancy/get.test.ts
  • test/unit/commands/rooms/reactions/subscribe.test.ts
  • test/unit/commands/rooms/messages.test.ts
  • test/unit/commands/logs/connection-lifecycle/subscribe.test.ts
  • test/helpers/mock-ably-realtime.ts
  • test/unit/commands/rooms/features.test.ts
  • test/unit/commands/spaces/spaces.test.ts
  • test/unit/commands/logs/connection-lifecycle/history.test.ts
  • test/unit/commands/channels/presence/enter.test.ts
  • test/unit/commands/spaces/locks/get.test.ts
  • test/unit/commands/connections/test.test.ts
  • test/unit/commands/channels/occupancy/subscribe.test.ts
  • test/unit/commands/connections/stats.test.ts
  • test/unit/commands/rooms/messages/reactions/send.test.ts
  • test/unit/commands/spaces/locks/get-all.test.ts
  • test/unit/commands/auth/revoke-token.test.ts
  • test/unit/commands/logs/connection/subscribe.test.ts
  • test/unit/commands/channels/presence/subscribe.test.ts
  • test/unit/commands/spaces/locations/subscribe.test.ts
  • test/unit/commands/apps/logs/subscribe.test.ts
  • test/helpers/mock-ably-chat.ts
  • test/helpers/mock-ably-spaces.ts
  • test/unit/commands/channels/batch-publish.test.ts
  • test/unit/commands/rooms/presence/subscribe.test.ts
  • test/unit/commands/logs/channel-lifecycle/subscribe.test.ts
  • test/unit/commands/channels/history.test.ts
  • test/unit/commands/channels/subscribe.test.ts
  • test/unit/commands/bench/benchmarking.test.ts
  • test/unit/commands/bench/bench.test.ts
  • test/unit/commands/rooms/messages/reactions/subscribe.test.ts
  • test/helpers/mock-ably-rest.ts
  • test/unit/commands/channels/publish.test.ts
🧬 Code graph analysis (37)
test/unit/commands/channels/list.test.ts (2)
src/base-command.ts (1)
  • getMockAblyRest (225-231)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/auth/issue-ably-token.test.ts (3)
test/helpers/mock-ably-rest.ts (1)
  • getMockAblyRest (241-246)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/logs/app/subscribe.test.ts (2)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/spaces/locations/get-all.test.ts (4)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-spaces.ts (1)
  • getMockAblySpaces (316-321)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/rooms/messages/reactions/remove.test.ts (3)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-chat.ts (1)
  • getMockAblyChat (496-501)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/apps/logs/history.test.ts (2)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/rooms/typing/subscribe.test.ts (2)
test/helpers/mock-ably-chat.ts (1)
  • getMockAblyChat (496-501)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/logs/push/history.test.ts (2)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/spaces/locks/subscribe.test.ts (3)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-spaces.ts (1)
  • getMockAblySpaces (316-321)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/spaces/cursors/get-all.test.ts (4)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-spaces.ts (1)
  • getMockAblySpaces (316-321)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/channels/occupancy/get.test.ts (4)
test/root-hooks.ts (1)
  • beforeEach (8-11)
src/base-command.ts (1)
  • getMockAblyRest (225-231)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/rooms/reactions/subscribe.test.ts (1)
test/helpers/mock-ably-chat.ts (1)
  • getMockAblyChat (496-501)
test/helpers/mock-ably-realtime.ts (1)
test/helpers/ably-event-emitter.ts (2)
  • AblyEventEmitter (14-19)
  • EventEmitter (25-27)
test/unit/commands/rooms/features.test.ts (5)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-chat.ts (1)
  • getMockAblyChat (496-501)
src/base-command.ts (1)
  • getMockAblyChat (262-268)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/spaces/spaces.test.ts (4)
test/helpers/mock-ably-realtime.ts (1)
  • getMockAblyRealtime (440-445)
src/base-command.ts (2)
  • getMockAblyRealtime (237-244)
  • getMockAblySpaces (250-256)
test/helpers/mock-ably-spaces.ts (1)
  • getMockAblySpaces (316-321)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/logs/connection-lifecycle/history.test.ts (2)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/channels/presence/enter.test.ts (2)
test/root-hooks.ts (1)
  • beforeEach (8-11)
src/base-command.ts (1)
  • getMockAblyRealtime (237-244)
test/unit/commands/connections/test.test.ts (5)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-realtime.ts (1)
  • getMockAblyRealtime (440-445)
src/base-command.ts (1)
  • getMockAblyRealtime (237-244)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/channels/occupancy/subscribe.test.ts (3)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/rooms/messages/reactions/send.test.ts (4)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-chat.ts (1)
  • getMockAblyChat (496-501)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/spaces/locks/get-all.test.ts (3)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-spaces.ts (1)
  • getMockAblySpaces (316-321)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/auth/revoke-token.test.ts (1)
test/helpers/mock-ably-realtime.ts (1)
  • getMockAblyRealtime (440-445)
test/unit/commands/logs/connection/subscribe.test.ts (5)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-realtime.ts (1)
  • getMockAblyRealtime (440-445)
src/base-command.ts (1)
  • getMockAblyRealtime (237-244)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/channels/presence/subscribe.test.ts (2)
test/root-hooks.ts (1)
  • beforeEach (8-11)
src/base-command.ts (1)
  • getMockAblyRealtime (237-244)
test/unit/commands/spaces/locations/subscribe.test.ts (3)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-spaces.ts (1)
  • getMockAblySpaces (316-321)
test/helpers/cli-runner.ts (1)
  • stdout (125-127)
test/unit/commands/apps/logs/subscribe.test.ts (1)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-chat.ts (2)
test/helpers/ably-event-emitter.ts (2)
  • AblyEventEmitter (14-19)
  • EventEmitter (25-27)
test/helpers/mock-ably-realtime.ts (2)
  • MockAblyRealtime (116-126)
  • getMockAblyRealtime (440-445)
test/helpers/mock-ably-spaces.ts (1)
test/helpers/ably-event-emitter.ts (2)
  • AblyEventEmitter (14-19)
  • EventEmitter (25-27)
test/unit/commands/channels/batch-publish.test.ts (2)
test/root-hooks.ts (1)
  • beforeEach (8-11)
src/base-command.ts (1)
  • getMockAblyRest (225-231)
test/unit/commands/rooms/presence/subscribe.test.ts (2)
test/helpers/mock-ably-chat.ts (1)
  • getMockAblyChat (496-501)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/logs/channel-lifecycle/subscribe.test.ts (2)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/channels/history.test.ts (2)
test/helpers/mock-ably-rest.ts (1)
  • getMockAblyRest (241-246)
src/base-command.ts (1)
  • getMockAblyRest (225-231)
test/unit/commands/channels/subscribe.test.ts (2)
test/root-hooks.ts (1)
  • beforeEach (8-11)
src/base-command.ts (1)
  • getMockAblyRealtime (237-244)
test/unit/commands/bench/benchmarking.test.ts (4)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-realtime.ts (1)
  • getMockAblyRealtime (440-445)
src/base-command.ts (2)
  • getMockAblyRealtime (237-244)
  • getMockAblyRest (225-231)
test/helpers/mock-ably-rest.ts (1)
  • getMockAblyRest (241-246)
test/unit/commands/bench/bench.test.ts (4)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-realtime.ts (1)
  • getMockAblyRealtime (440-445)
src/base-command.ts (1)
  • getMockAblyRealtime (237-244)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/rooms/messages/reactions/subscribe.test.ts (3)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-chat.ts (1)
  • getMockAblyChat (496-501)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/unit/commands/channels/publish.test.ts (5)
test/root-hooks.ts (1)
  • beforeEach (8-11)
test/helpers/mock-ably-realtime.ts (1)
  • getMockAblyRealtime (440-445)
src/base-command.ts (2)
  • getMockAblyRealtime (237-244)
  • getMockAblyRest (225-231)
test/helpers/command-helpers.ts (1)
  • runCommand (67-83)
test/helpers/cli-runner.ts (2)
  • stdout (125-127)
  • stderr (129-131)
🪛 GitHub Check: e2e-cli
test/unit/commands/bench/bench.test.ts

[warning] 1-1:
'vi' is defined but never used. Allowed unused vars must match /^_/u

🪛 GitHub Check: setup
test/unit/commands/bench/bench.test.ts

[warning] 1-1:
'vi' is defined but never used. Allowed unused vars must match /^_/u

🪛 GitHub Check: test
test/unit/commands/bench/bench.test.ts

[warning] 1-1:
'vi' is defined but never used. Allowed unused vars must match /^_/u

- Fix MockMessageReactions.subscribe return type to return { unsubscribe }
- Centralize vi.clearAllMocks() in test/unit/setup.ts
- Remove vi.clearAllMocks() from individual mock helpers
- Remove process.emit("SIGINT") from unit tests
- Add waitUntilInterruptedOrTimeout to subscribe commands
- Remove redundant unsubscribe/cleanup code from commands
- Remove unused vi import from bench.test.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Member

@jamiehenson jamiehenson left a comment

Choose a reason for hiding this comment

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

Cheers clive

The general mocking substitutions widely make sense, mocking a snapshot of various apis seems like an ongoing burden but at least it's more centralised now - I'm sure improving this further is all a part of the plan

* Access it from the Ably.Realtime class where it's exposed internally.
*/
export const EventEmitter = (
Ably.Realtime as unknown as { EventEmitter: new () => AblyEventEmitter }
Copy link
Member

Choose a reason for hiding this comment

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

This "cast X as Y via unknown" pattern is sometimes a smell for getting around tsc without actually setting correct types - given my lack of familiarity with this, what's the story here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ably-js doesn't formally export its EventEmitter type in ably.d.ts, but we do actually export it at the JS level so it's available to wrapper libraries (e.g. Chat) that use it. This is just the necessary typescript-fu to get access to it 😅

Similar thing in Chat: https://github.com/ably/ably-chat-js/blob/main/src/core/utils/event-emitter.ts

@AndyTWF AndyTWF merged commit 9b37916 into main Dec 18, 2025
10 checks passed
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.

3 participants