fix: sql guard, fk/index queries, pool hardening
This commit is contained in:
@@ -3,12 +3,14 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
const pgState = vi.hoisted(() => {
|
||||
const state = {
|
||||
pools: [] as FakePool[],
|
||||
nextResult: undefined as unknown
|
||||
nextResult: undefined as unknown,
|
||||
resultQueue: [] as unknown[]
|
||||
}
|
||||
|
||||
class FakePool {
|
||||
options: Record<string, unknown>
|
||||
queries: unknown[] = []
|
||||
errorHandlers: unknown[] = []
|
||||
ended = false
|
||||
|
||||
constructor(options: Record<string, unknown>) {
|
||||
@@ -16,8 +18,18 @@ const pgState = vi.hoisted(() => {
|
||||
state.pools.push(this)
|
||||
}
|
||||
|
||||
on(event: string, fn: unknown) {
|
||||
if (event === 'error') {
|
||||
this.errorHandlers.push(fn)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
async query(args: unknown) {
|
||||
this.queries.push(args)
|
||||
if (state.resultQueue.length > 0) {
|
||||
return state.resultQueue.shift()
|
||||
}
|
||||
return state.nextResult ?? { rows: [], fields: [], rowCount: 0, command: 'SELECT' }
|
||||
}
|
||||
|
||||
@@ -57,6 +69,7 @@ describe('createPostgresDriver', () => {
|
||||
beforeEach(() => {
|
||||
pgState.state.pools.length = 0
|
||||
pgState.state.nextResult = undefined
|
||||
pgState.state.resultQueue.length = 0
|
||||
})
|
||||
|
||||
it('creates one pool per database and reuses it', async () => {
|
||||
@@ -127,15 +140,78 @@ describe('createPostgresDriver', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('rejects multi-statement results', async () => {
|
||||
pgState.state.nextResult = [
|
||||
{ rows: [], fields: [], rowCount: 0 },
|
||||
{ rows: [], fields: [], rowCount: 0 }
|
||||
]
|
||||
it('rejects multi-statement sql before touching the pool', async () => {
|
||||
const driver = createPostgresDriver(target())
|
||||
await expect(driver.query({ sql: 'select 1; select 2', rowLimit: 10 })).rejects.toThrow(
|
||||
/one SQL statement/
|
||||
)
|
||||
// The guard runs before any pool is created.
|
||||
expect(pgState.state.pools).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('rejects session-level statements', async () => {
|
||||
const driver = createPostgresDriver(target())
|
||||
await expect(driver.query({ sql: 'BEGIN', rowLimit: 1 })).rejects.toThrow(/session-level/)
|
||||
expect(pgState.state.pools).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('registers a pool error listener', async () => {
|
||||
const driver = createPostgresDriver(target())
|
||||
await driver.query({ sql: 'select 1', rowLimit: 1 })
|
||||
expect(pgState.state.pools[0].errorHandlers).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('describeTable groups composite fks and multi-column indexes', async () => {
|
||||
pgState.state.resultQueue = [
|
||||
// columns
|
||||
{
|
||||
rows: [
|
||||
['id', 'integer', 'NO', null],
|
||||
['name', 'text', 'YES', null]
|
||||
],
|
||||
fields: [{ name: 'c' }],
|
||||
rowCount: 2
|
||||
},
|
||||
// primary key
|
||||
{ rows: [['id']], fields: [{ name: 'c' }], rowCount: 1 },
|
||||
// indexes: one index 'idx_ab' with two key columns
|
||||
{
|
||||
rows: [
|
||||
['idx_ab', false, 'a'],
|
||||
['idx_ab', false, 'b']
|
||||
],
|
||||
fields: [{ name: 'c' }],
|
||||
rowCount: 2
|
||||
},
|
||||
// foreign keys: one composite fk 'fk1' with two column pairs
|
||||
{
|
||||
rows: [
|
||||
['fk1', 'a', 'other', 'x'],
|
||||
['fk1', 'b', 'other', 'y']
|
||||
],
|
||||
fields: [{ name: 'c' }],
|
||||
rowCount: 2
|
||||
}
|
||||
]
|
||||
const driver = createPostgresDriver(target())
|
||||
const description = await driver.describeTable({ table: 't' })
|
||||
expect(description.indexes).toEqual([
|
||||
{ name: 'idx_ab', unique: false, columns: ['a', 'b'] }
|
||||
])
|
||||
expect(description.foreignKeys).toEqual([
|
||||
{
|
||||
name: 'fk1',
|
||||
columns: ['a', 'b'],
|
||||
referencedTable: 'other',
|
||||
referencedColumns: ['x', 'y']
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('describeTable throws when the table has no columns', async () => {
|
||||
pgState.state.resultQueue = [{ rows: [], fields: [], rowCount: 0 }]
|
||||
const driver = createPostgresDriver(target())
|
||||
await expect(driver.describeTable({ table: 'missing' })).rejects.toThrow(/not found/)
|
||||
})
|
||||
|
||||
it('lists databases against the maintenance db and maps sizes', async () => {
|
||||
|
||||
Reference in New Issue
Block a user