pgserve 2.1.2 → 2.2.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/CHANGELOG.md +86 -0
- package/README.md +105 -1
- package/bin/autopg-wrapper.cjs +16 -0
- package/bin/pgserve-wrapper.cjs +31 -6
- package/bin/postgres-server.js +80 -7
- package/console/README.md +131 -0
- package/console/api.js +173 -0
- package/console/app.jsx +483 -0
- package/console/colors_and_type.css +227 -0
- package/console/components.jsx +167 -0
- package/console/console.css +1666 -0
- package/console/data.jsx +350 -0
- package/console/index.html +31 -0
- package/console/screens/databases.jsx +5 -0
- package/console/screens/health.jsx +5 -0
- package/console/screens/ingress.jsx +5 -0
- package/console/screens/optimizer.jsx +5 -0
- package/console/screens/rlm-sim.jsx +5 -0
- package/console/screens/rlm-trace.jsx +5 -0
- package/console/screens/security.jsx +5 -0
- package/console/screens/settings.jsx +611 -0
- package/console/screens/sql.jsx +5 -0
- package/console/screens/sync.jsx +5 -0
- package/console/screens/tables.jsx +5 -0
- package/console/tweaks-panel.jsx +425 -0
- package/package.json +11 -1
- package/src/cli-config.cjs +310 -0
- package/src/cli-install.cjs +98 -11
- package/src/cli-restart.cjs +228 -0
- package/src/cli-ui.cjs +580 -0
- package/src/cluster.js +43 -38
- package/src/postgres.js +141 -19
- package/src/settings-loader.cjs +235 -0
- package/src/settings-migrate.cjs +212 -0
- package/src/settings-pg-args.cjs +146 -0
- package/src/settings-schema.cjs +422 -0
- package/src/settings-validator.cjs +416 -0
- package/src/settings-writer.cjs +288 -0
- package/.claude/context/windows-debug.md +0 -119
- package/.genie/AGENTS.md +0 -15
- package/.genie/agents/README.md +0 -110
- package/.genie/agents/analyze.md +0 -176
- package/.genie/agents/forge.md +0 -290
- package/.genie/agents/garbage-cleaner.md +0 -324
- package/.genie/agents/garbage-collector.md +0 -596
- package/.genie/agents/github-issue-gc.md +0 -618
- package/.genie/agents/review.md +0 -380
- package/.genie/agents/semantic-analyzer/find-duplicates.md +0 -90
- package/.genie/agents/semantic-analyzer/find-orphans.md +0 -99
- package/.genie/agents/semantic-analyzer.md +0 -101
- package/.genie/agents/update.md +0 -182
- package/.genie/agents/wish.md +0 -357
- package/.genie/brainstorms/pgserve-v2/DESIGN.md +0 -174
- package/.genie/code/AGENTS.md +0 -694
- package/.genie/code/agents/audit/risk.md +0 -173
- package/.genie/code/agents/audit/security.md +0 -189
- package/.genie/code/agents/audit.md +0 -145
- package/.genie/code/agents/challenge.md +0 -230
- package/.genie/code/agents/change-reviewer.md +0 -295
- package/.genie/code/agents/code-garbage-collector.md +0 -425
- package/.genie/code/agents/code-quality.md +0 -410
- package/.genie/code/agents/commit-suggester.md +0 -255
- package/.genie/code/agents/commit.md +0 -124
- package/.genie/code/agents/consensus.md +0 -204
- package/.genie/code/agents/daily-standup.md +0 -722
- package/.genie/code/agents/docgen.md +0 -48
- package/.genie/code/agents/explore.md +0 -79
- package/.genie/code/agents/fix.md +0 -100
- package/.genie/code/agents/git/commit-advisory.md +0 -219
- package/.genie/code/agents/git/workflows/issue.md +0 -244
- package/.genie/code/agents/git/workflows/pr.md +0 -179
- package/.genie/code/agents/git/workflows/release.md +0 -460
- package/.genie/code/agents/git/workflows/report.md +0 -342
- package/.genie/code/agents/git.md +0 -432
- package/.genie/code/agents/implementor.md +0 -161
- package/.genie/code/agents/install.md +0 -515
- package/.genie/code/agents/issue-creator.md +0 -344
- package/.genie/code/agents/polish.md +0 -116
- package/.genie/code/agents/qa.md +0 -653
- package/.genie/code/agents/refactor.md +0 -294
- package/.genie/code/agents/release.md +0 -1129
- package/.genie/code/agents/roadmap.md +0 -885
- package/.genie/code/agents/tests.md +0 -557
- package/.genie/code/agents/tracer.md +0 -50
- package/.genie/code/agents/update/upstream-update.md +0 -85
- package/.genie/code/agents/update/versions/generic-update.md +0 -305
- package/.genie/code/agents/vibe.md +0 -1317
- package/.genie/code/spells/agent-configuration.md +0 -58
- package/.genie/code/spells/automated-rc-publishing.md +0 -106
- package/.genie/code/spells/branch-tracker-guidance.md +0 -28
- package/.genie/code/spells/debug.md +0 -320
- package/.genie/code/spells/emoji-naming-convention.md +0 -303
- package/.genie/code/spells/evidence-storage.md +0 -26
- package/.genie/code/spells/file-naming-rules.md +0 -35
- package/.genie/code/spells/forge-code-blueprints.md +0 -195
- package/.genie/code/spells/genie-integration.md +0 -153
- package/.genie/code/spells/publishing-protocol.md +0 -61
- package/.genie/code/spells/team-consultation-protocol.md +0 -284
- package/.genie/code/spells/tool-requirements.md +0 -20
- package/.genie/code/spells/triad-maintenance-protocol.md +0 -154
- package/.genie/code/teams/tech-council/council.md +0 -328
- package/.genie/code/teams/tech-council/jt.md +0 -352
- package/.genie/code/teams/tech-council/nayr.md +0 -305
- package/.genie/code/teams/tech-council/oettam.md +0 -375
- package/.genie/neurons/README.md +0 -193
- package/.genie/neurons/forge.md +0 -106
- package/.genie/neurons/genie.md +0 -63
- package/.genie/neurons/review.md +0 -106
- package/.genie/neurons/wish.md +0 -104
- package/.genie/product/README.md +0 -20
- package/.genie/product/cli-automation.md +0 -359
- package/.genie/product/environment.md +0 -60
- package/.genie/product/mission.md +0 -60
- package/.genie/product/roadmap.md +0 -44
- package/.genie/product/tech-stack.md +0 -34
- package/.genie/product/templates/context-template.md +0 -218
- package/.genie/product/templates/qa-done-report-template.md +0 -68
- package/.genie/product/templates/review-report-template.md +0 -89
- package/.genie/product/templates/wish-template.md +0 -120
- package/.genie/scripts/helpers/analyze-commit.js +0 -195
- package/.genie/scripts/helpers/bullet-counter.js +0 -194
- package/.genie/scripts/helpers/bullet-find.js +0 -289
- package/.genie/scripts/helpers/bullet-id.js +0 -244
- package/.genie/scripts/helpers/check-secrets.js +0 -237
- package/.genie/scripts/helpers/count-tokens.js +0 -200
- package/.genie/scripts/helpers/create-frontmatter.js +0 -456
- package/.genie/scripts/helpers/detect-markers.js +0 -293
- package/.genie/scripts/helpers/detect-todos.js +0 -267
- package/.genie/scripts/helpers/detect-unlabeled-blocks.js +0 -135
- package/.genie/scripts/helpers/embeddings.js +0 -344
- package/.genie/scripts/helpers/find-empty-sections.js +0 -158
- package/.genie/scripts/helpers/index.js +0 -319
- package/.genie/scripts/helpers/validate-frontmatter.js +0 -578
- package/.genie/scripts/helpers/validate-links.js +0 -207
- package/.genie/scripts/helpers/validate-paths.js +0 -373
- package/.genie/spells/README.md +0 -9
- package/.genie/spells/ace-protocol.md +0 -118
- package/.genie/spells/ask-one-at-a-time.md +0 -175
- package/.genie/spells/backup-analyzer.md +0 -542
- package/.genie/spells/blocker.md +0 -12
- package/.genie/spells/break-things-move-fast.md +0 -56
- package/.genie/spells/context-candidates.md +0 -72
- package/.genie/spells/context-critic.md +0 -51
- package/.genie/spells/defer-to-expertise.md +0 -278
- package/.genie/spells/delegate-dont-do.md +0 -292
- package/.genie/spells/error-investigation-protocol.md +0 -328
- package/.genie/spells/evidence-based-completion.md +0 -273
- package/.genie/spells/experiment.md +0 -65
- package/.genie/spells/file-creation-protocol.md +0 -229
- package/.genie/spells/forge-integration.md +0 -281
- package/.genie/spells/forge-orchestration.md +0 -514
- package/.genie/spells/gather-context.md +0 -18
- package/.genie/spells/global-health-check.md +0 -34
- package/.genie/spells/global-noop-roundtrip.md +0 -25
- package/.genie/spells/install-genie.md +0 -1232
- package/.genie/spells/install.md +0 -82
- package/.genie/spells/investigate-before-commit.md +0 -112
- package/.genie/spells/know-yourself.md +0 -288
- package/.genie/spells/learn.md +0 -828
- package/.genie/spells/mcp-diagnostic-protocol.md +0 -246
- package/.genie/spells/mcp-first.md +0 -124
- package/.genie/spells/multi-step-execution.md +0 -67
- package/.genie/spells/orchestration-boundary-protocol.md +0 -256
- package/.genie/spells/orchestrator-not-implementor.md +0 -189
- package/.genie/spells/prompt.md +0 -746
- package/.genie/spells/reflect.md +0 -404
- package/.genie/spells/routing-decision-matrix.md +0 -368
- package/.genie/spells/run-in-parallel.md +0 -12
- package/.genie/spells/session-state-updater-example.md +0 -196
- package/.genie/spells/session-state-updater.md +0 -220
- package/.genie/spells/track-long-running-tasks.md +0 -133
- package/.genie/spells/troubleshoot-infrastructure.md +0 -176
- package/.genie/spells/upgrade-genie.md +0 -415
- package/.genie/spells/url-presentation-protocol.md +0 -301
- package/.genie/spells/wish-initiation.md +0 -158
- package/.genie/spells/wish-issue-linkage.md +0 -410
- package/.genie/spells/wish-lifecycle.md +0 -100
- package/.genie/state/provider-status.json +0 -3
- package/.genie/state/version.json +0 -16
- package/.genie/wishes/canonical-pgserve-pm2-supervision/WISH.md +0 -290
- package/.genie/wishes/pgserve-v2/BRIEF-from-genie-pgserve.md +0 -99
- package/.genie/wishes/pgserve-v2/WISH.md +0 -442
- package/.genie/wishes/release-system-genie-pattern/WISH.md +0 -268
- package/.genie/wishes/release-system-genie-pattern/validation.md +0 -205
- package/.gitguardian.yaml +0 -29
- package/.gitguardianignore +0 -16
- package/.github/workflows/ci.yml +0 -122
- package/.github/workflows/release.yml +0 -289
- package/.github/workflows/version.yml +0 -228
- package/.husky/pre-commit +0 -2
- package/AGENTS.md +0 -433
- package/CLAUDE.md +0 -1
- package/Makefile +0 -285
- package/assets/icon.ico +0 -0
- package/bun.lock +0 -435
- package/bunfig.toml +0 -28
- package/ecosystem.config.cjs +0 -23
- package/eslint.config.js +0 -63
- package/examples/multi-tenant-demo.js +0 -104
- package/install.sh +0 -123
- package/knip.json +0 -9
- package/scripts/test-bun-self-heal.sh +0 -163
- package/scripts/test-npx.sh +0 -60
- package/tests/audit.test.js +0 -189
- package/tests/backpressure.test.js +0 -167
- package/tests/benchmarks/runner.js +0 -1197
- package/tests/benchmarks/vector-generator.js +0 -368
- package/tests/cli-install.test.js +0 -322
- package/tests/control-db.test.js +0 -285
- package/tests/daemon-control.test.js +0 -171
- package/tests/daemon-fingerprint-integration.test.js +0 -111
- package/tests/daemon-pr24-regression.test.js +0 -198
- package/tests/fingerprint.test.js +0 -263
- package/tests/fixtures/240-orphan-seed.sql +0 -30
- package/tests/multi-tenant.test.js +0 -374
- package/tests/orphan-cleanup.test.js +0 -390
- package/tests/pg-version-regex.test.js +0 -129
- package/tests/quick-bench.js +0 -135
- package/tests/router-handshake-retry.test.js +0 -119
- package/tests/router-handshake-watchdog.test.js +0 -110
- package/tests/sdk.test.js +0 -71
- package/tests/stale-postmaster-pid.test.js +0 -85
- package/tests/stress-test.js +0 -439
- package/tests/sync-perf-test.js +0 -150
- package/tests/tcp-listen.test.js +0 -368
- package/tests/tenancy.test.js +0 -403
- package/tests/wrapper-supervision.test.js +0 -107
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Multi-Tenant Router Test
|
|
3
|
-
*
|
|
4
|
-
* Tests the new multi-tenant architecture:
|
|
5
|
-
* - Single port server
|
|
6
|
-
* - Multiple databases auto-provisioned
|
|
7
|
-
* - Database isolation
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { startMultiTenantServer } from '../src/index.js';
|
|
11
|
-
import pg from 'pg';
|
|
12
|
-
import { test, expect } from 'bun:test';
|
|
13
|
-
import fs from 'fs';
|
|
14
|
-
import path from 'path';
|
|
15
|
-
|
|
16
|
-
const { Client } = pg;
|
|
17
|
-
|
|
18
|
-
// Test data directory
|
|
19
|
-
const testDataDir = './test-data-multitenant';
|
|
20
|
-
|
|
21
|
-
// Cleanup helper
|
|
22
|
-
function cleanup() {
|
|
23
|
-
if (fs.existsSync(testDataDir)) {
|
|
24
|
-
fs.rmSync(testDataDir, { recursive: true, force: true });
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
test('Multi-tenant router - basic setup', async () => {
|
|
29
|
-
cleanup();
|
|
30
|
-
|
|
31
|
-
const router = await startMultiTenantServer({
|
|
32
|
-
port: 15432, // Use non-standard port for testing
|
|
33
|
-
baseDir: testDataDir,
|
|
34
|
-
logLevel: 'info'
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Verify router started
|
|
38
|
-
const stats = router.getStats();
|
|
39
|
-
expect(stats.port).toBe(15432);
|
|
40
|
-
expect(stats.postgres.databases.length).toBe(0); // No databases yet
|
|
41
|
-
|
|
42
|
-
await router.stop();
|
|
43
|
-
cleanup();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('Multi-tenant router - auto-provision database', async () => {
|
|
47
|
-
cleanup();
|
|
48
|
-
|
|
49
|
-
const router = await startMultiTenantServer({
|
|
50
|
-
port: 15432,
|
|
51
|
-
baseDir: testDataDir,
|
|
52
|
-
logLevel: 'info'
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Connect to database "testdb1" (should auto-create)
|
|
56
|
-
const client = new Client({
|
|
57
|
-
host: '127.0.0.1',
|
|
58
|
-
port: 15432,
|
|
59
|
-
database: 'testdb1',
|
|
60
|
-
user: 'postgres',
|
|
61
|
-
password: 'postgres'
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
await client.connect();
|
|
65
|
-
|
|
66
|
-
// Verify instance was created
|
|
67
|
-
const stats = router.getStats();
|
|
68
|
-
expect(stats.postgres.databases.length).toBe(1);
|
|
69
|
-
|
|
70
|
-
const databases = router.listDatabases();
|
|
71
|
-
expect(databases.length).toBe(1);
|
|
72
|
-
expect(databases[0]).toBe('testdb1');
|
|
73
|
-
|
|
74
|
-
// Create table
|
|
75
|
-
await client.query('CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT)');
|
|
76
|
-
await client.query("INSERT INTO users (name) VALUES ('Alice')");
|
|
77
|
-
|
|
78
|
-
// Query
|
|
79
|
-
const result = await client.query('SELECT * FROM users');
|
|
80
|
-
expect(result.rows.length).toBe(1);
|
|
81
|
-
expect(result.rows[0].name).toBe('Alice');
|
|
82
|
-
|
|
83
|
-
await client.end();
|
|
84
|
-
await router.stop();
|
|
85
|
-
cleanup();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('Multi-tenant router - multiple databases isolated', async () => {
|
|
89
|
-
cleanup();
|
|
90
|
-
|
|
91
|
-
const router = await startMultiTenantServer({
|
|
92
|
-
port: 15432,
|
|
93
|
-
baseDir: testDataDir,
|
|
94
|
-
logLevel: 'info'
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Connect to database 1
|
|
98
|
-
const client1 = new Client({
|
|
99
|
-
host: '127.0.0.1',
|
|
100
|
-
port: 15432,
|
|
101
|
-
database: 'db1',
|
|
102
|
-
user: 'postgres',
|
|
103
|
-
password: 'postgres'
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
await client1.connect();
|
|
107
|
-
await client1.query('CREATE TABLE users (id INT, name TEXT)');
|
|
108
|
-
await client1.query("INSERT INTO users VALUES (1, 'Alice')");
|
|
109
|
-
|
|
110
|
-
// Verify db1 exists
|
|
111
|
-
let stats = router.getStats();
|
|
112
|
-
expect(stats.postgres.databases.length).toBe(1);
|
|
113
|
-
|
|
114
|
-
await client1.end();
|
|
115
|
-
|
|
116
|
-
// Connect to database 2
|
|
117
|
-
const client2 = new Client({
|
|
118
|
-
host: '127.0.0.1',
|
|
119
|
-
port: 15432,
|
|
120
|
-
database: 'db2',
|
|
121
|
-
user: 'postgres',
|
|
122
|
-
password: 'postgres'
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
await client2.connect();
|
|
126
|
-
await client2.query('CREATE TABLE posts (id INT, title TEXT)');
|
|
127
|
-
await client2.query("INSERT INTO posts VALUES (1, 'Hello World')");
|
|
128
|
-
|
|
129
|
-
// Verify db2 exists
|
|
130
|
-
stats = router.getStats();
|
|
131
|
-
expect(stats.postgres.databases.length).toBe(2);
|
|
132
|
-
|
|
133
|
-
await client2.end();
|
|
134
|
-
|
|
135
|
-
// Reconnect to db1 - verify data is isolated
|
|
136
|
-
const client1Again = new Client({
|
|
137
|
-
host: '127.0.0.1',
|
|
138
|
-
port: 15432,
|
|
139
|
-
database: 'db1',
|
|
140
|
-
user: 'postgres',
|
|
141
|
-
password: 'postgres'
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
await client1Again.connect();
|
|
145
|
-
|
|
146
|
-
// Should have users table, NOT posts table
|
|
147
|
-
const usersResult = await client1Again.query('SELECT * FROM users');
|
|
148
|
-
expect(usersResult.rows.length).toBe(1);
|
|
149
|
-
expect(usersResult.rows[0].name).toBe('Alice');
|
|
150
|
-
|
|
151
|
-
// Posts table should NOT exist
|
|
152
|
-
try {
|
|
153
|
-
await client1Again.query('SELECT * FROM posts');
|
|
154
|
-
throw new Error('Should throw error - posts table does not exist in db1');
|
|
155
|
-
} catch (error) {
|
|
156
|
-
expect(error.message).toContain('does not exist');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
await client1Again.end();
|
|
160
|
-
await router.stop();
|
|
161
|
-
cleanup();
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test('Router - pre-handshake buffer is bounded (issue #18 root cause #2)', async () => {
|
|
165
|
-
// Regression test for issue #18: without a bound on state.buffer, a
|
|
166
|
-
// client that sends garbage and never completes the PG startup would
|
|
167
|
-
// grow router memory unbounded (traced to the production 74 GiB VmSize).
|
|
168
|
-
// After fix, the router must close the connection once the buffer
|
|
169
|
-
// exceeds MAX_STARTUP_BUFFER_SIZE (1 MiB).
|
|
170
|
-
cleanup();
|
|
171
|
-
|
|
172
|
-
const router = await startMultiTenantServer({
|
|
173
|
-
port: 15546,
|
|
174
|
-
baseDir: testDataDir,
|
|
175
|
-
logLevel: 'warn',
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const net = await import('net');
|
|
179
|
-
const sock = net.connect(15546, '127.0.0.1');
|
|
180
|
-
await new Promise((resolve) => sock.once('connect', resolve));
|
|
181
|
-
|
|
182
|
-
const garbage = Buffer.alloc(256 * 1024, 0x41); // 256 KiB of 'A'
|
|
183
|
-
let closed = false;
|
|
184
|
-
sock.on('close', () => { closed = true; });
|
|
185
|
-
|
|
186
|
-
// Send 5 × 256 KiB = 1.25 MiB, exceeding the 1 MiB cap.
|
|
187
|
-
for (let i = 0; i < 5 && !closed; i++) {
|
|
188
|
-
await new Promise((resolve) => sock.write(garbage, resolve));
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Wait up to 2s for the proxy to close the connection.
|
|
192
|
-
const deadline = Date.now() + 2000;
|
|
193
|
-
while (!closed && Date.now() < deadline) {
|
|
194
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
expect(closed).toBe(true);
|
|
198
|
-
sock.destroy();
|
|
199
|
-
|
|
200
|
-
await router.stop();
|
|
201
|
-
cleanup();
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
test('Router - socket state has startupInProgress flag (issue #18 root cause #1)', async () => {
|
|
205
|
-
// White-box regression test for the reentrancy guard. Without
|
|
206
|
-
// state.startupInProgress, two data events arriving during the first
|
|
207
|
-
// processStartupMessage() await would launch concurrent async tasks
|
|
208
|
-
// that race to assign state.pgSocket, leaking the loser. This test
|
|
209
|
-
// verifies the flag is wired into the state object.
|
|
210
|
-
cleanup();
|
|
211
|
-
|
|
212
|
-
const router = await startMultiTenantServer({
|
|
213
|
-
port: 15547,
|
|
214
|
-
baseDir: testDataDir,
|
|
215
|
-
logLevel: 'warn',
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
const net = await import('net');
|
|
219
|
-
const sock = net.connect(15547, '127.0.0.1');
|
|
220
|
-
await new Promise((resolve) => sock.once('connect', resolve));
|
|
221
|
-
|
|
222
|
-
// Router tracks client sockets in this.connections; introspect to pull
|
|
223
|
-
// the state object and confirm the flag exists and defaults to false.
|
|
224
|
-
// On some platforms (macOS in particular) the client-side 'connect'
|
|
225
|
-
// event fires slightly before the server-side handleSocketOpen runs,
|
|
226
|
-
// so poll until the router has registered the connection rather than
|
|
227
|
-
// asserting synchronously.
|
|
228
|
-
const deadline = Date.now() + 2000;
|
|
229
|
-
while (router.connections.size === 0 && Date.now() < deadline) {
|
|
230
|
-
await new Promise((r) => setTimeout(r, 20));
|
|
231
|
-
}
|
|
232
|
-
expect(router.connections.size).toBeGreaterThan(0);
|
|
233
|
-
const bunSocket = [...router.connections][0];
|
|
234
|
-
const state = router.socketState.get(bunSocket);
|
|
235
|
-
expect(state).toBeDefined();
|
|
236
|
-
expect(state.startupInProgress).toBe(false);
|
|
237
|
-
|
|
238
|
-
sock.destroy();
|
|
239
|
-
await router.stop();
|
|
240
|
-
cleanup();
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
test('PostgresManager - stop() nulls socketDir/databaseDir (issue #24)', async () => {
|
|
244
|
-
// Regression test for issue #24: router used to cache stale socketPath
|
|
245
|
-
// pointing to a directory that stop() had already rmSync'd. After fix,
|
|
246
|
-
// stop() nulls socketDir/databaseDir UNCONDITIONALLY so subsequent
|
|
247
|
-
// getSocketPath() returns null (forcing TCP fallback in the router).
|
|
248
|
-
cleanup();
|
|
249
|
-
|
|
250
|
-
const { PostgresManager } = await import('../src/postgres.js');
|
|
251
|
-
const { createLogger } = await import('../src/logger.js');
|
|
252
|
-
const pg = new PostgresManager({
|
|
253
|
-
port: 15543,
|
|
254
|
-
logger: createLogger({ level: 'warn' }),
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
await pg.start();
|
|
258
|
-
const socketPathBeforeStop = pg.getSocketPath();
|
|
259
|
-
expect(socketPathBeforeStop).not.toBeNull();
|
|
260
|
-
expect(fs.existsSync(pg.socketDir)).toBe(true);
|
|
261
|
-
|
|
262
|
-
await pg.stop();
|
|
263
|
-
|
|
264
|
-
// CORE ASSERTION: socketDir must be nulled after stop
|
|
265
|
-
expect(pg.socketDir).toBeNull();
|
|
266
|
-
expect(pg.getSocketPath()).toBeNull();
|
|
267
|
-
// databaseDir nulled only in memory mode (persistent mode keeps user-owned path)
|
|
268
|
-
expect(pg.databaseDir).toBeNull();
|
|
269
|
-
// And the dir on disk must actually be gone
|
|
270
|
-
// (socketPathBeforeStop points inside the deleted socketDir)
|
|
271
|
-
const staleSocketDir = path.dirname(socketPathBeforeStop);
|
|
272
|
-
expect(fs.existsSync(staleSocketDir)).toBe(false);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
test('PostgresManager - start()+stop()+start() yields fresh socketDir (issue #24)', async () => {
|
|
276
|
-
// Regression test for issue #24: pgManager.start() called after stop()
|
|
277
|
-
// must produce a FRESH socketDir (different path). Without the fix, a
|
|
278
|
-
// re-entry guard was missing and socketDir could leak across restarts.
|
|
279
|
-
cleanup();
|
|
280
|
-
|
|
281
|
-
const { PostgresManager } = await import('../src/postgres.js');
|
|
282
|
-
const { createLogger } = await import('../src/logger.js');
|
|
283
|
-
const pg = new PostgresManager({
|
|
284
|
-
port: 15544,
|
|
285
|
-
logger: createLogger({ level: 'warn' }),
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
await pg.start();
|
|
289
|
-
const socketDir1 = pg.socketDir;
|
|
290
|
-
expect(socketDir1).not.toBeNull();
|
|
291
|
-
|
|
292
|
-
await pg.stop();
|
|
293
|
-
expect(pg.socketDir).toBeNull();
|
|
294
|
-
|
|
295
|
-
await pg.start();
|
|
296
|
-
const socketDir2 = pg.socketDir;
|
|
297
|
-
expect(socketDir2).not.toBeNull();
|
|
298
|
-
expect(socketDir2).not.toBe(socketDir1);
|
|
299
|
-
expect(fs.existsSync(socketDir2)).toBe(true);
|
|
300
|
-
|
|
301
|
-
await pg.stop();
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
test('PostgresManager - double start() is a no-op (issue #24 re-entry guard)', async () => {
|
|
305
|
-
// Without the guard, a second start() would overwrite socketDir/databaseDir
|
|
306
|
-
// and leak the previous tmp dir (the "1,457 stale sock dirs" symptom).
|
|
307
|
-
cleanup();
|
|
308
|
-
|
|
309
|
-
const { PostgresManager } = await import('../src/postgres.js');
|
|
310
|
-
const { createLogger } = await import('../src/logger.js');
|
|
311
|
-
const pg = new PostgresManager({
|
|
312
|
-
port: 15545,
|
|
313
|
-
logger: createLogger({ level: 'warn' }),
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
await pg.start();
|
|
317
|
-
const socketDir1 = pg.socketDir;
|
|
318
|
-
|
|
319
|
-
// Second start() should silently return the same instance without
|
|
320
|
-
// reassigning socketDir/databaseDir.
|
|
321
|
-
const result = await pg.start();
|
|
322
|
-
expect(result).toBe(pg);
|
|
323
|
-
expect(pg.socketDir).toBe(socketDir1);
|
|
324
|
-
|
|
325
|
-
await pg.stop();
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
test('Multi-tenant router - instance reuse', async () => {
|
|
329
|
-
cleanup();
|
|
330
|
-
|
|
331
|
-
const router = await startMultiTenantServer({
|
|
332
|
-
port: 15432,
|
|
333
|
-
baseDir: testDataDir,
|
|
334
|
-
logLevel: 'info'
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
// First connection to "reuse-test"
|
|
338
|
-
const client1 = new Client({
|
|
339
|
-
host: '127.0.0.1',
|
|
340
|
-
port: 15432,
|
|
341
|
-
database: 'reuse-test',
|
|
342
|
-
user: 'postgres',
|
|
343
|
-
password: 'postgres'
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
await client1.connect();
|
|
347
|
-
await client1.query('CREATE TABLE test (value INT)');
|
|
348
|
-
await client1.query('INSERT INTO test VALUES (42)');
|
|
349
|
-
await client1.end();
|
|
350
|
-
|
|
351
|
-
// Second connection to same database (should reuse instance)
|
|
352
|
-
const client2 = new Client({
|
|
353
|
-
host: '127.0.0.1',
|
|
354
|
-
port: 15432,
|
|
355
|
-
database: 'reuse-test',
|
|
356
|
-
user: 'postgres',
|
|
357
|
-
password: 'postgres'
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
await client2.connect();
|
|
361
|
-
|
|
362
|
-
// Should still have the table from client1
|
|
363
|
-
const result = await client2.query('SELECT * FROM test');
|
|
364
|
-
expect(result.rows.length).toBe(1);
|
|
365
|
-
expect(result.rows[0].value).toBe(42);
|
|
366
|
-
|
|
367
|
-
// Still only 1 database
|
|
368
|
-
const stats = router.getStats();
|
|
369
|
-
expect(stats.postgres.databases.length).toBe(1);
|
|
370
|
-
|
|
371
|
-
await client2.end();
|
|
372
|
-
await router.stop();
|
|
373
|
-
cleanup();
|
|
374
|
-
});
|