* fix(migrate): v0_13_0 shells out to `gbrain` shim, not `process.execPath` On bun-installed trees, process.execPath is the bun runtime itself. `bun extract links ...` got reinterpreted as `bun run extract` and crashed the upgrade mid-Phase B. The canonical shim on PATH already wraps the right runtime+entrypoint; trust it. Regression-guarded by test/migrations-v0_13_0.test.ts which greps the source for `process.execPath` and `bun` invocations. This was Bug 1 of tonight's v0.13 → v0.14 upgrade-night postmortem. * fix(autopilot): resolveGbrainCliPath prefers shim, never returns .ts argv[1] check used to short-circuit on /cli.ts, so bun-source installs got a .ts path back. spawn() then failed EACCES because TypeScript source isn't executable, and autopilot silently lost its worker. Reordered probes: which gbrain (shim) first, then compiled execPath, then argv[1] only if it ends in /gbrain. Deleted the .ts branch entirely — no valid case exists. Rewrote the existing test that enshrined the buggy .ts return. Critical regression guard: resolver MUST NEVER return a .ts path across any combination of argv[1] + execPath + shim availability. This was Bug 4 of tonight's v0.13 → v0.14 upgrade-night postmortem. * chore: bump version and changelog (v0.15.3) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(db): resolvePrepare() helper for PgBouncer transaction-mode pools Adds port-6543 auto-detect with a 4-level precedence chain: GBRAIN_PREPARE env var → ?prepare= URL param → port auto-detect → default. Wires into the module-singleton connect() so the main CLI path no longer hits "prepared statement does not exist" against Supabase transaction pooler. Returns boolean | undefined; undefined means omit the option and let postgres.js default (true) stand. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(postgres-engine): honor resolvePrepare in worker-instance pool Without this, \`gbrain jobs work\` against a Supabase pooler URL hits "prepared statement does not exist" under load even after the module singleton was fixed in db.ts. Community PR #270 (@notjbg) caught this second path that #284 had missed. Reuses the shared helper, no regex duplication. Co-Authored-By: Jonah Berg <jonah.berg.g@gmail.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(doctor): pgbouncer_prepare check URL-only check (no DB roundtrip) that reads the configured URL via loadConfig() and flags the footgun: port 6543 with prepared statements still enabled. Warns with the exact env override (GBRAIN_PREPARE=false) and URL-query alternative (?prepare=false). Works for both the module singleton and worker-instance engines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: resolvePrepare precedence matrix + postgres-engine wiring guard - test/resolve-prepare.test.ts: 11 cases covering env override, URL query param, port auto-detect, malformed URLs, postgres:// scheme, URL-encoded credentials. Uses bun:test — #284's original vitest file would never have run in this project. - test/postgres-engine.test.ts: new source-level grep case asserting the worker-pool connect() branch calls db.resolvePrepare(url) and includes a typeof prepare === 'boolean' check. Mirrors the existing SET LOCAL regression guard. If anyone rips out the wiring, the build fails before shipping starts dropping rows. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: bump version and changelog (v0.15.4) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Jonah Berg <jonah.berg.g@gmail.com>
76 lines
2.5 KiB
TypeScript
76 lines
2.5 KiB
TypeScript
/**
|
|
* resolvePrepare precedence tests.
|
|
*
|
|
* The helper in src/core/db.ts decides whether to force `prepare: true|false`
|
|
* on the postgres.js client, or leave it unset (postgres.js default). The
|
|
* decision matters: on Supabase PgBouncer (port 6543) prepared statements
|
|
* break under load, but forcing `prepare: false` on direct Postgres loses
|
|
* plan-cache performance. Precedence ordering (env → URL query → port
|
|
* auto-detect → default) is enforced here so future edits to resolvePrepare
|
|
* cannot silently reshuffle the precedence and reintroduce the bug.
|
|
*/
|
|
|
|
import { describe, test, expect, afterEach } from 'bun:test';
|
|
import { resolvePrepare } from '../src/core/db.ts';
|
|
|
|
describe('resolvePrepare', () => {
|
|
afterEach(() => {
|
|
delete process.env.GBRAIN_PREPARE;
|
|
});
|
|
|
|
test('returns false for Supabase pooler port 6543', () => {
|
|
expect(resolvePrepare('postgresql://user:pass@host:6543/db')).toBe(false);
|
|
});
|
|
|
|
test('returns undefined for direct Postgres port 5432', () => {
|
|
expect(resolvePrepare('postgresql://user:pass@host:5432/db')).toBeUndefined();
|
|
});
|
|
|
|
test('returns undefined for default port (no port specified)', () => {
|
|
expect(resolvePrepare('postgresql://user:pass@host/db')).toBeUndefined();
|
|
});
|
|
|
|
test('respects ?prepare=false in URL', () => {
|
|
expect(
|
|
resolvePrepare('postgresql://user:pass@host:5432/db?prepare=false'),
|
|
).toBe(false);
|
|
});
|
|
|
|
test('respects ?prepare=true in URL even on port 6543', () => {
|
|
expect(
|
|
resolvePrepare('postgresql://user:pass@host:6543/db?prepare=true'),
|
|
).toBe(true);
|
|
});
|
|
|
|
test('GBRAIN_PREPARE=false overrides everything', () => {
|
|
process.env.GBRAIN_PREPARE = 'false';
|
|
expect(
|
|
resolvePrepare('postgresql://user:pass@host:5432/db?prepare=true'),
|
|
).toBe(false);
|
|
});
|
|
|
|
test('GBRAIN_PREPARE=true overrides auto-detect on 6543', () => {
|
|
process.env.GBRAIN_PREPARE = 'true';
|
|
expect(resolvePrepare('postgresql://user:pass@host:6543/db')).toBe(true);
|
|
});
|
|
|
|
test('GBRAIN_PREPARE=0 is falsy', () => {
|
|
process.env.GBRAIN_PREPARE = '0';
|
|
expect(resolvePrepare('postgresql://user:pass@host:6543/db')).toBe(false);
|
|
});
|
|
|
|
test('returns undefined for malformed URL', () => {
|
|
expect(resolvePrepare('not-a-url')).toBeUndefined();
|
|
});
|
|
|
|
test('handles postgres:// scheme (no ql)', () => {
|
|
expect(resolvePrepare('postgres://user:pass@host:6543/db')).toBe(false);
|
|
});
|
|
|
|
test('handles URL with encoded special chars in password', () => {
|
|
expect(
|
|
resolvePrepare('postgresql://user:p%40ss%24word@host:6543/db'),
|
|
).toBe(false);
|
|
});
|
|
});
|