import { beforeEach, describe, expect, it, vi } from 'vitest' const pgState = vi.hoisted(() => { const state = { pools: [] as FakePool[], nextResult: undefined as unknown } class FakePool { options: Record queries: unknown[] = [] ended = false constructor(options: Record) { this.options = options state.pools.push(this) } async query(args: unknown) { this.queries.push(args) return state.nextResult ?? { rows: [], fields: [], rowCount: 0, command: 'SELECT' } } async end() { this.ended = true } } return { state, FakePool } }) vi.mock('pg', () => ({ default: { Pool: pgState.FakePool } })) import type { ConnectionConfig } from '../../../src/config/types.js' import { createPostgresDriver } from '../../../src/db/postgres.js' const config = (extra: Partial = {}): ConnectionConfig => ({ name: 'pg', type: 'postgres', host: 'real-host', user: 'postgres', password: 'pw', database: 'main', readonly: false, ...extra }) const target = (extra: Partial = {}) => ({ config: config(extra), host: '127.0.0.1', port: 15432 }) describe('createPostgresDriver', () => { beforeEach(() => { pgState.state.pools.length = 0 pgState.state.nextResult = undefined }) it('creates one pool per database and reuses it', async () => { const driver = createPostgresDriver(target()) await driver.query({ sql: 'select 1', rowLimit: 10 }) await driver.query({ sql: 'select 2', rowLimit: 10 }) await driver.query({ sql: 'select 3', database: 'other', rowLimit: 10 }) expect(pgState.state.pools).toHaveLength(2) expect(pgState.state.pools[0].options).toMatchObject({ host: '127.0.0.1', port: 15432, database: 'main', max: 4 }) expect(pgState.state.pools[1].options).toMatchObject({ database: 'other' }) }) it('passes readonly as a startup option', async () => { const driver = createPostgresDriver(target({ readonly: true })) await driver.query({ sql: 'select 1', rowLimit: 10 }) expect(pgState.state.pools[0].options.options).toBe('-c default_transaction_read_only=on') }) it('omits startup options when not readonly', async () => { const driver = createPostgresDriver(target()) await driver.query({ sql: 'select 1', rowLimit: 10 }) expect(pgState.state.pools[0].options.options).toBeUndefined() }) it('maps array-mode results with column names and normalized cells', async () => { pgState.state.nextResult = { rows: [[1n, new Date('2026-01-01T00:00:00Z')]], fields: [{ name: 'id' }, { name: 'created' }], rowCount: 1, command: 'SELECT' } const driver = createPostgresDriver(target()) const result = await driver.query({ sql: 'select * from t', rowLimit: 10 }) expect(result).toEqual({ columns: ['id', 'created'], rows: [['1', '2026-01-01T00:00:00.000Z']], rowCount: 1, truncated: false }) }) it('truncates rows beyond the limit', async () => { pgState.state.nextResult = { rows: [[1], [2], [3]], fields: [{ name: 'n' }], rowCount: 3, command: 'SELECT' } const driver = createPostgresDriver(target()) const result = await driver.query({ sql: 'select n', rowLimit: 2 }) expect(result.rows).toEqual([[1], [2]]) expect(result.truncated).toBe(true) expect(result.rowCount).toBe(3) }) it('sends positional params through', async () => { const driver = createPostgresDriver(target()) await driver.query({ sql: 'select $1', params: [42], rowLimit: 10 }) expect(pgState.state.pools[0].queries[0]).toMatchObject({ text: 'select $1', values: [42], rowMode: 'array' }) }) it('rejects multi-statement results', async () => { pgState.state.nextResult = [ { rows: [], fields: [], rowCount: 0 }, { rows: [], fields: [], rowCount: 0 } ] const driver = createPostgresDriver(target()) await expect(driver.query({ sql: 'select 1; select 2', rowLimit: 10 })).rejects.toThrow( /one SQL statement/ ) }) it('lists databases against the maintenance db and maps sizes', async () => { pgState.state.nextResult = { rows: [ ['app', 1024], ['postgres', 2048] ], fields: [{ name: 'name' }, { name: 'size_bytes' }], rowCount: 2, command: 'SELECT' } const driver = createPostgresDriver(target({ database: undefined })) const databases = await driver.listDatabases() expect(databases).toEqual([ { name: 'app', sizeBytes: 1024 }, { name: 'postgres', sizeBytes: 2048 } ]) expect(pgState.state.pools[0].options.database).toBe('postgres') }) it('maps reltuples=-1 to null in listTables', async () => { pgState.state.nextResult = { rows: [ ['public', 'fresh', '-1'], ['public', 'analyzed', '42'] ], fields: [{ name: 'schema' }, { name: 'name' }, { name: 'row_estimate' }], rowCount: 2, command: 'SELECT' } const driver = createPostgresDriver(target()) const tables = await driver.listTables({}) expect(tables).toEqual([ { schema: 'public', name: 'fresh', rowEstimate: null }, { schema: 'public', name: 'analyzed', rowEstimate: 42 } ]) }) it('dispose ends every pool', async () => { const driver = createPostgresDriver(target()) await driver.query({ sql: 'select 1', rowLimit: 1 }) await driver.query({ sql: 'select 1', database: 'other', rowLimit: 1 }) await driver.dispose() expect(pgState.state.pools.every((pool) => pool.ended)).toBe(true) }) })