Skip to content
Merged
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
14 changes: 6 additions & 8 deletions test/unit/commands/interactive-terminal-behavior.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,10 @@ describe("Interactive Mode - Terminal Behavior Unit Tests", () => {
// Simulate command execution
rl.emit("line", "invalid command");

// Wait for async processing
await new Promise((resolve) => setTimeout(resolve, 100));

// Prompt should be called again after error
expect(promptStub).toHaveBeenCalled();
await vi.waitFor(() => {
expect(promptStub).toHaveBeenCalled();
});
});

it("should handle special characters in autocomplete", async () => {
Expand Down Expand Up @@ -393,11 +392,10 @@ describe("Interactive Mode - Terminal Behavior Unit Tests", () => {
// Simulate exit command
rl.emit("line", "exit");

// Wait for async processing
await new Promise((resolve) => setTimeout(resolve, 100));

// Readline should be closed
expect(closeStub).toHaveBeenCalled();
await vi.waitFor(() => {
expect(closeStub).toHaveBeenCalled();
});

await vi.waitFor(() => {
expect(exitStub).toHaveBeenCalled();
Expand Down
187 changes: 93 additions & 94 deletions test/unit/commands/logs/connection-lifecycle/subscribe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,109 +100,108 @@ describe("LogsConnectionLifecycleSubscribe", function () {
const mock = getMockAblyRealtime();
const channel = mock.channels._getChannel("[meta]connection.lifecycle");

// Capture the subscription callback
let messageCallback: ((message: unknown) => void) | null = null;
channel.subscribe.mockImplementation(
(callback: (message: unknown) => void) => {
messageCallback = callback;
},
);

// Simulate receiving a message
setTimeout(() => {
if (messageCallback) {
messageCallback({
name: "connection.opened",
data: {
connectionId: "test-connection-123",
transport: "websocket",
ipAddress: "192.168.1.1",
},
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-123",
id: "msg-123",
});
}
}, 50);

const { stdout } = await runCommand(
const commandPromise = runCommand(
["logs:connection-lifecycle:subscribe"],
import.meta.url,
);

await vi.waitFor(() => {
expect(messageCallback).not.toBeNull();
});

messageCallback!({
name: "connection.opened",
data: {
connectionId: "test-connection-123",
transport: "websocket",
ipAddress: "192.168.1.1",
},
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-123",
id: "msg-123",
});

const { stdout } = await commandPromise;

expect(stdout).toContain("connection.opened");
});

it("should output JSON when requested", async function () {
const mock = getMockAblyRealtime();
const channel = mock.channels._getChannel("[meta]connection.lifecycle");

// Capture the subscription callback
let messageCallback: ((message: unknown) => void) | null = null;
channel.subscribe.mockImplementation(
(callback: (message: unknown) => void) => {
messageCallback = callback;
},
);

// Simulate receiving a message
setTimeout(() => {
if (messageCallback) {
messageCallback({
name: "connection.opened",
data: { connectionId: "test-connection-123" },
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-123",
id: "msg-123",
});
}
}, 50);

const { stdout } = await runCommand(
const commandPromise = runCommand(
["logs:connection-lifecycle:subscribe", "--json"],
import.meta.url,
);

// Verify JSON output - the output contains the event name in JSON format
await vi.waitFor(() => {
expect(messageCallback).not.toBeNull();
});

messageCallback!({
name: "connection.opened",
data: { connectionId: "test-connection-123" },
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-123",
id: "msg-123",
});

const { stdout } = await commandPromise;

expect(stdout).toContain("connection.opened");
});

it("should handle connection state changes", async function () {
const mock = getMockAblyRealtime();
const channel = mock.channels._getChannel("[meta]connection.lifecycle");

// Capture the subscription callback
let messageCallback: ((message: unknown) => void) | null = null;
channel.subscribe.mockImplementation(
(callback: (message: unknown) => void) => {
messageCallback = callback;
},
);

// Simulate receiving a connection state change event
setTimeout(() => {
if (messageCallback) {
messageCallback({
name: "connection.connected",
data: {
connectionId: "test-connection-456",
transport: "websocket",
},
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-456",
id: "msg-state-change",
});
}
}, 50);

const { stdout } = await runCommand(
const commandPromise = runCommand(
["logs:connection-lifecycle:subscribe"],
import.meta.url,
);

await vi.waitFor(() => {
expect(messageCallback).not.toBeNull();
});

messageCallback!({
name: "connection.connected",
data: {
connectionId: "test-connection-456",
transport: "websocket",
},
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-456",
id: "msg-state-change",
});

const { stdout } = await commandPromise;

expect(channel.subscribe).toHaveBeenCalled();
expect(stdout).toContain("connection.connected");
});
Expand All @@ -211,73 +210,73 @@ describe("LogsConnectionLifecycleSubscribe", function () {
const mock = getMockAblyRealtime();
const channel = mock.channels._getChannel("[meta]connection.lifecycle");

// Capture the subscription callback
let messageCallback: ((message: unknown) => void) | null = null;
channel.subscribe.mockImplementation(
(callback: (message: unknown) => void) => {
messageCallback = callback;
},
);

// Simulate receiving different event types
setTimeout(() => {
if (messageCallback) {
messageCallback({
name: "connection.closed",
data: {
connectionId: "test-connection-123",
reason: "client closed",
},
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-123",
id: "msg-456",
});
}
}, 50);

const { stdout } = await runCommand(
const commandPromise = runCommand(
["logs:connection-lifecycle:subscribe"],
import.meta.url,
);

await vi.waitFor(() => {
expect(messageCallback).not.toBeNull();
});

messageCallback!({
name: "connection.closed",
data: {
connectionId: "test-connection-123",
reason: "client closed",
},
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-123",
id: "msg-456",
});

const { stdout } = await commandPromise;

expect(stdout).toContain("connection.closed");
});

it("should handle channel state changes", async function () {
const mock = getMockAblyRealtime();
const channel = mock.channels._getChannel("[meta]connection.lifecycle");

// Capture the subscription callback
let messageCallback: ((message: unknown) => void) | null = null;
channel.subscribe.mockImplementation(
(callback: (message: unknown) => void) => {
messageCallback = callback;
},
);

// Simulate receiving a channel state change event
setTimeout(() => {
if (messageCallback) {
messageCallback({
name: "channel.attached",
data: {
channelName: "test-channel",
state: "attached",
},
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-123",
id: "msg-channel-state",
});
}
}, 50);

const { stdout } = await runCommand(
const commandPromise = runCommand(
["logs:connection-lifecycle:subscribe"],
import.meta.url,
);

await vi.waitFor(() => {
expect(messageCallback).not.toBeNull();
});

messageCallback!({
name: "channel.attached",
data: {
channelName: "test-channel",
state: "attached",
},
timestamp: Date.now(),
clientId: "test-client",
connectionId: "test-connection-123",
id: "msg-channel-state",
});

const { stdout } = await commandPromise;

expect(channel.subscribe).toHaveBeenCalled();
expect(stdout).toContain("channel.attached");
});
Expand Down
49 changes: 24 additions & 25 deletions test/unit/commands/spaces/locations/subscribe.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect, beforeEach } from "vitest";
import { describe, it, expect, beforeEach, vi } from "vitest";
import { runCommand } from "@oclif/test";
import { getMockAblySpaces } from "../../../../helpers/mock-ably-spaces.js";
import { getMockAblyRealtime } from "../../../../helpers/mock-ably-realtime.js";
Expand Down Expand Up @@ -69,19 +69,18 @@ describe("spaces:locations:subscribe command", () => {
import.meta.url,
);

// Wait a tick for the subscribe to be set up
await new Promise((resolve) => setTimeout(resolve, 50));
await vi.waitFor(() => {
expect(locationHandler).toBeDefined();
});

if (locationHandler) {
locationHandler({
member: {
clientId: "user-1",
connectionId: "conn-1",
},
currentLocation: { room: "lobby" },
previousLocation: { room: "entrance" },
});
}
locationHandler!({
member: {
clientId: "user-1",
connectionId: "conn-1",
},
currentLocation: { room: "lobby" },
previousLocation: { room: "entrance" },
});

const { stdout } = await runPromise;

Expand Down Expand Up @@ -109,18 +108,18 @@ describe("spaces:locations:subscribe command", () => {
import.meta.url,
);

await new Promise((resolve) => setTimeout(resolve, 50));

if (locationHandler) {
locationHandler({
member: {
clientId: "user-1",
connectionId: "conn-1",
},
currentLocation: { room: "lobby" },
previousLocation: null,
});
}
await vi.waitFor(() => {
expect(locationHandler).toBeDefined();
});

locationHandler!({
member: {
clientId: "user-1",
connectionId: "conn-1",
},
currentLocation: { room: "lobby" },
previousLocation: null,
});

const { stdout } = await runPromise;

Expand Down
9 changes: 4 additions & 5 deletions test/unit/hooks/interactive-did-you-mean.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,10 @@ describe("Did You Mean Hook - Interactive Mode", function () {
context,
});

// Wait for async restoration
await new Promise((resolve) => setTimeout(resolve, 30));
// Wait for async restoration — all state changes happen together in the hook's cleanup path
await vi.waitFor(() => {
expect(mockReadline.resume).toHaveBeenCalled();
});

// Verify readline was paused during prompt
expect(mockReadline.pause).toHaveBeenCalled();
Expand All @@ -347,9 +349,6 @@ describe("Did You Mean Hook - Interactive Mode", function () {
expect(mockReadline.on.mock.calls[index]).toEqual(["line", listener]);
});

// Verify readline was resumed
expect(mockReadline.resume).toHaveBeenCalled();

// Verify terminal state was restored
expect(process.stdin.setRawMode).toHaveBeenCalledWith(false);
} finally {
Expand Down
Loading