companion-runtime-manager-stress.test.mjs
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