Offload functions to worker threads with shared memory primitives for Node.js.
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 130 lines 3.9 kB view raw
1import { describe, it } from 'node:test'; 2import assert from 'node:assert/strict'; 3import { workers } from 'moroutine'; 4import { fail, failType, failCause } from './fixtures/math.ts'; 5import { failAfterType } from './fixtures/stream-gen.ts'; 6 7describe('error handling', () => { 8 it('rejects with error from dedicated worker', async () => { 9 await assert.rejects(async () => fail('boom').then((x) => x), { 10 message: 'boom', 11 }); 12 }); 13 14 it('rejects with error from pool worker', async () => { 15 await using run = workers(1); 16 await assert.rejects(() => run(fail('pool boom')), { 17 message: 'pool boom', 18 }); 19 }); 20 21 it('preserves stack trace pointing to worker source via cause', async () => { 22 await using run = workers(1); 23 try { 24 await run(fail('stack check')); 25 assert.fail('should have thrown'); 26 } catch (err) { 27 assert.ok(err instanceof Error); 28 const cause = err.cause as Error; 29 assert.ok(cause instanceof Error); 30 assert.match(cause.stack!, /fixtures\/math\.ts/); 31 } 32 }); 33 34 it('preserves built-in error subclass identity', async () => { 35 await using run = workers(1); 36 try { 37 await run(failType('type check')); 38 assert.fail('should have thrown'); 39 } catch (err) { 40 assert.ok(err instanceof TypeError); 41 assert.equal((err as Error).message, 'type check'); 42 } 43 }); 44 45 it('preserves error cause (nested through wrapper)', async () => { 46 await using run = workers(1); 47 try { 48 await run(failCause('with cause')); 49 assert.fail('should have thrown'); 50 } catch (err) { 51 assert.ok(err instanceof Error); 52 // outer wrapper has the worker error as cause; worker error has RangeError as its cause 53 const workerErr = (err as Error).cause as Error; 54 assert.ok(workerErr instanceof Error); 55 assert.ok(workerErr.cause instanceof RangeError); 56 assert.equal((workerErr.cause as Error).message, 'root cause'); 57 } 58 }); 59 60 it('preserves error details on dedicated worker', async () => { 61 try { 62 await failType('dedicated type'); 63 assert.fail('should have thrown'); 64 } catch (err) { 65 assert.ok(err instanceof TypeError); 66 const cause = (err as Error).cause as Error; 67 assert.match(cause.stack!, /fixtures\/math\.ts/); 68 } 69 }); 70 71 it('main-thread stack includes run() caller', async () => { 72 await using run = workers(1); 73 74 async function someCaller() { 75 await run(fail('trace check')); 76 } 77 78 try { 79 await someCaller(); 80 assert.fail('should have thrown'); 81 } catch (err) { 82 assert.ok(err instanceof Error); 83 assert.match((err as Error).stack!, /someCaller/); 84 } 85 }); 86 87 it('main-thread stack includes exec() caller', async () => { 88 await using run = workers(1); 89 90 async function someCaller() { 91 await run.workers[0].exec(fail('exec trace check')); 92 } 93 94 try { 95 await someCaller(); 96 assert.fail('should have thrown'); 97 } catch (err) { 98 assert.ok(err instanceof Error); 99 assert.match((err as Error).stack!, /someCaller/); 100 } 101 }); 102 103 it('main-thread stack includes await-task caller on dedicated worker', async () => { 104 async function someCaller() { 105 await fail('dedicated trace check'); 106 } 107 108 try { 109 await someCaller(); 110 assert.fail('should have thrown'); 111 } catch (err) { 112 assert.ok(err instanceof Error); 113 assert.match((err as Error).stack!, /someCaller/); 114 } 115 }); 116 117 it('preserves error details on streaming task', async () => { 118 await using run = workers(1); 119 try { 120 for await (const _ of run(failAfterType(2))) { 121 // consume yields until error 122 } 123 assert.fail('should have thrown'); 124 } catch (err) { 125 assert.ok(err instanceof TypeError); 126 assert.equal((err as Error).message, 'stream type error'); 127 assert.match((err as Error).stack!, /fixtures\/stream-gen\.ts/); 128 } 129 }); 130});