postgresai 0.16.0-rc.4 → 0.16.0
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.
- package/README.md +154 -0
- package/dist/bin/postgres-ai.js +2911 -255
- package/package.json +12 -3
- package/schemas/A002.schema.json +63 -0
- package/schemas/A003.schema.json +73 -0
- package/schemas/A004.schema.json +81 -0
- package/schemas/A007.schema.json +71 -0
- package/schemas/A013.schema.json +61 -0
- package/schemas/D001.schema.json +71 -0
- package/schemas/D004.schema.json +136 -0
- package/schemas/F001.schema.json +73 -0
- package/schemas/F002.schema.json +108 -0
- package/schemas/F003.schema.json +138 -0
- package/schemas/F004.schema.json +125 -0
- package/schemas/F005.schema.json +131 -0
- package/schemas/F009.schema.json +155 -0
- package/schemas/G001.schema.json +135 -0
- package/schemas/G003.schema.json +90 -0
- package/schemas/H001.schema.json +141 -0
- package/schemas/H002.schema.json +129 -0
- package/schemas/H004.schema.json +128 -0
- package/schemas/I001.schema.json +149 -0
- package/schemas/K001.schema.json +161 -0
- package/schemas/K003.schema.json +163 -0
- package/schemas/K004.schema.json +110 -0
- package/schemas/K005.schema.json +110 -0
- package/schemas/K006.schema.json +110 -0
- package/schemas/K007.schema.json +110 -0
- package/schemas/K008.schema.json +110 -0
- package/schemas/M001.schema.json +119 -0
- package/schemas/M002.schema.json +110 -0
- package/schemas/M003.schema.json +128 -0
- package/schemas/N001.schema.json +161 -0
- package/schemas/query.schema.json +62 -0
- package/CHANGELOG.md +0 -11
- package/bin/postgres-ai.ts +0 -5578
- package/bun.lock +0 -258
- package/bunfig.toml +0 -20
- package/lib/aas-onboard.ts +0 -251
- package/lib/auth-server.ts +0 -285
- package/lib/checkup-api.ts +0 -526
- package/lib/checkup-dictionary.ts +0 -103
- package/lib/checkup-summary.ts +0 -338
- package/lib/checkup.ts +0 -2261
- package/lib/config.ts +0 -171
- package/lib/init.ts +0 -1152
- package/lib/instances.ts +0 -245
- package/lib/issues.ts +0 -1060
- package/lib/mcp-server.ts +0 -667
- package/lib/metrics-loader.ts +0 -134
- package/lib/pkce.ts +0 -79
- package/lib/reports.ts +0 -373
- package/lib/storage.ts +0 -367
- package/lib/supabase.ts +0 -826
- package/lib/util.ts +0 -134
- package/packages/postgres-ai/README.md +0 -26
- package/packages/postgres-ai/bin/postgres-ai.js +0 -27
- package/packages/postgres-ai/package.json +0 -27
- package/scripts/embed-checkup-dictionary.ts +0 -115
- package/scripts/embed-metrics.ts +0 -160
- package/scripts/generate-release-notes.ts +0 -668
- package/test/PERMISSION_CHECK_TEST_SUMMARY.md +0 -139
- package/test/aas-onboard.test.ts +0 -301
- package/test/auth.test.ts +0 -287
- package/test/checkup.integration.test.ts +0 -413
- package/test/checkup.test.ts +0 -3626
- package/test/compose-cmd.test.ts +0 -120
- package/test/config-consistency.test.ts +0 -352
- package/test/init.integration.test.ts +0 -438
- package/test/init.test.ts +0 -1816
- package/test/issues.cli.test.ts +0 -1162
- package/test/issues.test.ts +0 -456
- package/test/mcp-server.test.ts +0 -2530
- package/test/monitoring.test.ts +0 -746
- package/test/permission-check-sql.test.ts +0 -116
- package/test/reports.cli.test.ts +0 -793
- package/test/reports.test.ts +0 -977
- package/test/schema-validation.test.ts +0 -231
- package/test/storage.test.ts +0 -935
- package/test/supabase.test.ts +0 -709
- package/test/targets-add-config.test.ts +0 -28
- package/test/test-utils.ts +0 -190
- package/test/upgrade.test.ts +0 -1056
- package/test/util.test.ts +0 -44
- package/tsconfig.json +0 -20
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON Schema validation tests for express checkup reports.
|
|
3
|
-
* Validates that generated reports match 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
|
-
import { createMockClient } from "./test-utils";
|
|
12
|
-
|
|
13
|
-
const ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
14
|
-
const schemasDir = resolve(import.meta.dir, "../../reporter/schemas");
|
|
15
|
-
|
|
16
|
-
function validateAgainstSchema(report: any, checkId: string): void {
|
|
17
|
-
const schemaPath = resolve(schemasDir, `${checkId}.schema.json`);
|
|
18
|
-
const schema = JSON.parse(readFileSync(schemaPath, "utf8"));
|
|
19
|
-
const validate = ajv.compile(schema);
|
|
20
|
-
const valid = validate(report);
|
|
21
|
-
if (!valid) {
|
|
22
|
-
const errors = validate.errors?.map(e => `${e.instancePath}: ${e.message}`).join(", ");
|
|
23
|
-
throw new Error(`${checkId} schema validation failed: ${errors}`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Test data for index reports
|
|
28
|
-
const indexTestData = {
|
|
29
|
-
H001: {
|
|
30
|
-
emptyRows: { invalidIndexesRows: [] },
|
|
31
|
-
dataRows: {
|
|
32
|
-
invalidIndexesRows: [
|
|
33
|
-
{ schema_name: "public", table_name: "users", index_name: "users_email_idx", relation_name: "users", index_size_bytes: "1048576", index_definition: "CREATE INDEX users_email_idx ON public.users USING btree (email)", supports_fk: false },
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
H002: {
|
|
38
|
-
emptyRows: { unusedIndexesRows: [] },
|
|
39
|
-
dataRows: {
|
|
40
|
-
unusedIndexesRows: [
|
|
41
|
-
{ schema_name: "public", table_name: "logs", index_name: "logs_created_idx", index_definition: "CREATE INDEX logs_created_idx ON public.logs USING btree (created_at)", reason: "Never Used Indexes", idx_scan: "0", index_size_bytes: "8388608", idx_is_btree: true, supports_fk: false },
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
H004: {
|
|
46
|
-
emptyRows: { redundantIndexesRows: [] },
|
|
47
|
-
dataRows: {
|
|
48
|
-
redundantIndexesRows: [
|
|
49
|
-
{ schema_name: "public", table_name: "orders", index_name: "orders_user_id_idx", relation_name: "orders", access_method: "btree", reason: "public.orders_user_id_created_idx", index_size_bytes: "2097152", table_size_bytes: "16777216", index_usage: "0", supports_fk: false, index_definition: "CREATE INDEX orders_user_id_idx ON public.orders USING btree (user_id)", redundant_to_json: JSON.stringify([{ index_name: "public.orders_user_id_created_idx", index_definition: "CREATE INDEX ...", index_size_bytes: 1048576 }]) },
|
|
50
|
-
],
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
describe("Schema validation", () => {
|
|
56
|
-
// Index health checks (H001, H002, H004) - test empty and with data
|
|
57
|
-
for (const [checkId, testData] of Object.entries(indexTestData)) {
|
|
58
|
-
const generator = checkup.REPORT_GENERATORS[checkId];
|
|
59
|
-
|
|
60
|
-
test(`${checkId} validates with empty data`, async () => {
|
|
61
|
-
const mockClient = createMockClient(testData.emptyRows);
|
|
62
|
-
const report = await generator(mockClient as any, "node-01");
|
|
63
|
-
validateAgainstSchema(report, checkId);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test(`${checkId} validates with sample data`, async () => {
|
|
67
|
-
const mockClient = createMockClient(testData.dataRows);
|
|
68
|
-
const report = await generator(mockClient as any, "node-01");
|
|
69
|
-
validateAgainstSchema(report, checkId);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// F003 (Autovacuum: dead tuples) - test empty and with data
|
|
74
|
-
test("F003 validates with empty data", async () => {
|
|
75
|
-
const mockClient = createMockClient({ deadTuplesRows: [] });
|
|
76
|
-
const report = await checkup.REPORT_GENERATORS.F003(mockClient as any, "node-01");
|
|
77
|
-
validateAgainstSchema(report, "F003");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("F003 validates with sample data", async () => {
|
|
81
|
-
const mockClient = createMockClient({
|
|
82
|
-
deadTuplesRows: [
|
|
83
|
-
{
|
|
84
|
-
tag_schemaname: "public",
|
|
85
|
-
tag_relname: "events",
|
|
86
|
-
n_live_tup: "6361538",
|
|
87
|
-
n_dead_tup: "8270000",
|
|
88
|
-
dead_pct: 56.52,
|
|
89
|
-
last_autovacuum: "0",
|
|
90
|
-
last_vacuum: "1704067200",
|
|
91
|
-
autovacuum_count: "0",
|
|
92
|
-
vacuum_count: "1",
|
|
93
|
-
autovacuum_disabled: 1,
|
|
94
|
-
table_size_b: "2147483648",
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
});
|
|
98
|
-
const report = await checkup.REPORT_GENERATORS.F003(mockClient as any, "node-01");
|
|
99
|
-
validateAgainstSchema(report, "F003");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
for (const checkId of ["F004", "F005"]) {
|
|
103
|
-
test(`${checkId} distinguishes a healthy empty result from missing schema`, async () => {
|
|
104
|
-
const healthyClient = createMockClient();
|
|
105
|
-
const healthy = await checkup.REPORT_GENERATORS[checkId](healthyClient as any, "node-01");
|
|
106
|
-
const healthyDb = healthy.results["node-01"].data.testdb;
|
|
107
|
-
expect(healthyDb.status).toEqual({ ok: true, reason: null, error: null });
|
|
108
|
-
validateAgainstSchema(healthy, checkId);
|
|
109
|
-
|
|
110
|
-
const missingSchemaClient = createMockClient({
|
|
111
|
-
bloatCapabilityRows: [
|
|
112
|
-
{ schema_exists: false, schema_usage: false, view_exists: false, view_select: false },
|
|
113
|
-
],
|
|
114
|
-
});
|
|
115
|
-
const degraded = await checkup.REPORT_GENERATORS[checkId](missingSchemaClient as any, "node-01");
|
|
116
|
-
const degradedDb = degraded.results["node-01"].data.testdb;
|
|
117
|
-
expect(degradedDb.status).toEqual({
|
|
118
|
-
ok: false,
|
|
119
|
-
reason: "missing_schema",
|
|
120
|
-
error: 'schema "postgres_ai" does not exist',
|
|
121
|
-
});
|
|
122
|
-
validateAgainstSchema(degraded, checkId);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test(`${checkId} exposes missing-grant degradation`, async () => {
|
|
126
|
-
const client = createMockClient({
|
|
127
|
-
bloatCapabilityRows: [
|
|
128
|
-
{ schema_exists: true, schema_usage: true, view_exists: true, view_select: false },
|
|
129
|
-
],
|
|
130
|
-
});
|
|
131
|
-
const report = await checkup.REPORT_GENERATORS[checkId](client as any, "node-01");
|
|
132
|
-
const dbEntry = report.results["node-01"].data.testdb;
|
|
133
|
-
expect(dbEntry.status).toEqual({
|
|
134
|
-
ok: false,
|
|
135
|
-
reason: "missing_grant",
|
|
136
|
-
error: "permission denied for relation postgres_ai.pg_statistic",
|
|
137
|
-
});
|
|
138
|
-
validateAgainstSchema(report, checkId);
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Settings reports (D004, F001, G001) - single test each
|
|
143
|
-
for (const checkId of ["D004", "F001", "G001"]) {
|
|
144
|
-
test(`${checkId} validates against schema`, async () => {
|
|
145
|
-
const mockClient = createMockClient();
|
|
146
|
-
const report = await checkup.REPORT_GENERATORS[checkId](mockClient as any, "node-01");
|
|
147
|
-
validateAgainstSchema(report, checkId);
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
test("I001 validates with available pg_stat_io data", () => {
|
|
152
|
-
const report = {
|
|
153
|
-
version: null,
|
|
154
|
-
build_ts: null,
|
|
155
|
-
generation_mode: null,
|
|
156
|
-
checkId: "I001",
|
|
157
|
-
checkTitle: "I/O statistics (pg_stat_io)",
|
|
158
|
-
timestamptz: new Date("2026-01-01T00:00:00.000Z").toISOString(),
|
|
159
|
-
nodes: { primary: "node-01", standbys: [] },
|
|
160
|
-
results: {
|
|
161
|
-
"node-01": {
|
|
162
|
-
data: {
|
|
163
|
-
available: true,
|
|
164
|
-
by_backend_type: [{
|
|
165
|
-
backend_type: "total",
|
|
166
|
-
reads: 10,
|
|
167
|
-
read_bytes_mb: 64,
|
|
168
|
-
read_time_ms: 20,
|
|
169
|
-
writes: 5,
|
|
170
|
-
write_bytes_mb: 32,
|
|
171
|
-
write_time_ms: 10,
|
|
172
|
-
writebacks: 4,
|
|
173
|
-
writeback_bytes_mb: 16,
|
|
174
|
-
writeback_time_ms: 8,
|
|
175
|
-
fsyncs: 2,
|
|
176
|
-
fsync_time_ms: 6,
|
|
177
|
-
extends: 3,
|
|
178
|
-
extend_bytes_mb: 24,
|
|
179
|
-
hits: 90,
|
|
180
|
-
evictions: 7,
|
|
181
|
-
reuses: 11,
|
|
182
|
-
}],
|
|
183
|
-
analysis: {
|
|
184
|
-
total_read_mb: 64,
|
|
185
|
-
total_write_mb: 32,
|
|
186
|
-
total_io_time_ms: 30,
|
|
187
|
-
read_hit_ratio_pct: 90,
|
|
188
|
-
avg_read_time_ms: 2,
|
|
189
|
-
avg_write_time_ms: 2,
|
|
190
|
-
},
|
|
191
|
-
stats_reset_s: 7200,
|
|
192
|
-
},
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
validateAgainstSchema(report, "I001");
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test("I001 validates with unavailable pg_stat_io data", () => {
|
|
201
|
-
const report = {
|
|
202
|
-
version: null,
|
|
203
|
-
build_ts: null,
|
|
204
|
-
generation_mode: null,
|
|
205
|
-
checkId: "I001",
|
|
206
|
-
checkTitle: "I/O statistics (pg_stat_io)",
|
|
207
|
-
timestamptz: new Date("2026-01-01T00:00:00.000Z").toISOString(),
|
|
208
|
-
nodes: { primary: "node-01", standbys: [] },
|
|
209
|
-
results: {
|
|
210
|
-
"node-01": {
|
|
211
|
-
data: {
|
|
212
|
-
available: false,
|
|
213
|
-
min_version_required: "16",
|
|
214
|
-
by_backend_type: [],
|
|
215
|
-
analysis: {
|
|
216
|
-
total_read_mb: 0,
|
|
217
|
-
total_write_mb: 0,
|
|
218
|
-
total_io_time_ms: 0,
|
|
219
|
-
read_hit_ratio_pct: 0,
|
|
220
|
-
avg_read_time_ms: null,
|
|
221
|
-
avg_write_time_ms: null,
|
|
222
|
-
},
|
|
223
|
-
stats_reset_s: null,
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
validateAgainstSchema(report, "I001");
|
|
230
|
-
});
|
|
231
|
-
});
|