bundle-materializer.test.ts112 lines · main
1import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
2import { tmpdir } from 'node:os';
3import { join } from 'node:path';
4
5import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
6
7async function pathExists(p: string): Promise<boolean> {
8 try {
9 await stat(p);
10 return true;
11 } catch {
12 return false;
13 }
14}
15
16import {
17 cleanupIsolate,
18 materializeIsolate,
19 sweepOrphans,
20 type MaterializerConfig,
21} from './bundle-materializer.js';
22import type { Bundle } from './types.js';
23
24describe('bundle-materializer', () => {
25 let workTmp: string;
26 let bundleDir: string;
27 let isolateBase: string;
28 let runtimeStubDir: string;
29
30 beforeEach(async () => {
31 workTmp = await mkdtemp(join(tmpdir(), 'briven-mtest-'));
32 bundleDir = join(workTmp, 'bundle');
33 isolateBase = join(workTmp, 'isolates');
34 runtimeStubDir = join(workTmp, 'stub');
35 await mkdir(join(bundleDir, 'functions'), { recursive: true });
36 await writeFile(
37 join(bundleDir, 'functions', 'poolStats.ts'),
38 'export const poolStats = () => 1;\n',
39 );
40 await mkdir(runtimeStubDir, { recursive: true });
41 await writeFile(join(runtimeStubDir, 'loop.ts'), '// loop stub\n');
42 await writeFile(join(runtimeStubDir, 'server.ts'), '// server stub\n');
43 await writeFile(join(runtimeStubDir, 'types.ts'), '// types stub\n');
44 await mkdir(isolateBase, { recursive: true });
45 });
46
47 afterEach(async () => {
48 await rm(workTmp, { recursive: true, force: true });
49 });
50
51 test('materializes /tmp/briven-isolate-<id>/ layout', async () => {
52 const bundle: Bundle = {
53 projectId: 'p1',
54 deploymentId: 'd1',
55 functionNames: ['poolStats'],
56 directory: bundleDir,
57 };
58 const config: MaterializerConfig = { isolateBaseDir: isolateBase, runtimeStubDir };
59 const result = await materializeIsolate('iso-abc', bundle, config);
60
61 expect(result.tmpDir).toBe(join(isolateBase, 'briven-isolate-iso-abc'));
62 expect(result.entryPath).toBe(join(result.tmpDir, '__entry.ts'));
63 expect(result.importMapPath).toBe(join(result.tmpDir, 'import-map.json'));
64
65 const entryContent = await readFile(result.entryPath, 'utf8');
66 // Fault isolation: each function is loaded via its own dynamic import()
67 // wrapped in try/catch, so one bad file can't kill the whole bundle.
68 expect(entryContent).toContain("await import('./briven/functions/poolStats.ts')");
69 expect(entryContent).toContain('dispatch["poolStats"] = handler;');
70 expect(entryContent).toContain('loadErrors["poolStats"]');
71 expect(entryContent).toContain('runIsolateLoop(dispatch, "d1", loadErrors)');
72
73 const importMap = JSON.parse(await readFile(result.importMapPath, 'utf8'));
74 expect(importMap.imports['@briven/cli/server']).toBe('./.briven-runtime/server.ts');
75
76 const fn = await readFile(
77 join(result.tmpDir, 'briven', 'functions', 'poolStats.ts'),
78 'utf8',
79 );
80 expect(fn).toContain('export const poolStats');
81
82 const loopStub = await readFile(
83 join(result.tmpDir, '.briven-runtime', 'loop.ts'),
84 'utf8',
85 );
86 expect(loopStub).toBe('// loop stub\n');
87 });
88
89 test('cleanupIsolate removes the tmp dir', async () => {
90 const bundle: Bundle = {
91 projectId: 'p1',
92 deploymentId: 'd1',
93 functionNames: [],
94 directory: bundleDir,
95 };
96 const config: MaterializerConfig = { isolateBaseDir: isolateBase, runtimeStubDir };
97 const result = await materializeIsolate('iso-x', bundle, config);
98 await cleanupIsolate(result.tmpDir);
99 expect(await pathExists(join(result.tmpDir, '__entry.ts'))).toBe(false);
100 expect(await pathExists(result.tmpDir)).toBe(false);
101 });
102
103 test('sweepOrphans removes briven-isolate-* dirs not in the live set', async () => {
104 await mkdir(join(isolateBase, 'briven-isolate-stale1'), { recursive: true });
105 await mkdir(join(isolateBase, 'briven-isolate-live1'), { recursive: true });
106 await mkdir(join(isolateBase, 'unrelated-dir'), { recursive: true });
107 await sweepOrphans(isolateBase, new Set(['live1']));
108 expect(await pathExists(join(isolateBase, 'briven-isolate-stale1'))).toBe(false);
109 expect(await pathExists(join(isolateBase, 'briven-isolate-live1'))).toBe(true);
110 expect(await pathExists(join(isolateBase, 'unrelated-dir'))).toBe(true);
111 });
112});