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)) }) })