test(api): общий хелпер с фиксированным RSA-ключом

Фиксированный ключ (OIDC_RSA_PRIVATE_KEY) вместо generateKeyPair
на каждое построение приложения: фаза тестов 9.9с → 1.6с (×6).
Локальные копии buildTestApp/adminToken в 4 файлах заменены на helpers.ts
This commit is contained in:
Denozordec
2026-09-23 14:51:38 +07:00
parent c48132e89e
commit e135150c2d
5 changed files with 73 additions and 84 deletions
+3 -19
View File
@@ -1,17 +1,9 @@
import { describe, expect, it } from 'vitest'
import { buildApp } from '../src/app.js'
import { loadConfig } from '../src/config.js'
import { buildTestApp } from './helpers.js'
describe('app-switcher API', () => {
it('GET /api/v1/app-switcher is public and returns defaults', async () => {
const config = loadConfig({
...process.env,
JWT_SECRET: 'test-secret-at-least-8',
ADMIN_PASSWORD: 'admin',
DATABASE_URL: 'sqlite::memory:',
NODE_ENV: 'test',
})
const app = await buildApp({ config, databaseUrl: 'sqlite::memory:' })
const app = await buildTestApp()
const res = await app.inject({ method: 'GET', url: '/api/v1/app-switcher' })
expect(res.statusCode).toBe(200)
const body = res.json() as { menuLabel: string; apps: { id: string }[] }
@@ -38,15 +30,7 @@ describe('app-switcher API', () => {
})
it('PUT /api/v1/admin/app-switcher requires admin and persists', async () => {
const config = loadConfig({
...process.env,
JWT_SECRET: 'test-secret-at-least-8',
ADMIN_EMAIL: '[email protected]',
ADMIN_PASSWORD: 'adminpass',
DATABASE_URL: 'sqlite::memory:',
NODE_ENV: 'test',
})
const app = await buildApp({ config, databaseUrl: 'sqlite::memory:' })
const app = await buildTestApp()
const denied = await app.inject({
method: 'PUT',
+1 -25
View File
@@ -1,30 +1,6 @@
import { describe, expect, it } from 'vitest'
import { appendAudit, listAudit, purgeAuditOlderThan } from '@authportal/db'
import { buildApp } from '../src/app.js'
import { loadConfig } from '../src/config.js'
async function buildTestApp() {
const config = loadConfig({
...process.env,
JWT_SECRET: 'test-secret-at-least-8',
ADMIN_EMAIL: '[email protected]',
ADMIN_PASSWORD: 'adminpass',
DATABASE_URL: 'sqlite::memory:',
NODE_ENV: 'test',
AUDIT_INGEST_SECRET: 'dev-audit-ingest-secret',
})
return buildApp({ config, databaseUrl: 'sqlite::memory:' })
}
async function adminToken(app: Awaited<ReturnType<typeof buildTestApp>>) {
const login = await app.inject({
method: 'POST',
url: '/api/v1/auth/login',
payload: { email: '[email protected]', password: 'adminpass' },
})
expect(login.statusCode).toBe(200)
return (login.json() as { access_token: string }).access_token
}
import { adminToken, buildTestApp } from './helpers.js'
describe('audit log API', () => {
it('ingests external events with secret and dedupes by event_id', async () => {
+67
View File
@@ -0,0 +1,67 @@
import { expect } from 'vitest'
import { buildApp } from '../src/app.js'
import { loadConfig } from '../src/config.js'
import { resetOidcKeyCache } from '../src/lib/oidc/keys.js'
/** Фиксированный тестовый RSA-ключ: importPKCS8 (~мс) вместо generateKeyPair
* (~100–300 мс) при построении приложения в каждом тесте. */
const TEST_OIDC_RSA_PRIVATE_KEY = [
'-----BEGIN PRIVATE KEY-----',
'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCt/+Vwi6txs4ET',
'/OczfcOX0mVCOfxT3qGejj1xX19A4gKeGGjQ5LSBDGzcztncYMd0J/H1ozUWn0NU',
'ctolNRSTFchAYj1MoC2Pacfhe445nLR4bOssdADO9bwhHmWy8WQFyYc/lqs/xPSd',
'haZFwDRBRRfh9uxzzUPR8FXCovHEJG5HiLuvmALL1W7nkDg+L+vyLYCNje482hS+',
'ZcW3IH17gQpXgpqgXPaGHfZT1teIiYwhelaqPsApMQroOisKYGoQi1z/G/mdvaOF',
'/jravyBqhr5Hm03lFOMBvMptGrXsjopy+BVlqBVcvdwdyFfz94dayhnCmfEIvK7W',
'ynYGtoLnAgMBAAECggEAKqld04DjOb8FzGVLOKRCwyQ6YIkAFX42CT6d0QDNxYZE',
'lwesF1+j5CosPN1YrEBsVs3+dLgd6ZyF8Rwx4UKI92E1VpffqLxzP0Dz2ZSmM/j4',
'hLhL1vvgQN4eUM53p+6olASr7QrflQSQ51hgAqUUCtbKesREiEYRNVWkaVLzTSAr',
'KrwWAF81hzEms0KuHw6/EgThBkWYGAMzZjECFa173yBHklt1ikdB1AOmieL9PZbB',
'anjIequkUE2yoTJM/wOd2GN7sy61PmnMApwl1ke6k4VhrJeQhfahck0D2ynDvWqX',
'fnn1Wr/BRmSi38RNEOySWkrCVjM0XSjO3wEuHFFyuQKBgQDwgOgzgL1W5b13LKXo',
'8F/AJNRmi4vpM1lftMRalx5NU5ugw1iQ2zJzLG3Tl1/nvyt8tGFkXB7Dw9M9H0as',
'jjk7D8ZzL1a7COWQx7wya1l8vMxwv40ERvoWypwpuVo12SvRnHWgGwsbqW4wxYIG',
'9HsdvbxglFiVcSq+b6FDdg2nyQKBgQC5NgKatFPPed/DFb6MCkW+5Wnw2HzdKHxD',
'fEkAJeb3xX+9k7OifRHppurwaDV4jUPJ5W1Whly77cg+xmEYAbUdPS1kgfYJaDn1',
'mPN9dI7RP15v4X46/+5v5EZYAKcdUdMzr9gJ2iQVpoDi5ojp+LJknPLBuo7XTfp3',
'kj9lifONLwKBgHa3SudRXyTr1rBjC6qGLN2N0IKl03RTcoR5mIsXGVAWOzYJ90fZ',
'y3dqsPPHPwIILZErAQAnMNB1c2oybBJ1Se7LhKwK7Ab7B0aGMXG557xY/ZmSFCvO',
'VVi2vosiAVOfVa/sSWu0rbED226br+l6+OLfa4NI9skdyXmjZLkGJwtxAoGBALDp',
'LkOnzAZ6W4p00rXheg2GBVPQ9jQp38h2hyRlD+HL/OCQnR2wadPNZMyOPcUQ6peH',
'VpE3dozr7EJKK6RKvT1YmmtO2DzUGcjGeI51L5Yf+YzUz0Z8AL1QRwD4Xqha6GqO',
'pMWEYMNtb99MZatXqs8AO8Fg+eAe1Agy4euTgdhxAoGBAPAgrLUl7uJMdCsrHFEG',
'K8CH3BZwz2ytSj6429IdX4OCzkkmdsWnjx5/FK2hc+fXMluhybaIziSntgs55vzZ',
'1hd/Xw1T2FSTS6BuWegxVtjO6BQjbVXrxOumA0PPmFYaGMPvvBQrbZy8OSeuaKD/',
'a9jKZApDKnT8vmYvmyMMtsmE',
'-----END PRIVATE KEY-----',
].join('\n')
export async function buildTestApp(env: NodeJS.ProcessEnv = {}) {
resetOidcKeyCache()
const config = loadConfig({
...process.env,
JWT_SECRET: 'test-secret-at-least-8',
ADMIN_EMAIL: '[email protected]',
ADMIN_PASSWORD: 'adminpass',
DATABASE_URL: 'sqlite::memory:',
ISSUER: 'https://auth.test.local',
OIDC_ISSUER: 'https://auth.test.local',
NODE_ENV: 'test',
AUDIT_INGEST_SECRET: 'dev-audit-ingest-secret',
OIDC_RSA_PRIVATE_KEY: TEST_OIDC_RSA_PRIVATE_KEY,
...env,
})
return buildApp({ config, databaseUrl: 'sqlite::memory:' })
}
export async function adminToken(
app: Awaited<ReturnType<typeof buildTestApp>>,
): Promise<string> {
const login = await app.inject({
method: 'POST',
url: '/api/v1/auth/login',
payload: { email: '[email protected]', password: 'adminpass' },
})
expect(login.statusCode).toBe(200)
return (login.json() as { access_token: string }).access_token
}
+1 -15
View File
@@ -6,21 +6,7 @@ import { jwtVerify, createLocalJWKSet } from 'jose'
import { buildApp } from '../src/app.js'
import { loadConfig } from '../src/config.js'
import { resetOidcKeyCache } from '../src/lib/oidc/keys.js'
async function buildTestApp() {
resetOidcKeyCache()
const config = loadConfig({
...process.env,
JWT_SECRET: 'test-secret-at-least-8',
ADMIN_EMAIL: '[email protected]',
ADMIN_PASSWORD: 'adminpass',
DATABASE_URL: 'sqlite::memory:',
ISSUER: 'https://auth.test.local',
OIDC_ISSUER: 'https://auth.test.local',
NODE_ENV: 'test',
})
return buildApp({ config, databaseUrl: 'sqlite::memory:' })
}
import { buildTestApp } from './helpers.js'
describe('OIDC IdP', () => {
it('serves discovery and JWKS', async () => {
+1 -25
View File
@@ -1,30 +1,6 @@
import { describe, expect, it } from 'vitest'
import { buildApp } from '../src/app.js'
import { loadConfig } from '../src/config.js'
import { createWebauthnCredential } from '@authportal/db'
async function buildTestApp() {
const config = loadConfig({
...process.env,
JWT_SECRET: 'test-secret-at-least-8',
ADMIN_EMAIL: '[email protected]',
ADMIN_PASSWORD: 'adminpass',
DATABASE_URL: 'sqlite::memory:',
ISSUER: 'https://auth.test.local',
NODE_ENV: 'test',
})
return buildApp({ config, databaseUrl: 'sqlite::memory:' })
}
async function adminToken(app: Awaited<ReturnType<typeof buildTestApp>>) {
const login = await app.inject({
method: 'POST',
url: '/api/v1/auth/login',
payload: { email: '[email protected]', password: 'adminpass' },
})
expect(login.statusCode).toBe(200)
return (login.json() as { access_token: string }).access_token
}
import { adminToken, buildTestApp } from './helpers.js'
describe('webauthn / passkeys', () => {
it('exposes webauthn flag on auth config', async () => {