postgresai 0.14.0-dev.51 → 0.14.0-dev.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,260 +0,0 @@
1
- /**
2
- * JSON Schema validation tests for H001, H002, H004 express checkup reports.
3
- * These tests validate that the generated reports match the schemas in reporter/schemas/.
4
- */
5
- import { describe, test, expect } from "bun:test";
6
- import { resolve } from "path";
7
- import { readFileSync } from "fs";
8
- import Ajv2020 from "ajv/dist/2020";
9
-
10
- import * as checkup from "../lib/checkup";
11
-
12
- const ajv = new Ajv2020({ allErrors: true, strict: false });
13
- const schemasDir = resolve(import.meta.dir, "../../reporter/schemas");
14
-
15
- function loadSchema(checkId: string): object {
16
- const schemaPath = resolve(schemasDir, `${checkId}.schema.json`);
17
- return JSON.parse(readFileSync(schemaPath, "utf8"));
18
- }
19
-
20
- function validateReport(report: any, checkId: string): { valid: boolean; errors: string[] } {
21
- const schema = loadSchema(checkId);
22
- const validate = ajv.compile(schema);
23
- const valid = validate(report);
24
- const errors = validate.errors?.map(e => `${e.instancePath}: ${e.message}`) || [];
25
- return { valid: !!valid, errors };
26
- }
27
-
28
- // Mock client for testing
29
- function createMockClient(options: {
30
- versionRows?: any[];
31
- settingsRows?: any[];
32
- invalidIndexesRows?: any[];
33
- unusedIndexesRows?: any[];
34
- redundantIndexesRows?: any[];
35
- } = {}) {
36
- const {
37
- versionRows = [
38
- { name: "server_version", setting: "16.3" },
39
- { name: "server_version_num", setting: "160003" },
40
- ],
41
- settingsRows = [
42
- { name: "shared_buffers", setting: "128MB", unit: "", category: "Resource Usage / Memory", context: "postmaster", vartype: "string", pretty_value: "128 MB" },
43
- { name: "work_mem", setting: "4MB", unit: "", category: "Resource Usage / Memory", context: "user", vartype: "string", pretty_value: "4 MB" },
44
- { name: "autovacuum", setting: "on", unit: "", category: "Autovacuum", context: "sighup", vartype: "bool", pretty_value: "on" },
45
- { name: "pg_stat_statements.max", setting: "5000", unit: "", category: "Custom", context: "superuser", vartype: "integer", pretty_value: "5000" },
46
- ],
47
- invalidIndexesRows = [],
48
- unusedIndexesRows = [],
49
- redundantIndexesRows = [],
50
- } = options;
51
-
52
- return {
53
- query: async (sql: string) => {
54
- if (sql.includes("server_version") && sql.includes("server_version_num") && !sql.includes("order by")) {
55
- return { rows: versionRows };
56
- }
57
- // Full settings query
58
- if (sql.includes("pg_settings") && sql.includes("order by") && sql.includes("is_default")) {
59
- return { rows: settingsRows };
60
- }
61
- if (sql.includes("current_database()") && sql.includes("pg_database_size")) {
62
- return { rows: [{ datname: "testdb", size_bytes: "1073741824" }] };
63
- }
64
- if (sql.includes("stats_reset") && sql.includes("pg_stat_database")) {
65
- return { rows: [{
66
- stats_reset_epoch: "1704067200",
67
- stats_reset_time: "2024-01-01 00:00:00+00",
68
- days_since_reset: "30",
69
- postmaster_startup_epoch: "1704067200",
70
- postmaster_startup_time: "2024-01-01 00:00:00+00"
71
- }] };
72
- }
73
- if (sql.includes("indisvalid = false")) {
74
- return { rows: invalidIndexesRows };
75
- }
76
- if (sql.includes("Never Used Indexes") && sql.includes("idx_scan = 0")) {
77
- return { rows: unusedIndexesRows };
78
- }
79
- if (sql.includes("redundant_indexes") && sql.includes("columns like")) {
80
- return { rows: redundantIndexesRows };
81
- }
82
- // D004: pg_stat_statements extension check
83
- if (sql.includes("pg_extension") && sql.includes("pg_stat_statements")) {
84
- return { rows: [] }; // Extension not installed
85
- }
86
- // D004: pg_stat_kcache extension check
87
- if (sql.includes("pg_extension") && sql.includes("pg_stat_kcache")) {
88
- return { rows: [] }; // Extension not installed
89
- }
90
- // G001: Memory settings query
91
- if (sql.includes("pg_size_bytes") && sql.includes("shared_buffers") && sql.includes("work_mem")) {
92
- return { rows: [{
93
- shared_buffers_bytes: "134217728",
94
- wal_buffers_bytes: "4194304",
95
- work_mem_bytes: "4194304",
96
- maintenance_work_mem_bytes: "67108864",
97
- effective_cache_size_bytes: "4294967296",
98
- max_connections: 100,
99
- }] };
100
- }
101
- throw new Error(`Unexpected query: ${sql}`);
102
- },
103
- };
104
- }
105
-
106
- describe("H001 schema validation", () => {
107
- test("H001 report with empty data validates against schema", async () => {
108
- const mockClient = createMockClient({ invalidIndexesRows: [] });
109
- const report = await checkup.generateH001(mockClient as any, "node-01");
110
-
111
- const result = validateReport(report, "H001");
112
- if (!result.valid) {
113
- console.error("H001 validation errors:", result.errors);
114
- }
115
- expect(result.valid).toBe(true);
116
- });
117
-
118
- test("H001 report with data validates against schema", async () => {
119
- const mockClient = createMockClient({
120
- invalidIndexesRows: [
121
- {
122
- schema_name: "public",
123
- table_name: "users",
124
- index_name: "users_email_idx",
125
- relation_name: "users",
126
- index_size_bytes: "1048576",
127
- supports_fk: false
128
- },
129
- ],
130
- });
131
- const report = await checkup.generateH001(mockClient as any, "node-01");
132
-
133
- const result = validateReport(report, "H001");
134
- if (!result.valid) {
135
- console.error("H001 validation errors:", result.errors);
136
- }
137
- expect(result.valid).toBe(true);
138
- });
139
- });
140
-
141
- describe("H002 schema validation", () => {
142
- test("H002 report with empty data validates against schema", async () => {
143
- const mockClient = createMockClient({ unusedIndexesRows: [] });
144
- const report = await checkup.generateH002(mockClient as any, "node-01");
145
-
146
- const result = validateReport(report, "H002");
147
- if (!result.valid) {
148
- console.error("H002 validation errors:", result.errors);
149
- }
150
- expect(result.valid).toBe(true);
151
- });
152
-
153
- test("H002 report with data validates against schema", async () => {
154
- const mockClient = createMockClient({
155
- unusedIndexesRows: [
156
- {
157
- schema_name: "public",
158
- table_name: "logs",
159
- index_name: "logs_created_idx",
160
- index_definition: "CREATE INDEX logs_created_idx ON public.logs USING btree (created_at)",
161
- reason: "Never Used Indexes",
162
- idx_scan: "0",
163
- index_size_bytes: "8388608",
164
- idx_is_btree: true,
165
- supports_fk: false
166
- },
167
- ],
168
- });
169
- const report = await checkup.generateH002(mockClient as any, "node-01");
170
-
171
- const result = validateReport(report, "H002");
172
- if (!result.valid) {
173
- console.error("H002 validation errors:", result.errors);
174
- }
175
- expect(result.valid).toBe(true);
176
- });
177
- });
178
-
179
- describe("H004 schema validation", () => {
180
- test("H004 report with empty data validates against schema", async () => {
181
- const mockClient = createMockClient({ redundantIndexesRows: [] });
182
- const report = await checkup.generateH004(mockClient as any, "node-01");
183
-
184
- const result = validateReport(report, "H004");
185
- if (!result.valid) {
186
- console.error("H004 validation errors:", result.errors);
187
- }
188
- expect(result.valid).toBe(true);
189
- });
190
-
191
- test("H004 report with data validates against schema", async () => {
192
- const mockClient = createMockClient({
193
- redundantIndexesRows: [
194
- {
195
- schema_name: "public",
196
- table_name: "orders",
197
- index_name: "orders_user_id_idx",
198
- relation_name: "orders",
199
- access_method: "btree",
200
- reason: "public.orders_user_id_created_idx",
201
- index_size_bytes: "2097152",
202
- table_size_bytes: "16777216",
203
- index_usage: "0",
204
- supports_fk: false,
205
- index_definition: "CREATE INDEX orders_user_id_idx ON public.orders USING btree (user_id)",
206
- covering_indexes_json: JSON.stringify([
207
- { index_name: "public.orders_user_id_created_idx", index_definition: "CREATE INDEX orders_user_id_created_idx ON public.orders USING btree (user_id, created_at)" }
208
- ])
209
- },
210
- ],
211
- });
212
- const report = await checkup.generateH004(mockClient as any, "node-01");
213
-
214
- const result = validateReport(report, "H004");
215
- if (!result.valid) {
216
- console.error("H004 validation errors:", result.errors);
217
- }
218
- expect(result.valid).toBe(true);
219
- });
220
- });
221
-
222
- describe("D004 schema validation", () => {
223
- test("D004 report validates against schema (extensions not installed)", async () => {
224
- const mockClient = createMockClient();
225
- const report = await checkup.REPORT_GENERATORS.D004(mockClient as any, "node-01");
226
-
227
- const result = validateReport(report, "D004");
228
- if (!result.valid) {
229
- console.error("D004 validation errors:", result.errors);
230
- }
231
- expect(result.valid).toBe(true);
232
- });
233
- });
234
-
235
- describe("F001 schema validation", () => {
236
- test("F001 report validates against schema", async () => {
237
- const mockClient = createMockClient();
238
- const report = await checkup.REPORT_GENERATORS.F001(mockClient as any, "node-01");
239
-
240
- const result = validateReport(report, "F001");
241
- if (!result.valid) {
242
- console.error("F001 validation errors:", result.errors);
243
- }
244
- expect(result.valid).toBe(true);
245
- });
246
- });
247
-
248
- describe("G001 schema validation", () => {
249
- test("G001 report validates against schema", async () => {
250
- const mockClient = createMockClient();
251
- const report = await checkup.REPORT_GENERATORS.G001(mockClient as any, "node-01");
252
-
253
- const result = validateReport(report, "G001");
254
- if (!result.valid) {
255
- console.error("G001 validation errors:", result.errors);
256
- }
257
- expect(result.valid).toBe(true);
258
- });
259
- });
260
-