1import assert from 'node:assert/strict';
2import { readFile } from 'node:fs/promises';
3import test from 'node:test';
4import vm from 'node:vm';
5
6test('the durability bridge drains files without reentering the editor and waits for ordered worker receipts', async () => {
7 const source = await readFile(new URL('../../crates/snowbound/web/glue.js', import.meta.url), 'utf8');
8 const messages = [];
9 let worker;
10 const element = { style: {}, addEventListener() {}, focus() {}, remove() {} };
11 const context = vm.createContext({
12 Worker: class {
13 constructor() { worker = this; }
14 postMessage(data) {
15 messages.push(data);
16 if (data.kind === 'load') queueMicrotask(() => this.onmessage({ data: [] }));
17 }
18 },
19 Blob,
20 URL,
21 document: { getElementById: () => element, addEventListener() {} },
22 localStorage: { getItem: () => null },
23 ResizeObserver: class { observe() {} },
24 matchMedia: () => ({ addEventListener() {} }),
25 addEventListener() {},
26 console,
27 });
28 vm.runInContext(source.replace(/\bexport /g, '').replaceAll('import.meta.url', JSON.stringify(import.meta.url)), context);
29 await vm.runInContext('loadFiles()', context);
30 const flushed = [];
31 context.module = {
32 flush() { throw Error('Durability reentered State.persist'); },
33 flush_storage() {
34 const marker = flushed.length + 1;
35 flushed.push(marker);
36 context.nextChange = [['replica-wal', marker, [[0, new Uint8Array([marker])]]]];
37 vm.runInContext('storeFiles(nextChange)', context);
38 },
39 };
40 vm.runInContext('attach(module)', context);
41 const replies = [];
42 const first = vm.runInContext('snowboundFlushStorage()', context).then(() => replies.push(1));
43 const second = vm.runInContext('snowboundFlushStorage()', context).then(() => replies.push(2));
44 assert.deepEqual(messages.map(message => message.kind), ['load', 'store', 'settle', 'store', 'settle']);
45 await Promise.resolve();
46 assert.deepEqual(replies, []);
47 worker.onmessage({ data: { error: null } });
48 await first;
49 assert.deepEqual(replies, [1]);
50 worker.onmessage({ data: { error: null } });
51 await second;
52 assert.deepEqual(replies, [1, 2]);
53 const rejected = vm.runInContext('snowboundFlushStorage()', context);
54 worker.onmessage({ data: { error: 'Storage unavailable' } });
55 await assert.rejects(rejected, /Storage unavailable/);
56 const retried = vm.runInContext('snowboundFlushStorage()', context);
57 worker.onmessage({ data: { error: null } });
58 await retried;
59});