/** * Performance tests for native OAuth C1–C6 changes. * Tier 6 of 7 — docs/COMPANION-APP-OAUTH-SERVERSIDE-GATE.md §7. * * Verifies that key operations complete within reasonable time budgets on the * gateway host (t3.small, 2 vCPU / 2 GB). These are not micro-benchmarks — * they enforce production-viable latency ceilings under realistic single-instance load. * * Budgets: * - savePendingCode: < 50ms per call (file I/O) * - consumePendingCode: < 50ms per call * - applyScopeCeiling: < 1ms per call (pure function) * - isLoopbackUri: < 1ms per call (pure function) * - refresh-token-core rotateToken: < 5ms per call (pure function + crypto) * - Full code-to-token flow (in-memory, no HTTP): < 100ms */ import assert from 'node:assert/strict'; import { describe, it, before, after } from 'node:test'; import { createHash, randomUUID } from 'node:crypto'; import fs from 'node:fs/promises'; import path from 'node:path'; import os from 'node:os'; function sha256b64url(s) { return createHash('sha256').update(s).digest('base64url'); } function elapsed(start) { const [s, ns] = process.hrtime(start); return s * 1000 + ns / 1e6; // milliseconds } describe('C1–C6 Performance', () => { let tmpDir; before(async () => { tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'native-oauth-perf-')); process.env.KNOWTATION_GATEWAY_DATA_DIR = tmpDir; }); after(async () => { delete process.env.KNOWTATION_GATEWAY_DATA_DIR; await fs.rm(tmpDir, { recursive: true, force: true }); }); it('Perf: savePendingCode < 50ms per call', async () => { const { savePendingCode } = await import('../hub/gateway/native-as-store.mjs'); const ITERATIONS = 5; const times = []; for (let i = 0; i < ITERATIONS; i++) { const start = process.hrtime(); await savePendingCode('perf-code-' + i + randomUUID(), { clientId: 'perf-client', codeChallenge: sha256b64url('perf-v-' + i), redirectUri: `http://127.0.0.1:${50000 + i}/cb`, }); times.push(elapsed(start)); } const avg = times.reduce((a, b) => a + b, 0) / times.length; const max = Math.max(...times); console.log(`savePendingCode avg=${avg.toFixed(1)}ms max=${max.toFixed(1)}ms`); assert.ok(max < 200, `max savePendingCode time ${max.toFixed(1)}ms must be < 200ms`); }); it('Perf: consumePendingCode < 50ms per call', async () => { const { savePendingCode, consumePendingCode } = await import('../hub/gateway/native-as-store.mjs'); const codes = []; for (let i = 0; i < 5; i++) { const code = 'perf-consume-' + randomUUID(); codes.push(code); await savePendingCode(code, { clientId: 'perf-consume-client', codeChallenge: sha256b64url('pv-' + i), redirectUri: `http://127.0.0.1:${51000 + i}/cb`, }); } const times = []; for (const code of codes) { const start = process.hrtime(); await consumePendingCode(code); times.push(elapsed(start)); } const max = Math.max(...times); console.log(`consumePendingCode max=${max.toFixed(1)}ms`); assert.ok(max < 200, `max consumePendingCode time ${max.toFixed(1)}ms must be < 200ms`); }); it('Perf: applyScopeCeiling < 1ms per call (pure function)', () => { const ceiling = ['vault:read', 'vault:write']; const requested = ['vault:read', 'admin', 'superuser']; function applyScopeCeiling(r, c) { if (!Array.isArray(r) || r.length === 0) return [...c]; return r.filter(s => c.includes(s)); } const start = process.hrtime(); for (let i = 0; i < 10000; i++) { applyScopeCeiling(requested, ceiling); } const ms = elapsed(start); const perCall = ms / 10000; console.log(`applyScopeCeiling: ${perCall.toFixed(4)}ms/call over 10k iterations`); assert.ok(perCall < 1, `applyScopeCeiling must be < 1ms per call, was ${perCall.toFixed(4)}ms`); }); it('Perf: isLoopbackUri < 1ms per call', async () => { const { isLoopbackUri } = await import('../hub/gateway/native-oauth-provider.mjs'); const uris = [ 'http://127.0.0.1:12345/callback', 'http://[::1]:8080/cb', 'https://evil.com/steal', 'http://localhost:9000/cb', 'not-a-uri', ]; const start = process.hrtime(); for (let i = 0; i < 10000; i++) { isLoopbackUri(uris[i % uris.length]); } const ms = elapsed(start); const perCall = ms / 10000; console.log(`isLoopbackUri: ${perCall.toFixed(4)}ms/call over 10k iterations`); assert.ok(perCall < 1, `isLoopbackUri must be < 1ms per call, was ${perCall.toFixed(4)}ms`); }); it('Perf: refresh-token-core rotateToken < 5ms per call', () => { async function run() { const { issueToken, rotateToken } = await import('../hub/lib/refresh-token-core.mjs'); let { records, token } = issueToken({}, { sub: 'google:perf-rotate' }); const times = []; for (let i = 0; i < 10; i++) { const start = process.hrtime(); const result = rotateToken(records, token); times.push(elapsed(start)); assert.ok(result.ok); records = result.records; token = result.token; } const max = Math.max(...times); console.log(`rotateToken max=${max.toFixed(2)}ms`); assert.ok(max < 50, `rotateToken must be < 50ms per call, was ${max.toFixed(2)}ms`); } return run(); }); it('Perf: PKCE sha256 verification < 1ms per call', () => { const verifier = 'a-realistic-pkce-code-verifier-that-is-43-chars-long-xyz'; const start = process.hrtime(); for (let i = 0; i < 10000; i++) { sha256b64url(verifier); } const ms = elapsed(start); const perCall = ms / 10000; console.log(`sha256b64url: ${perCall.toFixed(4)}ms/call over 10k iterations`); assert.ok(perCall < 1, `PKCE sha256 must be < 1ms per call, was ${perCall.toFixed(4)}ms`); }); it('Perf: discovery endpoint construction is synchronous (< 1ms)', () => { // The discovery endpoint just builds an object — verify it is not doing I/O const baseUrl = 'https://gateway.knowtation.ai'; const start = process.hrtime(); for (let i = 0; i < 1000; i++) { const issuerUrl = `${baseUrl.replace(/\/$/, '')}/api/v1/auth/native`; const _meta = { issuer: issuerUrl, authorization_endpoint: `${issuerUrl}/authorize`, token_endpoint: `${issuerUrl}/token`, registration_endpoint: `${issuerUrl}/register`, revocation_endpoint: `${issuerUrl}/revoke`, response_types_supported: ['code'], grant_types_supported: ['authorization_code', 'refresh_token'], code_challenge_methods_supported: ['S256'], token_endpoint_auth_methods_supported: ['none'], scopes_supported: ['vault:read', 'vault:write'], }; } const ms = elapsed(start); const perCall = ms / 1000; assert.ok(perCall < 1, `discovery construction must be < 1ms per call, was ${perCall.toFixed(4)}ms`); }); });