fix: execute mit optionalen Params, release(error) bei reset-Fehler, Tests ergänzt
This commit is contained in:
@@ -62,6 +62,27 @@ describe('withTenant', () => {
|
||||
await withTenant('abc123', async (client) => {
|
||||
await client.execute('DELETE FROM sessions WHERE expired = true')
|
||||
})
|
||||
expect(mockClient.query).toHaveBeenCalledWith('DELETE FROM sessions WHERE expired = true')
|
||||
expect(mockClient.query).toHaveBeenCalledWith('DELETE FROM sessions WHERE expired = true', undefined)
|
||||
})
|
||||
|
||||
it('execute akzeptiert optionale Parameter', async () => {
|
||||
await withTenant('abc123', async (client) => {
|
||||
await client.execute('UPDATE t SET x = $1 WHERE id = $2', ['v', '1'])
|
||||
})
|
||||
expect(mockClient.query).toHaveBeenCalledWith('UPDATE t SET x = $1 WHERE id = $2', ['v', '1'])
|
||||
})
|
||||
|
||||
it('zerstört Verbindung bei search_path-Reset-Fehler (kein Recycling)', async () => {
|
||||
mockClient.query
|
||||
.mockResolvedValueOnce({ rows: [] }) // SET search_path = tenant_...
|
||||
.mockResolvedValueOnce({ rows: [] }) // fn-query
|
||||
.mockRejectedValueOnce(new Error('reset-fehler')) // SET search_path = public schlägt fehl
|
||||
|
||||
await withTenant('abc123', async (client) => {
|
||||
await client.query('SELECT 1', [])
|
||||
})
|
||||
|
||||
// release() muss mit Error aufgerufen worden sein (Verbindung zerstört, nicht recycelt)
|
||||
expect(mockClient.release).toHaveBeenCalledWith(expect.any(Error))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import { type QueryResultRow } from 'pg'
|
||||
import { pool } from './client'
|
||||
|
||||
interface TenantClient {
|
||||
execute: (sql: string) => Promise<void>
|
||||
execute: (sql: string, params?: unknown[]) => Promise<void>
|
||||
query: <T extends QueryResultRow = Record<string, unknown>>(sql: string, params: unknown[]) => Promise<T[]>
|
||||
}
|
||||
|
||||
@@ -18,21 +18,26 @@ export async function withTenant<T>(
|
||||
try {
|
||||
await client.query(`SET search_path = ${schema}, public`)
|
||||
const tenantClient: TenantClient = {
|
||||
execute: async (sql) => { await client.query(sql) },
|
||||
execute: async (sql, params?) => { await client.query(sql, params) },
|
||||
query: async <T extends QueryResultRow = Record<string, unknown>>(sql: string, params: unknown[]) =>
|
||||
client.query<T>(sql, params).then((r) => r.rows),
|
||||
}
|
||||
return await fn(tenantClient)
|
||||
} finally {
|
||||
let connectionBroken = false
|
||||
try {
|
||||
await client.query('SET search_path = public')
|
||||
} catch (resetErr) {
|
||||
connectionBroken = true
|
||||
console.error({ msg: 'search_path reset failed', error: (resetErr as Error).message })
|
||||
client.release(resetErr as Error)
|
||||
}
|
||||
if (!connectionBroken) {
|
||||
try {
|
||||
client.release()
|
||||
} catch (releaseErr) {
|
||||
console.error({ msg: 'Pool client release failed', error: (releaseErr as Error).message })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user