Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
env:
NO_COLOR: 1
E2E_ABLY_API_KEY: ${{ secrets.E2E_ABLY_API_KEY }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ACCESS_TOKEN }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ABLY_ACCESS_TOKEN }}
TERMINAL_SERVER_SIGNING_SECRET: ${{ secrets.TERMINAL_SERVER_SIGNING_SECRET }}
E2E_TESTS: true

Expand Down Expand Up @@ -69,7 +69,7 @@ jobs:
run: |
echo "ABLY_API_KEY=${{ secrets.E2E_ABLY_API_KEY }}" > .env.test
echo "E2E_ABLY_API_KEY=${{ secrets.E2E_ABLY_API_KEY }}" >> .env.test
echo "E2E_ABLY_ACCESS_TOKEN=${{ secrets.E2E_ABLY_ACCESS_TOKEN }}" >> .env.test
echo "E2E_ABLY_ACCESS_TOKEN=${{ secrets.E2E_ABLY_ABLY_ACCESS_TOKEN }}" >> .env.test
echo "TERMINAL_SERVER_SIGNING_SECRET=${{ secrets.TERMINAL_SERVER_SIGNING_SECRET }}" >> .env.test

- name: Run All E2E CLI Tests
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/e2e-web-cli-parallel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
env:
NO_COLOR: 1
E2E_ABLY_API_KEY: ${{ secrets.E2E_ABLY_API_KEY }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ACCESS_TOKEN }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ABLY_ACCESS_TOKEN }}
TERMINAL_SERVER_SIGNING_SECRET: ${{ secrets.TERMINAL_SERVER_SIGNING_SECRET }}
E2E_TESTS: true
HEADLESS: true
Expand Down Expand Up @@ -150,7 +150,7 @@ jobs:
env:
NO_COLOR: 1
E2E_ABLY_API_KEY: ${{ secrets.E2E_ABLY_API_KEY }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ACCESS_TOKEN }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ABLY_ACCESS_TOKEN }}
TERMINAL_SERVER_SIGNING_SECRET: ${{ secrets.TERMINAL_SERVER_SIGNING_SECRET }}
E2E_TESTS: true
HEADLESS: true
Expand Down Expand Up @@ -216,7 +216,7 @@ jobs:
env:
NO_COLOR: 1
E2E_ABLY_API_KEY: ${{ secrets.E2E_ABLY_API_KEY }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ACCESS_TOKEN }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ABLY_ACCESS_TOKEN }}
TERMINAL_SERVER_SIGNING_SECRET: ${{ secrets.TERMINAL_SERVER_SIGNING_SECRET }}
E2E_TESTS: true
HEADLESS: true
Expand Down Expand Up @@ -282,7 +282,7 @@ jobs:
env:
NO_COLOR: 1
E2E_ABLY_API_KEY: ${{ secrets.E2E_ABLY_API_KEY }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ACCESS_TOKEN }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ABLY_ACCESS_TOKEN }}
# Terminal Server Signing Secret is intentionally not set
# Rate limit test should NOT use terminal server signing secret.
E2E_TESTS: true
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,16 @@ jobs:
run: pnpm test:unit
env:
E2E_ABLY_API_KEY: ${{ secrets.E2E_ABLY_API_KEY }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ACCESS_TOKEN }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ABLY_ACCESS_TOKEN }}

- name: Run CLI Hooks Unit Tests
run: pnpm test:hooks
env:
E2E_ABLY_API_KEY: ${{ secrets.E2E_ABLY_API_KEY }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ACCESS_TOKEN }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ABLY_ACCESS_TOKEN }}

- name: Run CLI Integration Tests
run: pnpm test:integration
env:
E2E_ABLY_API_KEY: ${{ secrets.E2E_ABLY_API_KEY }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ACCESS_TOKEN }}
E2E_ABLY_ACCESS_TOKEN: ${{ secrets.E2E_ABLY_ABLY_ACCESS_TOKEN }}
5 changes: 5 additions & 0 deletions src/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,11 @@ export abstract class AblyBaseCommand extends InteractiveBaseCommand {
let appId = flags.app || this.configManager.getCurrentAppId();
let apiKey = this.configManager.getApiKey(appId);

// When apiKey comes from ABLY_API_KEY env var but appId is missing, extract it from the key
if (apiKey && !appId) {
appId = apiKey.split(".")[0] || "";
}

// If we have both, return them
if (appId && apiKey) {
return { apiKey, appId };
Expand Down
9 changes: 0 additions & 9 deletions src/commands/logs/subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,6 @@ export default class LogsSubscribe extends AblyBaseCommand {
includeUserFriendlyMessages: true,
});

// Get the logs channel
const appConfig = await this.ensureAppAndKey(flags);
if (!appConfig) {
this.fail(
"Unable to determine app configuration",
flags,
"logSubscribe",
);
}
const logsChannelName = `[meta]log`;

// Configure channel options for rewind if specified
Expand Down
27 changes: 15 additions & 12 deletions src/commands/rooms/typing/keystroke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,20 +158,23 @@ export default class TypingKeystroke extends ChatBaseCommand {
);
});
}, KEYSTROKE_INTERVAL);
}

this.logCliEvent(
flags,
"typing",
"listening",
"Maintaining typing status...",
);
this.logCliEvent(
flags,
"typing",
"listening",
"Maintaining typing status...",
);

// Wait until the user interrupts, duration elapses, or the room fails
await Promise.race([
this.waitAndTrackCleanup(flags, "typing", flags.duration),
failurePromise,
]);
// Wait until the user interrupts, duration elapses, or the room fails
await Promise.race([
this.waitAndTrackCleanup(flags, "typing", flags.duration),
failurePromise,
]);
} else {
// Suppress unhandled rejection — failurePromise exists from setupRoomStatusHandler
failurePromise.catch(() => {});
}
} catch (error) {
this.fail(error, flags, "roomTypingKeystroke", { room: args.room });
}
Expand Down
1 change: 1 addition & 0 deletions src/commands/spaces/cursors/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default class SpacesCursorsGet extends SpacesBaseCommand {
flags,
);

await this.waitForCursorsChannelAttachment(flags);
const allCursors = await this.space!.cursors.getAll();

const cursors: CursorUpdate[] = Object.values(allCursors).filter(
Expand Down
9 changes: 8 additions & 1 deletion src/services/control-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,14 @@ export class ControlApi {
}

async getNamespace(appId: string, namespaceId: string): Promise<Namespace> {
return this.request<Namespace>(`/apps/${appId}/namespaces/${namespaceId}`);
// Individual namespace GET endpoint is no longer available;
// list all namespaces and filter by ID instead.
const namespaces = await this.listNamespaces(appId);
const namespace = namespaces.find((ns) => ns.id === namespaceId);
if (!namespace) {
throw new Error(`Namespace with ID "${namespaceId}" not found`);
}
return namespace;
}

async getRule(appId: string, ruleId: string): Promise<Rule> {
Expand Down
68 changes: 68 additions & 0 deletions test/e2e/accounts/accounts-e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
describe,
it,
beforeEach,
afterEach,
beforeAll,
afterAll,
expect,
} from "vitest";
import {
E2E_ACCESS_TOKEN,
SHOULD_SKIP_CONTROL_E2E,
forceExit,
cleanupTrackedResources,
setupTestFailureHandler,
resetTestTracking,
} from "../../helpers/e2e-test-helper.js";
import { runCommand } from "../../helpers/command-helpers.js";

describe.skipIf(SHOULD_SKIP_CONTROL_E2E)("Accounts E2E Tests", () => {
beforeAll(() => {
process.on("SIGINT", forceExit);
});

afterAll(() => {
process.removeListener("SIGINT", forceExit);
});

beforeEach(() => {
resetTestTracking();
});

afterEach(async () => {
await cleanupTrackedResources();
});

it(
"should list locally configured accounts",
{ timeout: 15000 },
async () => {
setupTestFailureHandler("should list locally configured accounts");

// accounts list reads from local config, not the API directly.
// In E2E environment, there may or may not be configured accounts.
// We just verify the command runs without crashing.
const listResult = await runCommand(["accounts", "list"], {
env: { ABLY_ACCESS_TOKEN: E2E_ACCESS_TOKEN || "" },
});

// The command may exit 0 (accounts found) or non-zero (no accounts configured).
// Either way, it should produce output and not crash.
const combinedOutput = listResult.stdout + listResult.stderr;
expect(combinedOutput.length).toBeGreaterThan(0);
},
);

it("should show help for accounts current", { timeout: 10000 }, async () => {
setupTestFailureHandler("should show help for accounts current");

const helpResult = await runCommand(["accounts", "current", "--help"], {
env: { ABLY_ACCESS_TOKEN: E2E_ACCESS_TOKEN || "" },
});

expect(helpResult.exitCode).toBe(0);
const output = helpResult.stdout + helpResult.stderr;
expect(output).toContain("USAGE");
});
});
Loading
Loading