Skip to content

Commit 800393b

Browse files
committed
test: add unit tests for express checkup command
Add comprehensive tests for the checkup module including: - parseVersionNum function with various PG versions - createBaseReport structure validation - CHECK_INFO and REPORT_GENERATORS constants - Mock client tests for getPostgresVersion, getSettings - Report generators (A002, A003, A013) - CLI help output and error handling
1 parent 8ca45b1 commit 800393b

File tree

1 file changed

+295
-0
lines changed

1 file changed

+295
-0
lines changed

cli/test/checkup.test.cjs

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
const test = require("node:test");
2+
const assert = require("node:assert/strict");
3+
4+
// These tests intentionally import the compiled JS output.
5+
// Run via: npm --prefix cli test
6+
const checkup = require("../dist/lib/checkup.js");
7+
8+
function runCli(args, env = {}) {
9+
const { spawnSync } = require("node:child_process");
10+
const path = require("node:path");
11+
const node = process.execPath;
12+
const cliPath = path.resolve(__dirname, "..", "dist", "bin", "postgres-ai.js");
13+
return spawnSync(node, [cliPath, ...args], {
14+
encoding: "utf8",
15+
env: { ...process.env, ...env },
16+
});
17+
}
18+
19+
// Unit tests for parseVersionNum
20+
test("parseVersionNum parses PG 16.3 version number", () => {
21+
const result = checkup.parseVersionNum("160003");
22+
assert.equal(result.major, "16");
23+
assert.equal(result.minor, "3");
24+
});
25+
26+
test("parseVersionNum parses PG 15.7 version number", () => {
27+
const result = checkup.parseVersionNum("150007");
28+
assert.equal(result.major, "15");
29+
assert.equal(result.minor, "7");
30+
});
31+
32+
test("parseVersionNum parses PG 14.12 version number", () => {
33+
const result = checkup.parseVersionNum("140012");
34+
assert.equal(result.major, "14");
35+
assert.equal(result.minor, "12");
36+
});
37+
38+
test("parseVersionNum handles empty string", () => {
39+
const result = checkup.parseVersionNum("");
40+
assert.equal(result.major, "");
41+
assert.equal(result.minor, "");
42+
});
43+
44+
test("parseVersionNum handles null/undefined", () => {
45+
const result = checkup.parseVersionNum(null);
46+
assert.equal(result.major, "");
47+
assert.equal(result.minor, "");
48+
});
49+
50+
test("parseVersionNum handles short string", () => {
51+
const result = checkup.parseVersionNum("123");
52+
assert.equal(result.major, "");
53+
assert.equal(result.minor, "");
54+
});
55+
56+
// Unit tests for createBaseReport
57+
test("createBaseReport creates correct structure", () => {
58+
const report = checkup.createBaseReport("A002", "Postgres major version", "test-node");
59+
60+
assert.equal(report.checkId, "A002");
61+
assert.equal(report.checkTitle, "Postgres major version");
62+
assert.equal(report.version, null);
63+
assert.equal(report.build_ts, null);
64+
assert.equal(report.nodes.primary, "test-node");
65+
assert.deepEqual(report.nodes.standbys, []);
66+
assert.deepEqual(report.results, {});
67+
assert.ok(typeof report.timestamptz === "string");
68+
// Verify timestamp is ISO format
69+
assert.ok(new Date(report.timestamptz).toISOString() === report.timestamptz);
70+
});
71+
72+
test("createBaseReport uses provided node name", () => {
73+
const report = checkup.createBaseReport("A003", "Postgres settings", "my-custom-node");
74+
assert.equal(report.nodes.primary, "my-custom-node");
75+
});
76+
77+
// Tests for CHECK_INFO
78+
test("CHECK_INFO contains A002", () => {
79+
assert.ok("A002" in checkup.CHECK_INFO);
80+
assert.equal(checkup.CHECK_INFO.A002, "Postgres major version");
81+
});
82+
83+
test("CHECK_INFO contains A003", () => {
84+
assert.ok("A003" in checkup.CHECK_INFO);
85+
assert.equal(checkup.CHECK_INFO.A003, "Postgres settings");
86+
});
87+
88+
test("CHECK_INFO contains A013", () => {
89+
assert.ok("A013" in checkup.CHECK_INFO);
90+
assert.equal(checkup.CHECK_INFO.A013, "Postgres minor version");
91+
});
92+
93+
// Tests for REPORT_GENERATORS
94+
test("REPORT_GENERATORS has generator for A002", () => {
95+
assert.ok("A002" in checkup.REPORT_GENERATORS);
96+
assert.equal(typeof checkup.REPORT_GENERATORS.A002, "function");
97+
});
98+
99+
test("REPORT_GENERATORS has generator for A003", () => {
100+
assert.ok("A003" in checkup.REPORT_GENERATORS);
101+
assert.equal(typeof checkup.REPORT_GENERATORS.A003, "function");
102+
});
103+
104+
test("REPORT_GENERATORS has generator for A013", () => {
105+
assert.ok("A013" in checkup.REPORT_GENERATORS);
106+
assert.equal(typeof checkup.REPORT_GENERATORS.A013, "function");
107+
});
108+
109+
test("REPORT_GENERATORS and CHECK_INFO have same keys", () => {
110+
const generatorKeys = Object.keys(checkup.REPORT_GENERATORS).sort();
111+
const infoKeys = Object.keys(checkup.CHECK_INFO).sort();
112+
assert.deepEqual(generatorKeys, infoKeys);
113+
});
114+
115+
// Tests for METRICS_SQL
116+
test("METRICS_SQL.settings queries pg_settings", () => {
117+
assert.ok(checkup.METRICS_SQL.settings.includes("pg_settings"));
118+
assert.ok(checkup.METRICS_SQL.settings.includes("name"));
119+
assert.ok(checkup.METRICS_SQL.settings.includes("setting"));
120+
});
121+
122+
test("METRICS_SQL.version queries server_version fields", () => {
123+
assert.ok(checkup.METRICS_SQL.version.includes("server_version"));
124+
assert.ok(checkup.METRICS_SQL.version.includes("server_version_num"));
125+
});
126+
127+
// Mock client tests for report generators
128+
function createMockClient(versionRows, settingsRows) {
129+
return {
130+
query: async (sql) => {
131+
if (sql.includes("server_version")) {
132+
return { rows: versionRows };
133+
}
134+
if (sql.includes("pg_settings") && sql.includes("ORDER BY")) {
135+
return { rows: settingsRows };
136+
}
137+
throw new Error(`Unexpected query: ${sql}`);
138+
},
139+
};
140+
}
141+
142+
test("getPostgresVersion extracts version info from mock client", async () => {
143+
const mockClient = createMockClient([
144+
{ name: "server_version", setting: "16.3" },
145+
{ name: "server_version_num", setting: "160003" },
146+
], []);
147+
148+
const version = await checkup.getPostgresVersion(mockClient);
149+
assert.equal(version.version, "16.3");
150+
assert.equal(version.server_version_num, "160003");
151+
assert.equal(version.server_major_ver, "16");
152+
assert.equal(version.server_minor_ver, "3");
153+
});
154+
155+
test("getSettings transforms rows to keyed object", async () => {
156+
const mockClient = createMockClient([], [
157+
{
158+
name: "shared_buffers",
159+
setting: "16384",
160+
unit: "8kB",
161+
category: "Resource Usage / Memory",
162+
context: "postmaster",
163+
vartype: "integer",
164+
pretty_value: "128 MB",
165+
},
166+
{
167+
name: "work_mem",
168+
setting: "4096",
169+
unit: "kB",
170+
category: "Resource Usage / Memory",
171+
context: "user",
172+
vartype: "integer",
173+
pretty_value: "4 MB",
174+
},
175+
]);
176+
177+
const settings = await checkup.getSettings(mockClient);
178+
assert.ok("shared_buffers" in settings);
179+
assert.ok("work_mem" in settings);
180+
assert.equal(settings.shared_buffers.setting, "16384");
181+
assert.equal(settings.shared_buffers.unit, "8kB");
182+
assert.equal(settings.work_mem.pretty_value, "4 MB");
183+
});
184+
185+
test("generateA002 creates report with version data", async () => {
186+
const mockClient = createMockClient([
187+
{ name: "server_version", setting: "16.3" },
188+
{ name: "server_version_num", setting: "160003" },
189+
], []);
190+
191+
const report = await checkup.generateA002(mockClient, "test-node");
192+
assert.equal(report.checkId, "A002");
193+
assert.equal(report.checkTitle, "Postgres major version");
194+
assert.equal(report.nodes.primary, "test-node");
195+
assert.ok("test-node" in report.results);
196+
assert.ok("version" in report.results["test-node"].data);
197+
assert.equal(report.results["test-node"].data.version.version, "16.3");
198+
});
199+
200+
test("generateA003 creates report with settings and version", async () => {
201+
const mockClient = createMockClient(
202+
[
203+
{ name: "server_version", setting: "16.3" },
204+
{ name: "server_version_num", setting: "160003" },
205+
],
206+
[
207+
{
208+
name: "shared_buffers",
209+
setting: "16384",
210+
unit: "8kB",
211+
category: "Resource Usage / Memory",
212+
context: "postmaster",
213+
vartype: "integer",
214+
pretty_value: "128 MB",
215+
},
216+
]
217+
);
218+
219+
const report = await checkup.generateA003(mockClient, "test-node");
220+
assert.equal(report.checkId, "A003");
221+
assert.equal(report.checkTitle, "Postgres settings");
222+
assert.ok("test-node" in report.results);
223+
assert.ok("shared_buffers" in report.results["test-node"].data);
224+
assert.ok(report.results["test-node"].postgres_version);
225+
assert.equal(report.results["test-node"].postgres_version.version, "16.3");
226+
});
227+
228+
test("generateA013 creates report with minor version data", async () => {
229+
const mockClient = createMockClient([
230+
{ name: "server_version", setting: "16.3" },
231+
{ name: "server_version_num", setting: "160003" },
232+
], []);
233+
234+
const report = await checkup.generateA013(mockClient, "test-node");
235+
assert.equal(report.checkId, "A013");
236+
assert.equal(report.checkTitle, "Postgres minor version");
237+
assert.equal(report.nodes.primary, "test-node");
238+
assert.ok("test-node" in report.results);
239+
assert.ok("version" in report.results["test-node"].data);
240+
assert.equal(report.results["test-node"].data.version.server_minor_ver, "3");
241+
});
242+
243+
test("generateAllReports returns reports for all checks", async () => {
244+
const mockClient = createMockClient(
245+
[
246+
{ name: "server_version", setting: "16.3" },
247+
{ name: "server_version_num", setting: "160003" },
248+
],
249+
[
250+
{
251+
name: "shared_buffers",
252+
setting: "16384",
253+
unit: "8kB",
254+
category: "Resource Usage / Memory",
255+
context: "postmaster",
256+
vartype: "integer",
257+
pretty_value: "128 MB",
258+
},
259+
]
260+
);
261+
262+
const reports = await checkup.generateAllReports(mockClient, "test-node");
263+
assert.ok("A002" in reports);
264+
assert.ok("A003" in reports);
265+
assert.ok("A013" in reports);
266+
assert.equal(reports.A002.checkId, "A002");
267+
assert.equal(reports.A003.checkId, "A003");
268+
assert.equal(reports.A013.checkId, "A013");
269+
});
270+
271+
// CLI tests
272+
test("cli: checkup command exists and shows help", () => {
273+
const r = runCli(["checkup", "--help"]);
274+
assert.equal(r.status, 0, r.stderr || r.stdout);
275+
assert.match(r.stdout, /express mode/i);
276+
assert.match(r.stdout, /--check-id/);
277+
assert.match(r.stdout, /--node-name/);
278+
assert.match(r.stdout, /--output/);
279+
assert.match(r.stdout, /--json/);
280+
});
281+
282+
test("cli: checkup --help shows available check IDs", () => {
283+
const r = runCli(["checkup", "--help"]);
284+
assert.equal(r.status, 0, r.stderr || r.stdout);
285+
assert.match(r.stdout, /A002/);
286+
assert.match(r.stdout, /A003/);
287+
assert.match(r.stdout, /A013/);
288+
});
289+
290+
test("cli: checkup without connection shows error", () => {
291+
const r = runCli(["checkup"]);
292+
assert.notEqual(r.status, 0);
293+
// Should show connection required error
294+
assert.match(r.stderr, /connection|required|PostgreSQL/i);
295+
});

0 commit comments

Comments
 (0)