From e1723c5e6b2013900a1043a281816f84edc812db Mon Sep 17 00:00:00 2001 From: umair Date: Wed, 11 Mar 2026 15:58:49 +0000 Subject: [PATCH 1/5] Expand unit test coverage and add TTY test infrastructure --- .claude/skills/ably-codebase-review/SKILL.md | 13 +- .claude/skills/ably-new-command/SKILL.md | 3 +- .../ably-new-command/references/testing.md | 218 ++++++++++-- .claude/skills/ably-review/SKILL.md | 11 +- AGENTS.md | 29 +- CONTRIBUTING.md | 4 +- README.md | 215 +++++++---- docs/Project-Structure.md | 3 + docs/Testing.md | 13 + package.json | 1 + test/README.md | 16 + test/tty/commands/interactive-sigint.test.ts | 147 ++++++++ test/tty/tty-test-helper.ts | 129 +++++++ test/unit/commands/accounts/current.test.ts | 33 +- test/unit/commands/accounts/list.test.ts | 43 ++- test/unit/commands/accounts/login.test.ts | 23 +- test/unit/commands/accounts/logout.test.ts | 23 +- test/unit/commands/accounts/switch.test.ts | 43 ++- .../apps/channel-rules/create.test.ts | 34 +- .../apps/channel-rules/delete.test.ts | 34 +- .../commands/apps/channel-rules/list.test.ts | 33 +- .../apps/channel-rules/update.test.ts | 34 +- test/unit/commands/apps/create.test.ts | 29 +- test/unit/commands/apps/current.test.ts | 33 +- test/unit/commands/apps/delete.test.ts | 35 +- test/unit/commands/apps/list.test.ts | 33 +- test/unit/commands/apps/set-apns-p12.test.ts | 34 +- test/unit/commands/apps/switch.test.ts | 184 ++++++++++ test/unit/commands/apps/update.test.ts | 34 +- .../commands/auth/issue-ably-token.test.ts | 24 +- .../commands/auth/issue-jwt-token.test.ts | 24 +- test/unit/commands/auth/keys/create.test.ts | 24 +- test/unit/commands/auth/keys/current.test.ts | 33 +- test/unit/commands/auth/keys/get.test.ts | 31 +- test/unit/commands/auth/keys/list.test.ts | 33 +- test/unit/commands/auth/keys/revoke.test.ts | 34 +- test/unit/commands/auth/keys/switch.test.ts | 177 ++++++++++ test/unit/commands/auth/keys/update.test.ts | 34 +- test/unit/commands/bench/bench.test.ts | 27 ++ test/unit/commands/bench/publisher.test.ts | 191 ++++++++++ test/unit/commands/bench/subscriber.test.ts | 121 +++++++ .../unit/commands/channel-rule/create.test.ts | 43 ++- .../unit/commands/channel-rule/delete.test.ts | 43 ++- test/unit/commands/channel-rule/list.test.ts | 48 ++- .../unit/commands/channel-rule/update.test.ts | 43 ++- test/unit/commands/channels/inspect.test.ts | 47 ++- test/unit/commands/channels/list.test.ts | 24 +- .../commands/channels/occupancy/get.test.ts | 61 ++++ .../channels/occupancy/subscribe.test.ts | 24 +- .../commands/channels/presence/enter.test.ts | 29 +- .../channels/presence/subscribe.test.ts | 28 +- test/unit/commands/channels/publish.test.ts | 37 +- test/unit/commands/channels/subscribe.test.ts | 18 +- test/unit/commands/config/path.test.ts | 35 +- test/unit/commands/config/show.test.ts | 43 ++- test/unit/commands/connections/test.test.ts | 54 +++ .../unit/commands/integrations/create.test.ts | 33 +- .../unit/commands/integrations/delete.test.ts | 24 +- test/unit/commands/integrations/get.test.ts | 21 +- test/unit/commands/integrations/list.test.ts | 228 ++++++++++++ .../unit/commands/integrations/update.test.ts | 24 +- test/unit/commands/interactive-sigint.test.ts | 140 +------- test/unit/commands/interactive.test.ts | 111 +----- test/unit/commands/login.test.ts | 65 ++++ .../logs/channel-lifecycle/subscribe.test.ts | 25 +- .../logs/connection-lifecycle/history.test.ts | 40 ++- .../connection-lifecycle/subscribe.test.ts | 76 ++++ test/unit/commands/logs/history.test.ts | 61 +++- test/unit/commands/logs/push/history.test.ts | 40 ++- .../unit/commands/logs/push/subscribe.test.ts | 71 +++- test/unit/commands/logs/subscribe.test.ts | 38 +- test/unit/commands/queues/create.test.ts | 41 ++- test/unit/commands/queues/delete.test.ts | 72 +++- test/unit/commands/queues/list.test.ts | 33 +- test/unit/commands/rooms/features.test.ts | 156 +++++++- test/unit/commands/rooms/list.test.ts | 54 +++ test/unit/commands/rooms/messages.test.ts | 80 ++++- .../commands/rooms/messages/history.test.ts | 298 ++++++++++++++++ .../rooms/messages/reactions/remove.test.ts | 47 ++- .../rooms/messages/reactions/send.test.ts | 42 ++- .../messages/reactions/subscribe.test.ts | 40 ++- .../unit/commands/rooms/messages/send.test.ts | 333 ++++++++++++++++++ .../commands/rooms/messages/subscribe.test.ts | 283 +++++++++++++++ .../unit/commands/rooms/occupancy/get.test.ts | 48 ++- .../rooms/occupancy/subscribe.test.ts | 62 +++- .../commands/rooms/presence/enter.test.ts | 76 +++- .../commands/rooms/presence/subscribe.test.ts | 40 ++- .../commands/rooms/reactions/send.test.ts | 48 ++- .../rooms/reactions/subscribe.test.ts | 40 ++- .../commands/rooms/typing/keystroke.test.ts | 56 ++- .../commands/rooms/typing/subscribe.test.ts | 40 ++- .../commands/spaces/cursors/get-all.test.ts | 24 +- test/unit/commands/spaces/cursors/set.test.ts | 40 ++- .../commands/spaces/cursors/subscribe.test.ts | 40 ++- test/unit/commands/spaces/list.test.ts | 55 +++ .../commands/spaces/locations/get-all.test.ts | 40 ++- .../commands/spaces/locations/set.test.ts | 42 ++- .../spaces/locations/subscribe.test.ts | 26 +- .../commands/spaces/locks/acquire.test.ts | 61 +++- .../commands/spaces/locks/get-all.test.ts | 38 +- test/unit/commands/spaces/locks/get.test.ts | 38 +- .../commands/spaces/locks/subscribe.test.ts | 26 +- .../commands/spaces/members/enter.test.ts | 40 ++- .../commands/spaces/members/subscribe.test.ts | 39 +- test/unit/commands/spaces/spaces.test.ts | 43 ++- test/unit/commands/stats/account.test.ts | 62 ++++ test/unit/commands/stats/app.test.ts | 61 ++++ test/unit/commands/status.test.ts | 23 +- test/unit/commands/support.test.ts | 44 ++- test/unit/commands/support/ask.test.ts | 202 +++++++++++ test/unit/commands/support/contact.test.ts | 43 ++- test/unit/commands/test/wait.test.ts | 81 +++++ test/unit/commands/version.test.ts | 91 +++++ vitest.config.ts | 22 ++ 114 files changed, 6340 insertions(+), 541 deletions(-) create mode 100644 test/tty/commands/interactive-sigint.test.ts create mode 100644 test/tty/tty-test-helper.ts create mode 100644 test/unit/commands/apps/switch.test.ts create mode 100644 test/unit/commands/auth/keys/switch.test.ts create mode 100644 test/unit/commands/bench/publisher.test.ts create mode 100644 test/unit/commands/bench/subscriber.test.ts create mode 100644 test/unit/commands/integrations/list.test.ts create mode 100644 test/unit/commands/login.test.ts create mode 100644 test/unit/commands/rooms/messages/history.test.ts create mode 100644 test/unit/commands/rooms/messages/send.test.ts create mode 100644 test/unit/commands/rooms/messages/subscribe.test.ts create mode 100644 test/unit/commands/support/ask.test.ts create mode 100644 test/unit/commands/test/wait.test.ts create mode 100644 test/unit/commands/version.test.ts diff --git a/.claude/skills/ably-codebase-review/SKILL.md b/.claude/skills/ably-codebase-review/SKILL.md index 3ade6662..5db8ba4a 100644 --- a/.claude/skills/ably-codebase-review/SKILL.md +++ b/.claude/skills/ably-codebase-review/SKILL.md @@ -154,14 +154,21 @@ Launch these agents **in parallel**. Each agent gets a focused mandate and uses **Method (grep/glob — text patterns and file matching):** 1. **Glob** for each command in `src/commands/` and check if a corresponding test file exists at `test/unit/commands/` -2. **Grep** for `describe(` in test files to check for required describe blocks: `help`, `argument validation`, `functionality`, `flags`, `error handling` -3. **Grep** for `--duration` in subscribe test files +2. **Grep** for `describe(` in test files to check for the 5 required describe blocks with EXACT standard names: + - `describe("help"` — required in every test file + - `describe("argument validation"` — required (test required args OR unknown flag rejection) + - `describe("functionality"` — required (core happy-path tests) + - `describe("flags"` — required (verify flags exist and work) + - `describe("error handling"` — required (API errors, network failures) + Flag non-standard variants: `"command arguments and flags"`, `"command flags"`, `"flag options"`, `"parameter validation"`. + Exception: `interactive.test.ts`, `interactive-sigint.test.ts`, and `bench/*.test.ts` are exempt. +3. **Grep** for `--duration` in unit test `runCommand()` args — should NOT be present (env var handles it). Exceptions: `test:wait` tests, `interactive-sigint` tests, help output checks. 4. **Grep** for `getMockAblyRealtime`, `getMockAblyRest`, `getMockConfigManager` in test files to verify correct mock usage per command type 5. **Grep** for `--api-key`, `--token`, `--access-token` in unit test files — these should not use CLI auth flags **Reasoning guidance:** - Missing test files are deviations but may be documented as known gaps -- Missing describe blocks in existing tests are deviations but may be lower priority +- Missing describe blocks or non-standard block names are deviations that should be flagged - Subscribe tests that auto-exit via mocked callbacks (without `--duration`) may be acceptable ### Agent 7: Lifecycle & Convention Sweep diff --git a/.claude/skills/ably-new-command/SKILL.md b/.claude/skills/ably-new-command/SKILL.md index 8f3a5a58..2fa81db4 100644 --- a/.claude/skills/ably-new-command/SKILL.md +++ b/.claude/skills/ably-new-command/SKILL.md @@ -363,7 +363,8 @@ If the new command shouldn't be available in the web CLI, add it to the appropri After creating command and test files, always run: ```bash -pnpm prepare # Build + update manifest/README +pnpm prepare # Build + update manifest +pnpm exec oclif readme # Regenerate README.md from command metadata pnpm exec eslint . # Lint (must be 0 errors) pnpm test:unit # Run tests ``` diff --git a/.claude/skills/ably-new-command/references/testing.md b/.claude/skills/ably-new-command/references/testing.md index f71bd6ca..68065ec4 100644 --- a/.claude/skills/ably-new-command/references/testing.md +++ b/.claude/skills/ably-new-command/references/testing.md @@ -2,6 +2,8 @@ Test files go at `test/unit/commands/.test.ts`. +> **Note:** Do NOT pass `--duration` to `runCommand()` in unit/integration tests. `vitest.config.ts` sets `ABLY_CLI_DEFAULT_DURATION: "0.25"` which makes subscribe commands auto-exit after 250ms. Adding `--duration` overrides this with a slower value. + ## Table of Contents - [Product API Test (Realtime Mock)](#product-api-test-realtime-mock) - [Product API Test (REST Mock)](#product-api-test-rest-mock) @@ -62,7 +64,7 @@ describe("topic:action command", () => { describe("functionality", () => { it("should subscribe and display events", async () => { - const commandPromise = runCommand(["topic:action", "test-channel", "--duration", "1"], import.meta.url); + const commandPromise = runCommand(["topic:action", "test-channel"], import.meta.url); await vi.waitFor(() => { expect(mockCallback).not.toBeNull(); }); @@ -76,6 +78,28 @@ describe("topic:action command", () => { expect(stdout).toContain("test-channel"); }); }); + + describe("flags", () => { + it("should accept --duration flag", async () => { + const { stdout } = await runCommand(["topic:action", "--help"], import.meta.url); + expect(stdout).toContain("--duration"); + }); + }); + + describe("error handling", () => { + it("should handle connection failure", async () => { + const mock = getMockAblyRealtime(); + mock.connection.once.mockImplementation((event: string, cb: (stateChange: unknown) => void) => { + if (event === "failed") cb({ reason: new Error("Connection failed") }); + }); + + const { error } = await runCommand( + ["topic:action", "test-channel"], + import.meta.url, + ); + expect(error).toBeDefined(); + }); + }); }); ``` @@ -99,13 +123,50 @@ describe("topic:action command", () => { }); }); - it("should retrieve history", async () => { - const { stdout } = await runCommand( - ["topic:action", "test-channel"], - import.meta.url, - ); - expect(stdout).toContain("1"); - expect(stdout).toContain("messages"); + describe("help", () => { + it("should display help with --help flag", async () => { + const { stdout } = await runCommand(["topic:action", "--help"], import.meta.url); + expect(stdout).toContain("USAGE"); + }); + }); + + describe("argument validation", () => { + it("should require channel argument", async () => { + const { error } = await runCommand(["topic:action"], import.meta.url); + expect(error?.message).toMatch(/Missing .* required arg/i); + }); + }); + + describe("functionality", () => { + it("should retrieve history", async () => { + const { stdout } = await runCommand( + ["topic:action", "test-channel"], + import.meta.url, + ); + expect(stdout).toContain("1"); + expect(stdout).toContain("messages"); + }); + }); + + describe("flags", () => { + it("should accept --json flag", async () => { + const { stdout } = await runCommand(["topic:action", "--help"], import.meta.url); + expect(stdout).toContain("--json"); + }); + }); + + describe("error handling", () => { + it("should handle API errors", async () => { + const mock = getMockAblyRest(); + const channel = mock.channels._getChannel("test-channel"); + channel.history.mockRejectedValue(new Error("API error")); + + const { error } = await runCommand( + ["topic:action", "test-channel"], + import.meta.url, + ); + expect(error).toBeDefined(); + }); }); }); ``` @@ -125,32 +186,61 @@ describe("topic:action command", () => { nock.cleanAll(); }); - it("should create resource", async () => { - const appId = getMockConfigManager().getCurrentAppId()!; - nock("https://control.ably.net") - .post(`/v1/apps/${appId}/resources`) - .reply(201, { id: "res-123", appId }); + describe("help", () => { + it("should display help with --help flag", async () => { + const { stdout } = await runCommand(["topic:action", "--help"], import.meta.url); + expect(stdout).toContain("USAGE"); + }); + }); - const { stdout } = await runCommand( - ["topic:action", "--flag", "value"], - import.meta.url, - ); + describe("argument validation", () => { + it("should reject unknown flags", async () => { + const { error } = await runCommand( + ["topic:action", "--unknown-flag-xyz"], + import.meta.url, + ); + expect(error).toBeDefined(); + expect(error?.message).toMatch(/unknown|Nonexistent flag/i); + }); + }); + + describe("functionality", () => { + it("should create resource", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + nock("https://control.ably.net") + .post(`/v1/apps/${appId}/resources`) + .reply(201, { id: "res-123", appId }); + + const { stdout } = await runCommand( + ["topic:action", "--flag", "value"], + import.meta.url, + ); + + expect(stdout).toContain("created"); + }); + }); - expect(stdout).toContain("created"); + describe("flags", () => { + it("should accept --json flag", async () => { + const { stdout } = await runCommand(["topic:action", "--help"], import.meta.url); + expect(stdout).toContain("--json"); + }); }); - it("should handle API errors", async () => { - const appId = getMockConfigManager().getCurrentAppId()!; - nock("https://control.ably.net") - .post(`/v1/apps/${appId}/resources`) - .reply(400, { error: "Bad request" }); + describe("error handling", () => { + it("should handle API errors", async () => { + const appId = getMockConfigManager().getCurrentAppId()!; + nock("https://control.ably.net") + .post(`/v1/apps/${appId}/resources`) + .reply(400, { error: "Bad request" }); - const { error } = await runCommand( - ["topic:action", "--flag", "value"], - import.meta.url, - ); + const { error } = await runCommand( + ["topic:action", "--flag", "value"], + import.meta.url, + ); - expect(error).toBeDefined(); + expect(error).toBeDefined(); + }); }); }); ``` @@ -266,9 +356,69 @@ describe.skipIf(SHOULD_SKIP_E2E)("topic:action CRUD E2E", { timeout: 30_000 }, ( ## Test Structure -Always include these describe blocks: -1. `help` — verify `--help` shows USAGE, key flags, and EXAMPLES -2. `argument validation` — verify required args produce errors when missing -3. `functionality` — core behavior tests -4. `flags` — verify key flags are accepted and configured -5. `error handling` — API errors, invalid input +Every test file MUST include all 5 of these describe blocks (exact names — no variants): + +1. **`help`** — verify `--help` shows USAGE, key flags, and EXAMPLES +2. **`argument validation`** — verify required args produce errors when missing. For commands with no required args, test that unknown flags are rejected. +3. **`functionality`** — core happy-path behavior tests. This is where the main command logic is tested. +4. **`flags`** — verify key flags are accepted and configured (check help output contains flag names, test flag behavior) +5. **`error handling`** — API errors, invalid input, network failures + +### Block naming rules +- Use EXACTLY these names: `"help"`, `"argument validation"`, `"functionality"`, `"flags"`, `"error handling"` +- Do NOT use variants like `"command arguments and flags"`, `"command flags"`, `"flag options"`, `"parameter validation"`, or domain-specific names like `"subscription behavior"` +- Additional describe blocks beyond the 5 required ones are fine (e.g., `"JSON output"`, `"cleanup behavior"`) + +### What goes in each block + +**`argument validation`** for commands WITH required args: +```typescript +describe("argument validation", () => { + it("should require channel argument", async () => { + const { error } = await runCommand(["topic:action"], import.meta.url); + expect(error?.message).toMatch(/Missing .* required arg/i); + }); +}); +``` + +**`argument validation`** for commands WITHOUT required args: +```typescript +describe("argument validation", () => { + it("should reject unknown flags", async () => { + const { error } = await runCommand( + ["topic:action", "--unknown-flag-xyz"], + import.meta.url, + ); + expect(error).toBeDefined(); + expect(error?.message).toMatch(/unknown|Nonexistent flag/i); + }); +}); +``` + +**`argument validation`** for topic commands (that route to subcommands): +```typescript +describe("argument validation", () => { + it("should handle unknown subcommand gracefully", async () => { + const { stdout } = await runCommand(["topic", "nonexistent"], import.meta.url); + expect(stdout).toBeDefined(); + }); +}); +``` + +**`flags`** block pattern: +```typescript +describe("flags", () => { + it("should show available flags in help", async () => { + const { stdout } = await runCommand(["topic:action", "--help"], import.meta.url); + expect(stdout).toContain("--json"); + }); + + it("should reject unknown flags", async () => { + const { error } = await runCommand( + ["topic:action", "test-arg", "--unknown-flag"], + import.meta.url, + ); + expect(error).toBeDefined(); + }); +}); +``` diff --git a/.claude/skills/ably-review/SKILL.md b/.claude/skills/ably-review/SKILL.md index 8a91d716..34134b01 100644 --- a/.claude/skills/ably-review/SKILL.md +++ b/.claude/skills/ably-review/SKILL.md @@ -121,9 +121,16 @@ For each changed command file, run the relevant checks. Spawn agents for paralle ### For changed test files (`test/unit/commands/**/*.ts`) -1. **Grep** for `describe(` to check for required describe blocks: `help`, `argument validation`, `functionality`, `flags`, `error handling` +1. **Grep** for `describe(` to check for the 5 required describe blocks with EXACT standard names: + - `describe("help"` — required in every test file + - `describe("argument validation"` — required (test required args OR unknown flag rejection) + - `describe("functionality"` — required (core happy-path tests) + - `describe("flags"` — required (verify flags exist and work) + - `describe("error handling"` — required (API errors, network failures) + Flag any non-standard variants: `"command arguments and flags"`, `"command flags"`, `"flag options"`, `"parameter validation"`. These must use the exact standard names above. + Exception: `interactive.test.ts`, `interactive-sigint.test.ts`, and `bench/*.test.ts` are exempt (REPL/benchmark tests, not command tests). 2. **Grep** for `getMockAblyRealtime`, `getMockAblyRest`, `getMockConfigManager` to verify correct mock usage -3. **Grep** for `--duration` in subscribe test files +3. **Grep** for `--duration` in unit test `runCommand()` args — should NOT be present (env var handles it). Exceptions: `test:wait` tests, `interactive-sigint` tests, help output checks. 4. **Grep** for `--api-key`, `--token`, `--access-token` — unit tests should not use CLI auth flags ### For new command files (added, not modified) diff --git a/AGENTS.md b/AGENTS.md index 8ed3d7f3..fb3e57a9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,10 +5,12 @@ **Run these IN ORDER for EVERY change:** ```bash -pnpm prepare # 1. Build + update manifest/README -pnpm exec eslint . # 2. Lint (MUST be 0 errors) -pnpm test:unit # 3. Test (at minimum) - # 4. Update docs if needed +pnpm prepare # 1. Build + update manifest +pnpm exec oclif readme # 2. Regenerate README.md from command metadata +pnpm exec eslint . # 3. Lint (MUST be 0 errors) +pnpm test:unit # 4. Test (at minimum) +pnpm test:tty # 5. TTY tests (local only, skip in CI) + # 6. Update docs if needed ``` **If you skip these steps, the work is NOT complete.** @@ -164,16 +166,35 @@ runCommand(["stats", "account"], { }); ``` +**Duration in tests — do NOT use `--duration` in unit/integration tests:** +Unit and integration tests set `ABLY_CLI_DEFAULT_DURATION: "0.25"` in `vitest.config.ts`, which makes all subscribe/long-running commands auto-exit after 250ms. Do NOT pass `--duration` to `runCommand()` — it overrides the fast 250ms default with a slower explicit value. + +Exceptions: +- `test:wait` command tests — `--duration` is a required flag for that command +- `interactive-sigint.test.ts` — needs a longer duration for SIGINT testing +- Help output checks — testing that `--help` mentions `--duration` is fine + **Test structure:** - `test/unit/` — Fast, mocked tests. Auth via `MockConfigManager` (automatic). Only set `ABLY_API_KEY` env var when testing env var override behavior. - `test/integration/` — Integration tests (e.g., interactive mode). Mocked external services but tests multi-component interaction. +- `test/tty/` — Interactive mode tests requiring a real pseudo-TTY (e.g., SIGINT/Ctrl+C with readline). Uses `node-pty`. Local only — cannot run in CI. - `test/e2e/` — Full scenarios against real Ably. Auth via env vars (`ABLY_API_KEY`, `ABLY_ACCESS_TOKEN`). - Helpers in `test/helpers/` — `runCommand()`, `runLongRunningBackgroundProcess()`, `e2e-test-helper.ts`, `mock-config-manager.ts`. +**Required test describe blocks** (exact names — every unit test file must have all 5): +1. `"help"` — verify `--help` shows USAGE +2. `"argument validation"` — test required args or unknown flag rejection +3. `"functionality"` — core happy-path behavior +4. `"flags"` — verify flags exist and work +5. `"error handling"` — API errors, network failures + +Do NOT use variants like `"command arguments and flags"`, `"command flags"`, `"flag options"`, or `"parameter validation"`. Exempt: `interactive.test.ts`, `interactive-sigint.test.ts`, `bench/*.test.ts`. + **Running tests:** ```bash pnpm test:unit # All unit tests pnpm test:integration # Integration tests +pnpm test:tty # TTY tests (local only, needs real terminal) pnpm test:e2e # All E2E tests pnpm test test/unit/commands/foo.test.ts # Specific test ``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index edcffa9a..34ef02ef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,9 +8,9 @@ All code changes, whether features or bug fixes, **MUST** follow the mandatory w In summary, this involves: -1. **Build:** Run `pnpm prepare` to compile, update manifests, and update the README. +1. **Build:** Run `pnpm prepare` to compile and update manifests, then `pnpm exec oclif readme` to regenerate the README. 2. **Lint:** Run `pnpm exec eslint .` and fix all errors/warnings. -3. **Test:** Run relevant tests (`pnpm test:unit`, `pnpm test:integration`, `pnpm test:e2e`, `pnpm test:playwright`, or specific files) and ensure they pass. Add new tests or update existing ones as needed. +3. **Test:** Run relevant tests (`pnpm test:unit`, `pnpm test:integration`, `pnpm test:e2e`, `pnpm test:playwright`, or specific files) and ensure they pass. For interactive mode changes, also run `pnpm run test:tty` (requires a real terminal, not run in CI). Add new tests or update existing ones as needed. 4. **Document:** Update all relevant documentation (`docs/`, `AGENTS.md`, `README.md`) to reflect your changes. **Pull requests will not be merged unless all these steps are completed and verified.** diff --git a/README.md b/README.md index 7379c57a..e2bb1dbb 100644 --- a/README.md +++ b/README.md @@ -429,6 +429,8 @@ EXAMPLES $ ably apps channel-rules create --name "chat" --persisted + $ ably apps channel-rules update chat --mutable-messages + $ ably apps channel-rules update chat --push-enabled $ ably apps channel-rules delete chat @@ -444,8 +446,8 @@ Create a channel rule USAGE $ ably apps channel-rules create --name [-v] [--json | --pretty-json] [--app ] [--authenticated] [--batching-enabled] [--batching-interval ] [--conflation-enabled] [--conflation-interval ] - [--conflation-key ] [--expose-time-serial] [--persist-last] [--persisted] [--populate-channel-registry] - [--push-enabled] [--tls-only] + [--conflation-key ] [--expose-time-serial] [--mutable-messages] [--persist-last] [--persisted] + [--populate-channel-registry] [--push-enabled] [--tls-only] FLAGS -v, --verbose Output verbose logs @@ -458,6 +460,8 @@ FLAGS --conflation-key= The conflation key for messages on channels matching this rule --expose-time-serial Whether to expose the time serial for messages on channels matching this rule --json Output in JSON format + --mutable-messages Whether messages on channels matching this rule can be updated or deleted after + publishing. Automatically enables message persistence. --name= (required) Name of the channel rule --persist-last Whether to persist only the last message on channels matching this rule --persisted Whether messages on channels matching this rule should be persisted @@ -472,9 +476,13 @@ DESCRIPTION EXAMPLES $ ably apps channel-rules create --name "chat" --persisted + $ ably apps channel-rules create --name "chat" --mutable-messages + $ ably apps channel-rules create --name "events" --push-enabled $ ably apps channel-rules create --name "notifications" --persisted --push-enabled --app "My App" + + $ ably apps channel-rules create --name "chat" --persisted --json ``` _See code: [src/commands/apps/channel-rules/create.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/apps/channel-rules/create.ts)_ @@ -551,8 +559,8 @@ Update a channel rule USAGE $ ably apps channel-rules update NAMEORID [-v] [--json | --pretty-json] [--app ] [--authenticated] [--batching-enabled] [--batching-interval ] [--conflation-enabled] [--conflation-interval ] - [--conflation-key ] [--expose-time-serial] [--persist-last] [--persisted] [--populate-channel-registry] - [--push-enabled] [--tls-only] + [--conflation-key ] [--expose-time-serial] [--mutable-messages] [--persist-last] [--persisted] + [--populate-channel-registry] [--push-enabled] [--tls-only] ARGUMENTS NAMEORID Name or ID of the channel rule to update @@ -568,6 +576,8 @@ FLAGS --conflation-key= The conflation key for messages on channels matching this rule --[no-]expose-time-serial Whether to expose the time serial for messages on channels matching this rule --json Output in JSON format + --[no-]mutable-messages Whether messages on channels matching this rule can be updated or deleted after + publishing. Automatically enables message persistence. --[no-]persist-last Whether to persist only the last message on channels matching this rule --[no-]persisted Whether messages on channels matching this rule should be persisted --[no-]populate-channel-registry Whether to populate the channel registry for channels matching this rule @@ -581,9 +591,13 @@ DESCRIPTION EXAMPLES $ ably apps channel-rules update chat --persisted + $ ably apps channel-rules update chat --mutable-messages + $ ably apps channel-rules update events --push-enabled=false $ ably apps channel-rules update notifications --persisted --push-enabled --app "My App" + + $ ably apps channel-rules update chat --persisted --json ``` _See code: [src/commands/apps/channel-rules/update.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/apps/channel-rules/update.ts)_ @@ -611,6 +625,8 @@ EXAMPLES $ ably apps create --name "My New App" --tls-only + $ ably apps create --name "My New App" --json + $ ABLY_ACCESS_TOKEN="YOUR_ACCESS_TOKEN" ably apps create --name "My New App" ``` @@ -736,6 +752,8 @@ EXAMPLES $ ably apps set-apns-p12 app-id --certificate /path/to/certificate.p12 --password "YOUR_CERTIFICATE_PASSWORD" $ ably apps set-apns-p12 app-id --certificate /path/to/certificate.p12 --use-for-sandbox + + $ ably apps set-apns-p12 app-id --certificate /path/to/certificate.p12 --json ``` _See code: [src/commands/apps/set-apns-p12.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/apps/set-apns-p12.ts)_ @@ -763,6 +781,8 @@ EXAMPLES $ ably apps switch APP_ID $ ably apps switch + + $ ably apps switch APP_ID --json ``` _See code: [src/commands/apps/switch.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/apps/switch.ts)_ @@ -793,7 +813,7 @@ EXAMPLES $ ably apps update app-id --tls-only - $ ably apps update app-id --name "Updated App Name" --tls-only + $ ably apps update app-id --name "Updated App Name" --json $ ABLY_ACCESS_TOKEN="YOUR_ACCESS_TOKEN" ably apps update app-id --name "Updated App Name" ``` @@ -1135,6 +1155,8 @@ EXAMPLES $ ably auth keys switch APP_ID.KEY_ID $ ably auth keys switch KEY_ID --app APP_ID + + $ ably auth keys switch --json ``` _See code: [src/commands/auth/keys/switch.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/auth/keys/switch.ts)_ @@ -1168,6 +1190,8 @@ EXAMPLES $ ably auth keys update KEY_ID --app APP_ID --capabilities "publish,subscribe" $ ably auth keys update APP_ID.KEY_ID --name "New Name" --capabilities "publish,subscribe" + + $ ably auth keys update APP_ID.KEY_ID --name "New Name" --json ``` _See code: [src/commands/auth/keys/update.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/auth/keys/update.ts)_ @@ -1178,7 +1202,7 @@ Revoke a token ``` USAGE - $ ably auth revoke-token TOKEN [-v] [--json | --pretty-json] [--app ] [-c ] [--debug] + $ ably auth revoke-token TOKEN [-v] [--json | --pretty-json] [--app ] [-c ] ARGUMENTS TOKEN Token to revoke @@ -1187,7 +1211,6 @@ FLAGS -c, --client-id= Client ID to revoke tokens for -v, --verbose Output verbose logs --app= The app ID or name (defaults to current app) - --debug Show debug information --json Output in JSON format --pretty-json Output in colorized JSON format @@ -1266,8 +1289,8 @@ Run a publisher benchmark test ``` USAGE - $ ably bench publisher CHANNEL [-v] [--json | --pretty-json] [--message-size ] [-m ] [-r ] [-t - rest|realtime] [--wait-for-subscribers] + $ ably bench publisher CHANNEL [-v] [--json | --pretty-json] [--client-id ] [--message-size ] [-m + ] [-r ] [-t rest|realtime] [--wait-for-subscribers] ARGUMENTS CHANNEL The channel name to publish to @@ -1278,6 +1301,8 @@ FLAGS -t, --transport=