companion-runtime-manager-stress.test.mjs
220 lines 8.3 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Tier 4 — STRESS: lib/companion-runtime-manager.mjs
3 *
4 * High-volume, adversarial-volume, and boundary-condition tests. Verifies that the pure
5 * functions remain correct and do not corrupt state under heavy concurrent-use simulation,
6 * floods of requests at the backpressure bound, and pathological inputs.
7 *
8 * Reference: docs/COMPANION-APP-PHASE-4-RUNTIME-MANAGER.md §4 (backpressure), §5 (Phase 5 obligations).
9 */
10
11 import { describe, it } from 'node:test';
12 import assert from 'node:assert/strict';
13 import crypto from 'node:crypto';
14
15 import {
16 RUNTIME_MANAGER_REASONS,
17 LIFECYCLE_STATES,
18 LIFECYCLE_EVENTS,
19 createIntegrityAccumulator,
20 createLifecycleState,
21 transitionLifecycle,
22 canServeInference,
23 createAdmissionState,
24 evaluateAdmission,
25 recordInFlight,
26 recordCompletion,
27 createResourceLimits,
28 evaluateResourceLimits,
29 evaluateRuntimeRequest,
30 } from '../lib/companion-runtime-manager.mjs';
31
32 function makeDigest(data) {
33 return crypto.createHash('sha256').update(data).digest('hex');
34 }
35
36 const ALLOWED_URLS = ['https://models.example.com/'];
37 const VALID_URL = 'https://models.example.com/model.gguf';
38 const READY_STATE = { state: LIFECYCLE_STATES.READY };
39 const VALID_LIMITS = createResourceLimits({ maxRamBytes: 8e9, maxVramBytes: 4e9, maxCpuPercent: 80 });
40 const VALID_OBS = { ramBytes: 1e9, vramBytes: 0.5e9, cpuPercent: 10 };
41
42 // ── Stress: lifecycle transitions ────────────────────────────────────────────
43
44 describe('stress: 10k lifecycle round-trips', () => {
45 it('produces correct terminal state after 10000 stopped→starting→ready→drain→stopped cycles', () => {
46 let lifecycle = createLifecycleState();
47 const N = 10_000;
48
49 for (let i = 0; i < N; i++) {
50 lifecycle = transitionLifecycle(lifecycle, LIFECYCLE_EVENTS.START).newState;
51 lifecycle = transitionLifecycle(lifecycle, LIFECYCLE_EVENTS.HEALTH_OK).newState;
52 lifecycle = transitionLifecycle(lifecycle, LIFECYCLE_EVENTS.DRAIN).newState;
53 lifecycle = transitionLifecycle(lifecycle, LIFECYCLE_EVENTS.STOPPED).newState;
54 }
55
56 assert.equal(lifecycle.state, LIFECYCLE_STATES.STOPPED);
57 assert.equal(canServeInference(lifecycle), false);
58 });
59 });
60
61 // ── Stress: admission at backpressure bound ───────────────────────────────────
62
63 describe('stress: backpressure trips at exact bound', () => {
64 it('MAX_IN_FLIGHT=100 — 100 admissions succeed, 101st is AT_CAPACITY', () => {
65 const MAX = 100;
66 let admission = createAdmissionState({ maxInFlight: MAX, queueBound: 50 });
67 const limits = VALID_LIMITS;
68 const obs = VALID_OBS;
69
70 for (let i = 0; i < MAX; i++) {
71 const d = evaluateRuntimeRequest({
72 lifecycleState: READY_STATE, admissionState: admission,
73 resourceObservation: obs, resourceLimits: limits,
74 });
75 assert.equal(d.ok, true, `request ${i + 1} should be admitted`);
76 admission = recordInFlight(admission);
77 }
78
79 // 101st hits AT_CAPACITY
80 const over = evaluateRuntimeRequest({
81 lifecycleState: READY_STATE, admissionState: admission,
82 resourceObservation: obs, resourceLimits: limits,
83 });
84 assert.equal(over.ok, false);
85 assert.equal(over.reason, RUNTIME_MANAGER_REASONS.AT_CAPACITY);
86 assert.equal(admission.inFlight, MAX); // exact bound
87 });
88
89 it('QUEUE_FULL trips at exact queueBound', () => {
90 const MAX_FLIGHT = 2;
91 const QUEUE = 10;
92 let admission = createAdmissionState({ maxInFlight: MAX_FLIGHT, queueBound: QUEUE });
93
94 // Fill in-flight
95 for (let i = 0; i < MAX_FLIGHT; i++) admission = recordInFlight(admission);
96 // Fill queue
97 for (let i = 0; i < QUEUE; i++) admission = { ...admission, queued: admission.queued + 1 };
98
99 const r = evaluateAdmission(admission);
100 assert.equal(r.ok, false);
101 assert.equal(r.reason, RUNTIME_MANAGER_REASONS.QUEUE_FULL);
102 assert.equal(admission.inFlight, MAX_FLIGHT);
103 assert.equal(admission.queued, QUEUE);
104 });
105 });
106
107 // ── Stress: 50k admission evaluations ────────────────────────────────────────
108
109 describe('stress: 50k admission evaluations — no state corruption', () => {
110 it('evaluateAdmission gives consistent ok:true when under limit for 50000 calls', () => {
111 const admission = createAdmissionState({ maxInFlight: 10, queueBound: 20 });
112 const underLimit = { ...admission, inFlight: 5 };
113
114 for (let i = 0; i < 50_000; i++) {
115 const r = evaluateAdmission(underLimit);
116 assert.equal(r.ok, true, `failed at iteration ${i}`);
117 }
118 // Original unchanged (pure)
119 assert.equal(underLimit.inFlight, 5);
120 });
121 });
122
123 // ── Stress: integrity accumulator — 1000 chunks ─────────────────────────────
124
125 describe('stress: integrity accumulator with 1000 chunks', () => {
126 it('correctly verifies 100 KB model data split into 1000 chunks of 100 bytes each', () => {
127 const CHUNK_SIZE = 100;
128 const NUM_CHUNKS = 1000;
129 const data = crypto.randomBytes(CHUNK_SIZE * NUM_CHUNKS);
130 const digest = makeDigest(data);
131
132 const acc = createIntegrityAccumulator({
133 expectedDigest: digest, expectedSizeBytes: data.length,
134 sourceUrl: VALID_URL, allowedSourceUrls: ALLOWED_URLS,
135 });
136
137 for (let i = 0; i < NUM_CHUNKS; i++) {
138 acc.update(data.subarray(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE));
139 }
140
141 assert.equal(acc.getReceivedBytes(), data.length);
142 const verdict = acc.finalize();
143 assert.equal(verdict.ok, true, `integrity failed: ${verdict.reason}`);
144 });
145
146 it('detects 1-byte corruption in 100KB data', () => {
147 const data = crypto.randomBytes(100_000);
148 const digest = makeDigest(data);
149
150 // Corrupt 1 byte
151 const corrupted = Buffer.from(data);
152 corrupted[50_000] ^= 0xff;
153
154 const acc = createIntegrityAccumulator({
155 expectedDigest: digest, expectedSizeBytes: corrupted.length,
156 sourceUrl: VALID_URL, allowedSourceUrls: ALLOWED_URLS,
157 });
158 acc.update(corrupted);
159 const verdict = acc.finalize();
160 assert.equal(verdict.ok, false);
161 assert.equal(verdict.reason, RUNTIME_MANAGER_REASONS.DIGEST_MISMATCH);
162 });
163 });
164
165 // ── Stress: evaluateResourceLimits under varied inputs ───────────────────────
166
167 describe('stress: resource limit evaluation 20k calls', () => {
168 it('consistently returns correct verdicts for boundary values', () => {
169 const limits = VALID_LIMITS;
170
171 // Exactly at limit → ok
172 const atLimit = { ramBytes: limits.maxRamBytes, vramBytes: limits.maxVramBytes, cpuPercent: limits.maxCpuPercent };
173 for (let i = 0; i < 20_000; i++) {
174 const r = evaluateResourceLimits(atLimit, limits);
175 assert.equal(r.ok, true, `failed at iteration ${i}`);
176 }
177
178 // One byte over RAM → always fail
179 const overRam = { ...atLimit, ramBytes: limits.maxRamBytes + 1 };
180 for (let i = 0; i < 20_000; i++) {
181 const r = evaluateResourceLimits(overRam, limits);
182 assert.equal(r.ok, false, `should fail at iteration ${i}`);
183 assert.equal(r.reason, RUNTIME_MANAGER_REASONS.RAM_OVER_LIMIT);
184 }
185 });
186 });
187
188 // ── Stress: evaluateRuntimeRequest — 10k combined gate evaluations ────────────
189
190 describe('stress: evaluateRuntimeRequest 10k calls', () => {
191 it('all green — consistent ok:true', () => {
192 const admission = createAdmissionState({ maxInFlight: 100, queueBound: 200 });
193
194 for (let i = 0; i < 10_000; i++) {
195 const r = evaluateRuntimeRequest({
196 lifecycleState: READY_STATE,
197 admissionState: admission,
198 resourceObservation: VALID_OBS,
199 resourceLimits: VALID_LIMITS,
200 });
201 assert.equal(r.ok, true, `failed at iteration ${i}`);
202 }
203 });
204
205 it('all red — NOT_READY when not ready for 10k calls', () => {
206 const stopped = createLifecycleState();
207 const admission = createAdmissionState({ maxInFlight: 100, queueBound: 200 });
208
209 for (let i = 0; i < 10_000; i++) {
210 const r = evaluateRuntimeRequest({
211 lifecycleState: stopped,
212 admissionState: admission,
213 resourceObservation: VALID_OBS,
214 resourceLimits: VALID_LIMITS,
215 });
216 assert.equal(r.ok, false, `should fail at ${i}`);
217 assert.equal(r.reason, RUNTIME_MANAGER_REASONS.NOT_READY);
218 }
219 });
220 });
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago