89 lines
3.0 KiB
TypeScript
89 lines
3.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
|
|
// Pool aus client.ts mocken — gibt einen fake PoolClient zurück
|
|
const mockClient = vi.hoisted(() => ({
|
|
query: vi.fn(),
|
|
release: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('./client', () => ({
|
|
pool: {
|
|
connect: vi.fn().mockResolvedValue(mockClient),
|
|
},
|
|
}))
|
|
|
|
import { withTenant } from './tenant'
|
|
|
|
describe('withTenant', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
mockClient.query.mockResolvedValue({ rows: [] })
|
|
})
|
|
|
|
it('setzt search_path auf tenant-Schema', async () => {
|
|
await withTenant('abc123', async (client) => {
|
|
await client.query('SELECT 1', [])
|
|
})
|
|
expect(mockClient.query).toHaveBeenCalledWith('SET search_path = tenant_abc123, public')
|
|
})
|
|
|
|
it('setzt search_path zurück auf public nach Ausführung', async () => {
|
|
await withTenant('abc123', async (client) => {
|
|
await client.query('SELECT 1', [])
|
|
})
|
|
expect(mockClient.query).toHaveBeenLastCalledWith('SET search_path = public')
|
|
})
|
|
|
|
it('setzt search_path zurück auch bei Fehler', async () => {
|
|
await expect(
|
|
withTenant('abc123', async () => { throw new Error('DB-Fehler') })
|
|
).rejects.toThrow('DB-Fehler')
|
|
expect(mockClient.query).toHaveBeenLastCalledWith('SET search_path = public')
|
|
expect(mockClient.release).toHaveBeenCalled()
|
|
})
|
|
|
|
it('gibt Rückgabewert der Funktion zurück', async () => {
|
|
const result = await withTenant('abc123', async () => 'testdata')
|
|
expect(result).toBe('testdata')
|
|
})
|
|
|
|
it('wirft bei ungültiger tenantId', async () => {
|
|
await expect(
|
|
withTenant('invalid-tenant!', async () => 'x')
|
|
).rejects.toThrow('Ungültige tenantId')
|
|
})
|
|
|
|
it('released PoolClient nach Ausführung', async () => {
|
|
await withTenant('tenant1', async () => {})
|
|
expect(mockClient.release).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it('execute delegiert an den dedizierten PoolClient', async () => {
|
|
await withTenant('abc123', async (client) => {
|
|
await client.execute('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))
|
|
})
|
|
})
|