Skip to content

Commit 4de5ecc

Browse files
committed
Add JSON output validation to E2E tests that were only checking exit code
13 E2E tests passed --json but only asserted exitCode === 0, ignoring the response body entirely. Added parseNdjsonLines + assertions for the JSON envelope (success, type) and domain-specific fields (arrays, pagination, nested objects) across spaces, rooms, logs, integrations, and config tests.
1 parent aa462a2 commit 4de5ecc

File tree

7 files changed

+147
-0
lines changed

7 files changed

+147
-0
lines changed

test/e2e/config/config-e2e.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ describe("Config E2E Tests", () => {
5858

5959
// Same as above — just verify it doesn't crash
6060
expect([0, 1]).toContain(result.exitCode);
61+
62+
// Validate JSON structure — both success and error produce valid JSON
63+
const lines = result.stdout
64+
.trim()
65+
.split("\n")
66+
.filter((l) => l.trim().startsWith("{"));
67+
expect(lines.length).toBeGreaterThan(0);
68+
const json = JSON.parse(lines[0]);
69+
expect(json).toHaveProperty("type");
6170
});
6271
});
6372

test/e2e/integrations/integrations-e2e.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ describe.skipIf(SHOULD_SKIP_CONTROL_E2E)("Integrations E2E Tests", () => {
8383
);
8484

8585
expect(listResult.exitCode).toBe(0);
86+
87+
const listRecords = parseNdjsonLines(listResult.stdout);
88+
const listRecord = listRecords.find((r) => r.type === "result");
89+
expect(listRecord).toBeDefined();
90+
expect(listRecord!.success).toBe(true);
91+
expect(Array.isArray(listRecord!.integrations)).toBe(true);
92+
expect(listRecord).toHaveProperty("appId");
93+
expect(listRecord).toHaveProperty("total");
8694
});
8795

8896
it(

test/e2e/logs/logs-e2e.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
cleanupRunners,
2323
} from "../../helpers/command-helpers.js";
2424
import type { CliRunner } from "../../helpers/cli-runner.js";
25+
import { parseNdjsonLines } from "../../helpers/ndjson.js";
2526

2627
describe.skipIf(SHOULD_SKIP_E2E)("Logs E2E Tests", () => {
2728
beforeAll(() => {
@@ -51,6 +52,13 @@ describe.skipIf(SHOULD_SKIP_E2E)("Logs E2E Tests", () => {
5152

5253
// Should succeed even if empty
5354
expect(result.exitCode).toBe(0);
55+
56+
const records = parseNdjsonLines(result.stdout);
57+
const resultRecord = records.find((r) => r.type === "result");
58+
expect(resultRecord).toBeDefined();
59+
expect(resultRecord!.success).toBe(true);
60+
expect(Array.isArray(resultRecord!.messages)).toBe(true);
61+
expect(resultRecord).toHaveProperty("hasMore");
5462
});
5563
});
5664

@@ -163,6 +171,13 @@ describe.skipIf(SHOULD_SKIP_E2E)("Logs E2E Tests", () => {
163171
);
164172

165173
expect(result.exitCode).toBe(0);
174+
175+
const records = parseNdjsonLines(result.stdout);
176+
const resultRecord = records.find((r) => r.type === "result");
177+
expect(resultRecord).toBeDefined();
178+
expect(resultRecord!.success).toBe(true);
179+
expect(Array.isArray(resultRecord!.messages)).toBe(true);
180+
expect(resultRecord).toHaveProperty("hasMore");
166181
});
167182
});
168183

@@ -177,6 +192,13 @@ describe.skipIf(SHOULD_SKIP_E2E)("Logs E2E Tests", () => {
177192

178193
// Should succeed even if empty
179194
expect(result.exitCode).toBe(0);
195+
196+
const records = parseNdjsonLines(result.stdout);
197+
const resultRecord = records.find((r) => r.type === "result");
198+
expect(resultRecord).toBeDefined();
199+
expect(resultRecord!.success).toBe(true);
200+
expect(Array.isArray(resultRecord!.messages)).toBe(true);
201+
expect(resultRecord).toHaveProperty("hasMore");
180202
});
181203

182204
it("should subscribe to push logs", { timeout: 60000 }, async () => {

test/e2e/rooms/rooms-list-e2e.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
resetTestTracking,
1717
} from "../../helpers/e2e-test-helper.js";
1818
import { runCommand } from "../../helpers/command-helpers.js";
19+
import { parseNdjsonLines } from "../../helpers/ndjson.js";
1920

2021
describe.skipIf(SHOULD_SKIP_E2E)("Rooms List E2E Tests", () => {
2122
beforeAll(() => {
@@ -45,6 +46,14 @@ describe.skipIf(SHOULD_SKIP_E2E)("Rooms List E2E Tests", () => {
4546

4647
// Should succeed even if the list is empty
4748
expect(result.exitCode).toBe(0);
49+
50+
const records = parseNdjsonLines(result.stdout);
51+
const resultRecord = records.find((r) => r.type === "result");
52+
expect(resultRecord).toBeDefined();
53+
expect(resultRecord!.success).toBe(true);
54+
expect(Array.isArray(resultRecord!.rooms)).toBe(true);
55+
expect(resultRecord).toHaveProperty("total");
56+
expect(resultRecord).toHaveProperty("hasMore");
4857
});
4958

5059
it("should list rooms with limit", async () => {
@@ -59,6 +68,14 @@ describe.skipIf(SHOULD_SKIP_E2E)("Rooms List E2E Tests", () => {
5968
);
6069

6170
expect(result.exitCode).toBe(0);
71+
72+
const records = parseNdjsonLines(result.stdout);
73+
const resultRecord = records.find((r) => r.type === "result");
74+
expect(resultRecord).toBeDefined();
75+
expect(resultRecord!.success).toBe(true);
76+
expect(Array.isArray(resultRecord!.rooms)).toBe(true);
77+
expect(resultRecord).toHaveProperty("total");
78+
expect(resultRecord).toHaveProperty("hasMore");
6279
});
6380
});
6481
});

test/e2e/spaces/spaces-crud-e2e.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
cleanupRunners,
2424
} from "../../helpers/command-helpers.js";
2525
import type { CliRunner } from "../../helpers/cli-runner.js";
26+
import { parseNdjsonLines } from "../../helpers/ndjson.js";
2627

2728
describe.skipIf(SHOULD_SKIP_E2E)("Spaces CRUD E2E Tests", () => {
2829
beforeAll(() => {
@@ -59,6 +60,14 @@ describe.skipIf(SHOULD_SKIP_E2E)("Spaces CRUD E2E Tests", () => {
5960
);
6061

6162
expect(result.exitCode).toBe(0);
63+
64+
const records = parseNdjsonLines(result.stdout);
65+
const resultRecord = records.find((r) => r.type === "result");
66+
expect(resultRecord).toBeDefined();
67+
expect(resultRecord!.success).toBe(true);
68+
const space = resultRecord!.space as { name: string };
69+
expect(space).toBeDefined();
70+
expect(space.name).toBe(spaceName);
6271
});
6372
});
6473

@@ -73,6 +82,14 @@ describe.skipIf(SHOULD_SKIP_E2E)("Spaces CRUD E2E Tests", () => {
7382

7483
// Should succeed even if the list is empty
7584
expect(result.exitCode).toBe(0);
85+
86+
const records = parseNdjsonLines(result.stdout);
87+
const resultRecord = records.find((r) => r.type === "result");
88+
expect(resultRecord).toBeDefined();
89+
expect(resultRecord!.success).toBe(true);
90+
expect(Array.isArray(resultRecord!.spaces)).toBe(true);
91+
expect(resultRecord).toHaveProperty("total");
92+
expect(resultRecord).toHaveProperty("hasMore");
7693
});
7794
});
7895

@@ -112,6 +129,20 @@ describe.skipIf(SHOULD_SKIP_E2E)("Spaces CRUD E2E Tests", () => {
112129
);
113130

114131
expect(result.exitCode).toBe(0);
132+
133+
const records = parseNdjsonLines(result.stdout);
134+
const resultRecord = records.find((r) => r.type === "result");
135+
expect(resultRecord).toBeDefined();
136+
expect(resultRecord!.success).toBe(true);
137+
const space = resultRecord!.space as {
138+
name: string;
139+
members: Array<{ clientId: string }>;
140+
};
141+
expect(space).toBeDefined();
142+
expect(space.name).toBe(spaceName);
143+
expect(Array.isArray(space.members)).toBe(true);
144+
expect(space.members.length).toBeGreaterThan(0);
145+
expect(space.members[0].clientId).toBe(clientId);
115146
} finally {
116147
if (member) {
117148
await cleanupRunners([member]);

test/e2e/spaces/spaces-locations-e2e.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
cleanupRunners,
2424
} from "../../helpers/command-helpers.js";
2525
import type { CliRunner } from "../../helpers/cli-runner.js";
26+
import { parseNdjsonLines } from "../../helpers/ndjson.js";
2627

2728
describe.skipIf(SHOULD_SKIP_E2E)(
2829
"Spaces Locations, Cursors, and Locks E2E Tests",
@@ -92,6 +93,19 @@ describe.skipIf(SHOULD_SKIP_E2E)(
9293
);
9394

9495
expect(getResult.exitCode).toBe(0);
96+
97+
const records = parseNdjsonLines(getResult.stdout);
98+
const resultRecord = records.find((r) => r.type === "result");
99+
expect(resultRecord).toBeDefined();
100+
expect(resultRecord!.success).toBe(true);
101+
const locations = resultRecord!.locations as Array<{
102+
connectionId: string;
103+
location: unknown;
104+
}>;
105+
expect(locations).toBeDefined();
106+
expect(locations.length).toBeGreaterThan(0);
107+
expect(locations[0]).toHaveProperty("connectionId");
108+
expect(locations[0]).toHaveProperty("location");
95109
} finally {
96110
if (locationRunner) {
97111
await cleanupRunners([locationRunner]);
@@ -144,6 +158,19 @@ describe.skipIf(SHOULD_SKIP_E2E)(
144158
);
145159

146160
expect(getResult.exitCode).toBe(0);
161+
162+
const cursorRecords = parseNdjsonLines(getResult.stdout);
163+
const cursorResult = cursorRecords.find((r) => r.type === "result");
164+
expect(cursorResult).toBeDefined();
165+
expect(cursorResult!.success).toBe(true);
166+
const cursors = cursorResult!.cursors as Array<{
167+
position: { x: number; y: number };
168+
}>;
169+
expect(cursors).toBeDefined();
170+
expect(cursors.length).toBeGreaterThan(0);
171+
expect(cursors[0].position).toBeDefined();
172+
expect(typeof cursors[0].position.x).toBe("number");
173+
expect(typeof cursors[0].position.y).toBe("number");
147174
} finally {
148175
if (cursorRunner) {
149176
await cleanupRunners([cursorRunner]);
@@ -195,6 +222,24 @@ describe.skipIf(SHOULD_SKIP_E2E)(
195222
);
196223

197224
expect(getResult.exitCode).toBe(0);
225+
226+
const lockRecords = parseNdjsonLines(getResult.stdout);
227+
const lockResult = lockRecords.find((r) => r.type === "result");
228+
expect(lockResult).toBeDefined();
229+
expect(lockResult!.success).toBe(true);
230+
const locks = lockResult!.locks as Array<{
231+
id: string;
232+
status: string;
233+
member: { clientId: string };
234+
timestamp: string;
235+
}>;
236+
expect(locks).toBeDefined();
237+
expect(locks.length).toBeGreaterThan(0);
238+
expect(locks[0].id).toBe("test-lock-1");
239+
expect(locks[0].status).toBe("locked");
240+
expect(locks[0].member).toBeDefined();
241+
expect(locks[0].member.clientId).toBe(clientId);
242+
expect(locks[0].timestamp).toBeDefined();
198243
} finally {
199244
if (lockRunner) {
200245
await cleanupRunners([lockRunner]);

test/e2e/spaces/spaces-occupancy-e2e.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
cleanupRunners,
2424
} from "../../helpers/command-helpers.js";
2525
import type { CliRunner } from "../../helpers/cli-runner.js";
26+
import { parseNdjsonLines } from "../../helpers/ndjson.js";
2627

2728
describe.skipIf(SHOULD_SKIP_E2E)("Spaces Occupancy E2E Tests", () => {
2829
beforeAll(() => {
@@ -85,6 +86,20 @@ describe.skipIf(SHOULD_SKIP_E2E)("Spaces Occupancy E2E Tests", () => {
8586
);
8687

8788
expect(result.exitCode).toBe(0);
89+
90+
const records = parseNdjsonLines(result.stdout);
91+
const resultRecord = records.find((r) => r.type === "result");
92+
expect(resultRecord).toBeDefined();
93+
expect(resultRecord!.success).toBe(true);
94+
const occupancy = resultRecord!.occupancy as {
95+
spaceName: string;
96+
metrics: { connections: number; presenceMembers: number };
97+
};
98+
expect(occupancy).toBeDefined();
99+
expect(occupancy.spaceName).toBe(spaceName);
100+
expect(occupancy.metrics).toBeDefined();
101+
expect(typeof occupancy.metrics.connections).toBe("number");
102+
expect(typeof occupancy.metrics.presenceMembers).toBe("number");
88103
} finally {
89104
if (memberRunner) {
90105
await cleanupRunners([memberRunner]);

0 commit comments

Comments
 (0)