also looking at this
test(pg): make the Postgres-gated tests actually run in CI
#1077
4 files
+68
-35
| @@ -30,19 +30,11 @@ import { join, resolve } from 'node:path' | ||
| 30 | 30 | import process from 'node:process' |
| 31 | 31 | import { SQL } from 'bun' |
| 32 | 32 | import { describe, expect, it } from 'bun:test' |
| 33 | import { PG_URL, probePostgres } from './pg' | |
| 34 | ||
| 35 | const pgAvailable = await probePostgres() | |
| 33 | 36 | |
| 34 | const PG_URL = `postgres://${process.env.USER}@localhost:5432/postgres` | |
| 35 | 37 | |
| 36 | let pgAvailable = false | |
| 37 | try { | |
| 38 | const probe = new SQL(PG_URL) | |
| 39 | await probe.unsafe('SELECT 1') | |
| 40 | await probe.end() | |
| 41 | pgAvailable = true | |
| 42 | } | |
| 43 | catch { | |
| 44 | pgAvailable = false | |
| 45 | } | |
| 46 | 38 | |
| 47 | 39 | describe.skipIf(!pgAvailable)('builder-local connection routing', () => { |
| 48 | 40 | it('keeps transactional writes inside the transaction, and reads inside it current', () => { |
| @@ -14,18 +14,10 @@ import { tmpdir } from 'node:os' | ||
| 14 | 14 | import { join, resolve } from 'node:path' |
| 15 | 15 | import { SQL } from 'bun' |
| 16 | 16 | import { describe, expect, it } from 'bun:test' |
| 17 | import { PG_URL, probePostgres } from './pg' | |
| 18 | ||
| 19 | const pgAvailable = await probePostgres() | |
| 17 | 20 | |
| 18 | const PG_URL = `postgres://${process.env.USER}@localhost:5432/postgres` | |
| 19 | let pgAvailable = false | |
| 20 | try { | |
| 21 | const probe = new SQL(PG_URL) | |
| 22 | await probe.unsafe('SELECT 1') | |
| 23 | await probe.end() | |
| 24 | pgAvailable = true | |
| 25 | } | |
| 26 | catch { | |
| 27 | pgAvailable = false | |
| 28 | } | |
| 29 | 21 | |
| 30 | 22 | describe.skipIf(!pgAvailable)('Postgres integration (#1038)', () => { |
| 31 | 23 | it('runs ORM CRUD + relations + raw builder against live Postgres', () => { |
| @@ -47,7 +39,9 @@ await raw.unsafe('CREATE TABLE _qb_users (id serial primary key, name text, age | ||
| 47 | 39 | await raw.unsafe('CREATE TABLE _qb_posts (id serial primary key, title text, pguser_id int)') |
| 48 | 40 | await raw.end() |
| 49 | 41 | |
| 50 | setConfig({ dialect: 'postgres', database: { database: 'postgres', username: process.env.USER, host: 'localhost', port: 5432, password: '' } }) | |
| 42 | // Configure from the same URL the gate probed, not a second hand-built copy of | |
| 43 | // it — the two drifting is what made this file skip in CI. | |
| 44 | setConfig({ dialect: 'postgres', database: { url: URL } }) | |
| 51 | 45 | resetConnection() |
| 52 | 46 | |
| 53 | 47 | // defineModel registers in the model registry, so relations resolve to the |
| @@ -24,20 +24,12 @@ import { join, resolve } from 'node:path' | ||
| 24 | 24 | import process from 'node:process' |
| 25 | 25 | import { SQL } from 'bun' |
| 26 | 26 | import { describe, expect, it } from 'bun:test' |
| 27 | import { PG_URL, probePostgres } from './pg' | |
| 28 | ||
| 29 | const pgAvailable = await probePostgres() | |
| 27 | 30 | |
| 28 | 31 | const SRC = resolve(import.meta.dir, '../src/index.ts') |
| 29 | const PG_URL = `postgres://${process.env.USER}@localhost:5432/postgres` | |
| 30 | ||
| 31 | let pgAvailable = false | |
| 32 | try { | |
| 33 | const probe = new SQL(PG_URL) | |
| 34 | await probe.unsafe('SELECT 1') | |
| 35 | await probe.end() | |
| 36 | pgAvailable = true | |
| 37 | } | |
| 38 | catch { | |
| 39 | pgAvailable = false | |
| 40 | } | |
| 32 | ||
| 41 | 33 | |
| 42 | 34 | /** A throwaway workspace with a migrations directory. */ |
| 43 | 35 | function workspace(files: Record<string, string>): string { |
| @@ -0,0 +1,55 @@ | ||
| 1 | /** | |
| 2 | * The Postgres a gated test should connect to, and whether it is reachable. | |
| 3 | * | |
| 4 | * Several tests can only be written against a real pooled driver — the | |
| 5 | * transaction data leak (#1065), the migration lock (#1067), the ORM | |
| 6 | * integration pass (#1038) — so they gate on a live server and skip without | |
| 7 | * one. That gate was quietly wrong in CI. | |
| 8 | * | |
| 9 | * Each of those files built its own URL as | |
| 10 | * `postgres://${process.env.USER}@localhost:5432/postgres`, which describes a | |
| 11 | * developer machine: a peer-authenticated server owned by the current user, | |
| 12 | * no password. CI is not that. It starts Postgres 17 as a service with | |
| 13 | * `POSTGRES_USER=postgres`, `POSTGRES_PASSWORD=postgres`, `POSTGRES_DB=test_db` | |
| 14 | * and exports `DATABASE_URL`. On a runner, `process.env.USER` is `runner`, so | |
| 15 | * every probe failed with | |
| 16 | * | |
| 17 | * FATAL: password authentication failed for user "runner" | |
| 18 | * | |
| 19 | * the gate read that as "no Postgres here", and the tests SKIPPED. CI stayed | |
| 20 | * green while the guards for the two most severe bugs in the release never | |
| 21 | * ran at all — the worst shape for a test to be in, because the green tick | |
| 22 | * says the opposite. | |
| 23 | * | |
| 24 | * So: prefer what the environment tells us, fall back to the local shape. | |
| 25 | */ | |
| 26 | ||
| 27 | import { SQL } from 'bun' | |
| 28 | import process from 'node:process' | |
| 29 | ||
| 30 | export const PG_URL: string | |
| 31 | = process.env.DATABASE_URL | |
| 32 | ?? process.env.POSTGRES_URL | |
| 33 | ?? `postgres://${process.env.USER ?? 'postgres'}@localhost:5432/postgres` | |
| 34 | ||
| 35 | /** | |
| 36 | * Whether that server actually answers. | |
| 37 | * | |
| 38 | * A function rather than an awaited constant so this module needs no top-level | |
| 39 | * await: the rule against it exists to keep one out of the shipped bundle, and | |
| 40 | * suppressing it in a helper to save three callers one line each is a poor | |
| 41 | * trade. Callers await it at the top of their own `.test.ts`, where the rule | |
| 42 | * does not apply. The URL above is the part that was wrong and the part worth | |
| 43 | * sharing; this is three lines of probe. | |
| 44 | */ | |
| 45 | export async function probePostgres(): Promise<boolean> { | |
| 46 | try { | |
| 47 | const probe = new SQL(PG_URL) | |
| 48 | await probe.unsafe('SELECT 1') | |
| 49 | await probe.end() | |
| 50 | return true | |
| 51 | } | |
| 52 | catch { | |
| 53 | return false | |
| 54 | } | |
| 55 | } | |