Skip to content

Commit 57e61ac

Browse files
authored
Merge pull request #118 from ably/add-missing-unit-tests
test: add more unit tests
2 parents e2753ad + e12bea1 commit 57e61ac

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+11163
-5
lines changed

src/commands/logs/channel-lifecycle.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export default class LogsChannelLifecycle extends AblyBaseCommand {
9898
this.log("");
9999
});
100100

101+
// Wait until interrupted
101102
await waitUntilInterruptedOrTimeout();
102103
} catch (error: unknown) {
103104
const err = error as Error;
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
2+
import { runCommand } from "@oclif/test";
3+
import nock from "nock";
4+
import { resolve } from "node:path";
5+
import { mkdirSync, writeFileSync, existsSync, rmSync } from "node:fs";
6+
import { tmpdir } from "node:os";
7+
8+
describe("apps:channel-rules:create command", () => {
9+
const mockAccessToken = "fake_access_token";
10+
const mockAccountId = "test-account-id";
11+
const mockAppId = "550e8400-e29b-41d4-a716-446655440000";
12+
const mockRuleName = "chat";
13+
const mockRuleId = "chat";
14+
let testConfigDir: string;
15+
let originalConfigDir: string;
16+
17+
beforeEach(() => {
18+
process.env.ABLY_ACCESS_TOKEN = mockAccessToken;
19+
20+
testConfigDir = resolve(tmpdir(), `ably-cli-test-${Date.now()}`);
21+
mkdirSync(testConfigDir, { recursive: true, mode: 0o700 });
22+
23+
originalConfigDir = process.env.ABLY_CLI_CONFIG_DIR || "";
24+
process.env.ABLY_CLI_CONFIG_DIR = testConfigDir;
25+
26+
const configContent = `[current]
27+
account = "default"
28+
29+
[accounts.default]
30+
accessToken = "${mockAccessToken}"
31+
accountId = "${mockAccountId}"
32+
accountName = "Test Account"
33+
userEmail = "test@example.com"
34+
currentAppId = "${mockAppId}"
35+
`;
36+
writeFileSync(resolve(testConfigDir, "config"), configContent);
37+
});
38+
39+
afterEach(() => {
40+
nock.cleanAll();
41+
delete process.env.ABLY_ACCESS_TOKEN;
42+
43+
if (originalConfigDir) {
44+
process.env.ABLY_CLI_CONFIG_DIR = originalConfigDir;
45+
} else {
46+
delete process.env.ABLY_CLI_CONFIG_DIR;
47+
}
48+
49+
if (existsSync(testConfigDir)) {
50+
rmSync(testConfigDir, { recursive: true, force: true });
51+
}
52+
});
53+
54+
describe("successful channel rule creation", () => {
55+
it("should create a channel rule successfully", async () => {
56+
nock("https://control.ably.net")
57+
.post(`/v1/apps/${mockAppId}/namespaces`)
58+
.reply(201, {
59+
id: mockRuleId,
60+
persisted: false,
61+
pushEnabled: false,
62+
created: Date.now(),
63+
modified: Date.now(),
64+
});
65+
66+
const { stdout } = await runCommand(
67+
[
68+
"apps:channel-rules:create",
69+
"--name",
70+
mockRuleName,
71+
"--app",
72+
mockAppId,
73+
],
74+
import.meta.url,
75+
);
76+
77+
expect(stdout).toContain("Channel rule created successfully");
78+
expect(stdout).toContain(mockRuleId);
79+
});
80+
81+
it("should create a channel rule with persisted flag", async () => {
82+
nock("https://control.ably.net")
83+
.post(`/v1/apps/${mockAppId}/namespaces`, (body) => {
84+
return body.persisted === true;
85+
})
86+
.reply(201, {
87+
id: mockRuleId,
88+
persisted: true,
89+
pushEnabled: false,
90+
created: Date.now(),
91+
modified: Date.now(),
92+
});
93+
94+
const { stdout } = await runCommand(
95+
[
96+
"apps:channel-rules:create",
97+
"--name",
98+
mockRuleName,
99+
"--app",
100+
mockAppId,
101+
"--persisted",
102+
],
103+
import.meta.url,
104+
);
105+
106+
expect(stdout).toContain("Channel rule created successfully");
107+
expect(stdout).toContain("Persisted: Yes");
108+
});
109+
110+
it("should create a channel rule with push-enabled flag", async () => {
111+
nock("https://control.ably.net")
112+
.post(`/v1/apps/${mockAppId}/namespaces`, (body) => {
113+
return body.pushEnabled === true;
114+
})
115+
.reply(201, {
116+
id: mockRuleId,
117+
persisted: false,
118+
pushEnabled: true,
119+
created: Date.now(),
120+
modified: Date.now(),
121+
});
122+
123+
const { stdout } = await runCommand(
124+
[
125+
"apps:channel-rules:create",
126+
"--name",
127+
mockRuleName,
128+
"--app",
129+
mockAppId,
130+
"--push-enabled",
131+
],
132+
import.meta.url,
133+
);
134+
135+
expect(stdout).toContain("Channel rule created successfully");
136+
expect(stdout).toContain("Push Enabled: Yes");
137+
});
138+
139+
it("should output JSON format when --json flag is used", async () => {
140+
const mockRule = {
141+
id: mockRuleId,
142+
persisted: false,
143+
pushEnabled: false,
144+
created: Date.now(),
145+
modified: Date.now(),
146+
};
147+
148+
nock("https://control.ably.net")
149+
.post(`/v1/apps/${mockAppId}/namespaces`)
150+
.reply(201, mockRule);
151+
152+
const { stdout } = await runCommand(
153+
[
154+
"apps:channel-rules:create",
155+
"--name",
156+
mockRuleName,
157+
"--app",
158+
mockAppId,
159+
"--json",
160+
],
161+
import.meta.url,
162+
);
163+
164+
const result = JSON.parse(stdout);
165+
expect(result).toHaveProperty("success", true);
166+
expect(result).toHaveProperty("rule");
167+
expect(result.rule).toHaveProperty("id", mockRuleId);
168+
});
169+
});
170+
171+
describe("error handling", () => {
172+
it("should require name parameter", async () => {
173+
const { error } = await runCommand(
174+
["apps:channel-rules:create", "--app", mockAppId],
175+
import.meta.url,
176+
);
177+
178+
expect(error).toBeDefined();
179+
expect(error!.message).toMatch(/Missing required flag.*name/);
180+
});
181+
182+
it("should handle 401 authentication error", async () => {
183+
nock("https://control.ably.net")
184+
.post(`/v1/apps/${mockAppId}/namespaces`)
185+
.reply(401, { error: "Unauthorized" });
186+
187+
const { error } = await runCommand(
188+
[
189+
"apps:channel-rules:create",
190+
"--name",
191+
mockRuleName,
192+
"--app",
193+
mockAppId,
194+
],
195+
import.meta.url,
196+
);
197+
198+
expect(error).toBeDefined();
199+
expect(error!.message).toMatch(/401/);
200+
});
201+
202+
it("should handle 400 validation error", async () => {
203+
nock("https://control.ably.net")
204+
.post(`/v1/apps/${mockAppId}/namespaces`)
205+
.reply(400, { error: "Validation failed" });
206+
207+
const { error } = await runCommand(
208+
[
209+
"apps:channel-rules:create",
210+
"--name",
211+
mockRuleName,
212+
"--app",
213+
mockAppId,
214+
],
215+
import.meta.url,
216+
);
217+
218+
expect(error).toBeDefined();
219+
expect(error!.message).toMatch(/400/);
220+
});
221+
222+
it("should handle network errors", async () => {
223+
nock("https://control.ably.net")
224+
.post(`/v1/apps/${mockAppId}/namespaces`)
225+
.replyWithError("Network error");
226+
227+
const { error } = await runCommand(
228+
[
229+
"apps:channel-rules:create",
230+
"--name",
231+
mockRuleName,
232+
"--app",
233+
mockAppId,
234+
],
235+
import.meta.url,
236+
);
237+
238+
expect(error).toBeDefined();
239+
expect(error!.message).toMatch(/Network error/);
240+
});
241+
});
242+
});

0 commit comments

Comments
 (0)