Skip to content
File

Blob: src/workerd/api/tests/rpc-error-test.js

javascript105 lines
1// Copyright (c) 2025 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4import { notStrictEqual, ok, strictEqual, throws } from 'assert';
5 
6export const test = {
7 async test(_, env) {
8 try {
9 await env.RPC.foo(null);
10 throw new Error('Expected error not thrown');
11 } catch (e) {
12 // The error returned by RPC will have a stack2 property that contains
13 // the original stack from the worker where the error was thrown.
14 notStrictEqual(e.stack2, undefined);
15 // Because we are not setting the trusted option when deserializing the
16 // error, the stack of the error reported here should be different from
17 // the original stack.
18 notStrictEqual(e.stack2, e.stack);
19 }
20 
21 try {
22 const bar = await env.RPC.bar(null);
23 const baz = await bar.baz(null);
24 await baz.qux(null);
25 throw new Error('Expected error not thrown');
26 } catch (e) {
27 // The error returned by RPC will have a stack2 property that contains
28 // the original stack from the worker where the error was thrown.
29 notStrictEqual(e.stack2, undefined);
30 // Because we are not setting the trusted option when deserializing the
31 // error, the stack of the error reported here should be different from
32 // the original stack.
33 notStrictEqual(e.stack2, e.stack);
34 }
35 
36 try {
37 await env.RPC.xyz(null);
38 throw new Error('Expected error not thrown');
39 } catch (e) {
40 // The error returned by RPC will have a stack2 property that contains
41 // the original stack from the worker where the error was thrown.
42 notStrictEqual(e.stack2, undefined);
43 // Because we are not setting the trusted option when deserializing the
44 // error, the stack of the error reported here should be different from
45 // the original stack.
46 notStrictEqual(e.stack2, e.stack);
47 }
48 
49 {
50 const err = await env.RPC.abc(null).a;
51 // The error returned by RPC will have a stack2 property that contains
52 // the original stack from the worker where the error was thrown.
53 notStrictEqual(err.stack2, undefined);
54 // Because we are not setting the trusted option when deserializing the
55 // error, the stack of the error reported here should be different from
56 // the original stack.
57 notStrictEqual(err.stack2, err.stack);
58 }
59 },
60};
61 
62export let structuredCloneNotBroken = {
63 test(controller, env, ctx) {
64 // At one point, enhanced_error_serialization inadvertently broke structuredClone()'s support
65 // for host objects.
66 let orig = new Headers({ foo: '123', bar: 'abc' });
67 let cloned = structuredClone(orig);
68 ok(cloned instanceof Headers);
69 strictEqual(cloned.get('foo'), '123');
70 strictEqual(cloned.get('bar'), 'abc');
71 
72 throws(() => structuredClone(new TextEncoder()), {
73 name: 'DataCloneError',
74 code: DOMException.DATA_CLONE_ERR,
75 message:
76 'Could not serialize object of type "TextEncoder". This type does not support ' +
77 'serialization.',
78 });
79 },
80};
81 
82// DOMException is structured cloneable
83export let domExceptionClone = {
84 test() {
85 const de1 = new DOMException('hello', 'NotAllowedError');
86 
87 de1.foo = 'abc';
88 
89 const de2 = structuredClone(de1);
90 ok(de2 instanceof DOMException);
91 strictEqual(de1.name, de2.name);
92 strictEqual(de1.message, de2.message);
93 strictEqual(de1.stack, de2.stack);
94 strictEqual(de1.code, de2.code);
95 notStrictEqual(de1, de2);
96 
97 // TODO(bug): This is supposed to work, but currently doesn't because the "native error"
98 // handling in jsg::Serializer does not kick in for DOMException. Instead, DOMException's
99 // own serializer (in dom-exception.c++) is used, and it hasn't been updated to handle
100 // serializing application properties.
101 // strictEqual(de1.foo, de2.foo);
102 // strictEqual(de2.foo, 'abc');
103 },
104};