import { afterAll, describe, expect, it } from 'vitest' import { appendAuditBatch, closeDb, createDb, createUser, getUserApps, getUserById, listActiveSessions, listAudit, migrateSchema, runHousekeeping, searchDirectoryUsers, } from '../../src/index.js' const { db, sqlite } = createDb('sqlite::memory:') migrateSchema(sqlite) afterAll(() => closeDb(sqlite)) function timed(fn: () => T): { ms: number; value: T } { const t0 = performance.now() const value = fn() return { ms: performance.now() - t0, value } } // Бюджеты щедрые (CI-раннеры шумные): ловим деградацию на порядок, а не на проценты. describe('бюджеты производительности БД', () => { const users = Array.from({ length: 200 }, (_, i) => createUser(db, { email: `u${i}@x.io`, name: `User ${i}`, passwordHash: 'h', isAdmin: false, }), ) it('вставка 100k сессий и фильтр активных по пользователю < 150 мс', () => { const ins = sqlite.prepare( `INSERT INTO refresh_sessions (id,user_id,token_hash,expires_at,created_at) VALUES (?,?,?,?,?)`, ) const future = new Date(Date.now() + 86_400_000).toISOString() const past = '2000-01-01T00:00:00.000Z' sqlite.transaction(() => { for (let i = 0; i < 100_000; i++) { ins.run(`s${i}`, users[i % users.length]!.id, `h${i}`, i % 10 === 0 ? future : past, `t${i}`) } })() listActiveSessions(db, { userId: users[0]!.id }) // прогрев const { ms, value } = timed(() => listActiveSessions(db, { userId: users[0]!.id })) console.log(`listActiveSessions(user) на 100k: ${ms.toFixed(1)} мс, ${value.length} строк`) expect(value.length).toBeGreaterThan(0) expect(ms).toBeLessThan(150) }) it('housekeeping 100k истёкших сессий < 3 с', () => { const { ms, value } = timed(() => runHousekeeping(db, { retentionDays: 1 })) console.log(`runHousekeeping: ${ms.toFixed(0)} мс, удалено ${value.sessions}`) expect(value.sessions).toBeGreaterThan(80_000) expect(ms).toBeLessThan(3000) }) it('пакетный ingest 10k событий одной транзакцией < 1.5 с', () => { const events = Array.from({ length: 10_000 }, (_, i) => ({ eventId: `e${i}`, action: i % 2 ? 'auth.login' : 'user.update', summary: 'bench', })) const { ms, value } = timed(() => appendAuditBatch(db, events)) console.log(`appendAuditBatch(10k): ${ms.toFixed(0)} мс`) expect(value.accepted).toBe(10_000) expect(ms).toBeLessThan(1500) }) it('listAudit по kind использует индекс: < 50 мс на 10k', () => { listAudit(db, { kind: 'logins', limit: 100 }) const { ms, value } = timed(() => listAudit(db, { kind: 'logins', limit: 100 })) console.log(`listAudit(kind=logins): ${ms.toFixed(1)} мс`) expect(value.length).toBe(100) expect(ms).toBeLessThan(50) }) it('10k горячих запросов профиля (prepared) < 400 мс', () => { const id = users[0]!.id const { ms } = timed(() => { for (let i = 0; i < 10_000; i++) { getUserById(db, id) getUserApps(db, id) } }) console.log(`20k prepared-запросов: ${ms.toFixed(0)} мс`) expect(ms).toBeLessThan(400) }) it('поиск каталога по 200 пользователям < 20 мс', () => { searchDirectoryUsers(db, 'user 1') const { ms } = timed(() => searchDirectoryUsers(db, 'user 1')) expect(ms).toBeLessThan(20) }) })