memory-cap.integration.test.ts51 lines · main
1// Integration test for CLAUDE.md §7.3 — V8 heap cap enforced via
2// `--v8-flags=--max-old-space-size=<maxMemoryMb>`. Customer code that
3// allocates beyond the cap should crash the isolate (V8 OOM). We accept
4// any of the kill-coded errors since the exact surface depends on
5// whether V8 throws a clean RangeError before the heap is exhausted or
6// the process gets killed mid-allocation.
7
8import { describe, expect, test } from 'bun:test';
9
10import { runIntegrationFixture } from './test-helpers.js';
11
12describe('memory cap (integration)', () => {
13 test('allocating beyond cap kills isolate', async () => {
14 const { result, cleanup } = await runIntegrationFixture({
15 fnName: 'test',
16 deploymentId: 'd1',
17 poolConfig: { maxMemoryMb: 64, invocationTimeoutMs: 15_000 },
18 fnSource: `
19 import { query } from '@briven/cli/server';
20 export const test = query(async () => {
21 // V8's --max-old-space-size caps the old-generation heap. Long
22 // strings get an "external" backing store outside the heap, and
23 // typed-array buffers live in array-buffer-allocator memory — both
24 // bypass the cap. Many small JS objects DO live in the old-gen
25 // heap, so we allocate millions of them to force a real heap OOM.
26 const arr: unknown[] = [];
27 for (let i = 0; i < 5_000_000; i++) {
28 arr.push({ a: i, b: i * 2, c: i * 3, d: 'k' + i });
29 }
30 return arr.length;
31 });
32 `,
33 });
34 try {
35 expect(result.ok).toBe(false);
36 if (!result.ok) {
37 // V8 may surface this as a heap RangeError (function_threw),
38 // a process kill (isolate_crashed), or a hung allocation that
39 // hits the invocation timeout. All three prove the cap matters.
40 expect([
41 'isolate_crashed',
42 'memory_limit_exceeded',
43 'invocation_timeout',
44 'function_threw',
45 ]).toContain(result.code);
46 }
47 } finally {
48 await cleanup();
49 }
50 }, 30_000);
51});