native-oauth-c1-c6-performance.test.mjs file-level

at sha256:3 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:9 feat(calendar): hosted bridge/gateway route parity and timeline noteRec… · aaronrene · Jun 19, 2026
1 /**
2 * Performance tests for native OAuth C1–C6 changes.
3 * Tier 6 of 7 — docs/COMPANION-APP-OAUTH-SERVERSIDE-GATE.md §7.
4 *
5 * Verifies that key operations complete within reasonable time budgets on the
6 * gateway host (t3.small, 2 vCPU / 2 GB). These are not micro-benchmarks —
7 * they enforce production-viable latency ceilings under realistic single-instance load.
8 *
9 * Budgets:
10 * - savePendingCode: < 50ms per call (file I/O)
11 * - consumePendingCode: < 50ms per call
12 * - applyScopeCeiling: < 1ms per call (pure function)
13 * - isLoopbackUri: < 1ms per call (pure function)
14 * - refresh-token-core rotateToken: < 5ms per call (pure function + crypto)
15 * - Full code-to-token flow (in-memory, no HTTP): < 100ms
16 */
17
18 import assert from 'node:assert/strict';
19 import { describe, it, before, after } from 'node:test';
20 import { createHash, randomUUID } from 'node:crypto';
21 import fs from 'node:fs/promises';
22 import path from 'node:path';
23 import os from 'node:os';
24
25 function sha256b64url(s) {
26 return createHash('sha256').update(s).digest('base64url');
27 }
28
29 function elapsed(start) {
30 const [s, ns] = process.hrtime(start);
31 return s * 1000 + ns / 1e6; // milliseconds
32 }
33
34 describe('C1–C6 Performance', () => {
35 let tmpDir;
36
37 before(async () => {
38 tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'native-oauth-perf-'));
39 process.env.KNOWTATION_GATEWAY_DATA_DIR = tmpDir;
40 });
41
42 after(async () => {
43 delete process.env.KNOWTATION_GATEWAY_DATA_DIR;
44 await fs.rm(tmpDir, { recursive: true, force: true });
45 });
46
47 it('Perf: savePendingCode < 50ms per call', async () => {
48 const { savePendingCode } = await import('../hub/gateway/native-as-store.mjs');
49 const ITERATIONS = 5;
50 const times = [];
51 for (let i = 0; i < ITERATIONS; i++) {
52 const start = process.hrtime();
53 await savePendingCode('perf-code-' + i + randomUUID(), {
54 clientId: 'perf-client',
55 codeChallenge: sha256b64url('perf-v-' + i),
56 redirectUri: `http://127.0.0.1:${50000 + i}/cb`,
57 });
58 times.push(elapsed(start));
59 }
60 const avg = times.reduce((a, b) => a + b, 0) / times.length;
61 const max = Math.max(...times);
62 console.log(`savePendingCode avg=${avg.toFixed(1)}ms max=${max.toFixed(1)}ms`);
63 assert.ok(max < 200, `max savePendingCode time ${max.toFixed(1)}ms must be < 200ms`);
64 });
65
66 it('Perf: consumePendingCode < 50ms per call', async () => {
67 const { savePendingCode, consumePendingCode } = await import('../hub/gateway/native-as-store.mjs');
68 const codes = [];
69 for (let i = 0; i < 5; i++) {
70 const code = 'perf-consume-' + randomUUID();
71 codes.push(code);
72 await savePendingCode(code, {
73 clientId: 'perf-consume-client',
74 codeChallenge: sha256b64url('pv-' + i),
75 redirectUri: `http://127.0.0.1:${51000 + i}/cb`,
76 });
77 }
78 const times = [];
79 for (const code of codes) {
80 const start = process.hrtime();
81 await consumePendingCode(code);
82 times.push(elapsed(start));
83 }
84 const max = Math.max(...times);
85 console.log(`consumePendingCode max=${max.toFixed(1)}ms`);
86 assert.ok(max < 200, `max consumePendingCode time ${max.toFixed(1)}ms must be < 200ms`);
87 });
88
89 it('Perf: applyScopeCeiling < 1ms per call (pure function)', () => {
90 const ceiling = ['vault:read', 'vault:write'];
91 const requested = ['vault:read', 'admin', 'superuser'];
92 function applyScopeCeiling(r, c) {
93 if (!Array.isArray(r) || r.length === 0) return [...c];
94 return r.filter(s => c.includes(s));
95 }
96 const start = process.hrtime();
97 for (let i = 0; i < 10000; i++) {
98 applyScopeCeiling(requested, ceiling);
99 }
100 const ms = elapsed(start);
101 const perCall = ms / 10000;
102 console.log(`applyScopeCeiling: ${perCall.toFixed(4)}ms/call over 10k iterations`);
103 assert.ok(perCall < 1, `applyScopeCeiling must be < 1ms per call, was ${perCall.toFixed(4)}ms`);
104 });
105
106 it('Perf: isLoopbackUri < 1ms per call', async () => {
107 const { isLoopbackUri } = await import('../hub/gateway/native-oauth-provider.mjs');
108 const uris = [
109 'http://127.0.0.1:12345/callback',
110 'http://[::1]:8080/cb',
111 'https://evil.com/steal',
112 'http://localhost:9000/cb',
113 'not-a-uri',
114 ];
115 const start = process.hrtime();
116 for (let i = 0; i < 10000; i++) {
117 isLoopbackUri(uris[i % uris.length]);
118 }
119 const ms = elapsed(start);
120 const perCall = ms / 10000;
121 console.log(`isLoopbackUri: ${perCall.toFixed(4)}ms/call over 10k iterations`);
122 assert.ok(perCall < 1, `isLoopbackUri must be < 1ms per call, was ${perCall.toFixed(4)}ms`);
123 });
124
125 it('Perf: refresh-token-core rotateToken < 5ms per call', () => {
126 async function run() {
127 const { issueToken, rotateToken } = await import('../hub/lib/refresh-token-core.mjs');
128 let { records, token } = issueToken({}, { sub: 'google:perf-rotate' });
129 const times = [];
130 for (let i = 0; i < 10; i++) {
131 const start = process.hrtime();
132 const result = rotateToken(records, token);
133 times.push(elapsed(start));
134 assert.ok(result.ok);
135 records = result.records;
136 token = result.token;
137 }
138 const max = Math.max(...times);
139 console.log(`rotateToken max=${max.toFixed(2)}ms`);
140 assert.ok(max < 50, `rotateToken must be < 50ms per call, was ${max.toFixed(2)}ms`);
141 }
142 return run();
143 });
144
145 it('Perf: PKCE sha256 verification < 1ms per call', () => {
146 const verifier = 'a-realistic-pkce-code-verifier-that-is-43-chars-long-xyz';
147 const start = process.hrtime();
148 for (let i = 0; i < 10000; i++) {
149 sha256b64url(verifier);
150 }
151 const ms = elapsed(start);
152 const perCall = ms / 10000;
153 console.log(`sha256b64url: ${perCall.toFixed(4)}ms/call over 10k iterations`);
154 assert.ok(perCall < 1, `PKCE sha256 must be < 1ms per call, was ${perCall.toFixed(4)}ms`);
155 });
156
157 it('Perf: discovery endpoint construction is synchronous (< 1ms)', () => {
158 // The discovery endpoint just builds an object — verify it is not doing I/O
159 const baseUrl = 'https://gateway.knowtation.ai';
160 const start = process.hrtime();
161 for (let i = 0; i < 1000; i++) {
162 const issuerUrl = `${baseUrl.replace(/\/$/, '')}/api/v1/auth/native`;
163 const _meta = {
164 issuer: issuerUrl,
165 authorization_endpoint: `${issuerUrl}/authorize`,
166 token_endpoint: `${issuerUrl}/token`,
167 registration_endpoint: `${issuerUrl}/register`,
168 revocation_endpoint: `${issuerUrl}/revoke`,
169 response_types_supported: ['code'],
170 grant_types_supported: ['authorization_code', 'refresh_token'],
171 code_challenge_methods_supported: ['S256'],
172 token_endpoint_auth_methods_supported: ['none'],
173 scopes_supported: ['vault:read', 'vault:write'],
174 };
175 }
176 const ms = elapsed(start);
177 const perCall = ms / 1000;
178 assert.ok(perCall < 1, `discovery construction must be < 1ms per call, was ${perCall.toFixed(4)}ms`);
179 });
180 });