forked from cloudflare/capnweb
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathworkerd.test.ts
More file actions
286 lines (227 loc) · 7.99 KB
/
workerd.test.ts
File metadata and controls
286 lines (227 loc) · 7.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// Copyright (c) 2025 Cloudflare, Inc.
// Licensed under the MIT license found in the LICENSE.txt file or at:
// https://opensource.org/license/mit
/// <reference types="@cloudflare/workers-types" />
import { expect, it, describe } from "vitest";
import { RpcStub as NativeRpcStub, RpcTarget as NativeRpcTarget, env, DurableObject } from "cloudflare:workers";
import { newHttpBatchRpcSession, newWebSocketRpcSession, RpcStub, RpcTarget } from "../src/index.js";
import { Counter, TestTarget } from "./test-util.js";
class JsCounter extends RpcTarget {
constructor(private i: number = 0) {
super();
}
increment(amount: number = 1): number {
this.i += amount;
return this.i;
}
get value() {
return this.i;
}
}
class NativeCounter extends RpcTarget {
constructor(private i: number = 0) {
super();
}
increment(amount: number = 1): number {
this.i += amount;
return this.i;
}
get value() {
return this.i;
}
}
class CounterFactory extends RpcTarget {
getNative() {
return new NativeRpcStub(new NativeCounter());
}
getNativeEmbedded() {
return {stub: new NativeRpcStub(new NativeCounter())};
}
getJs() {
return new RpcStub(new JsCounter());
}
getJsEmbedded() {
return {stub: new RpcStub(new JsCounter())};
}
}
describe("workerd compatibility", () => {
it("allows native RpcStubs to be created using userspace RpcTargets", async () => {
let stub = new NativeRpcStub(new JsCounter());
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
})
it("allows userspace RpcStubs to be created using native RpcTargets", async () => {
let stub = new RpcStub(new NativeCounter());
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
})
it("can wrap a native stub in a userspace stub", async () => {
let stub = new RpcStub(new NativeRpcStub(new NativeCounter()));
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
})
it("can return a native stub from a userspace call", async () => {
// Returning a bare stub.
{
let factory = new RpcStub(new CounterFactory());
let stub = await factory.getNative();
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
}
// Again with a stub wrapped in an object.
{
let factory = new RpcStub(new CounterFactory());
let obj = await factory.getNativeEmbedded();
expect(await obj.stub.increment()).toBe(1);
expect(await obj.stub.increment()).toBe(2);
expect(await obj.stub.value).toBe(2);
}
})
it("can wrap a native promise or property in a userspace stub", async () => {
// Wrap a native RpcPromise in a userspace stub.
{
let factory = new NativeRpcStub(new CounterFactory());
let stub = new RpcStub(factory.getNative());
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
}
// Wrap a native RpcProperty in a userspace stub.
{
let factory = new NativeRpcStub(new CounterFactory());
let stub = new RpcStub(factory.getNativeEmbedded().stub);
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
}
})
it("can pipeline on a native stub returned from a userspace call", async () => {
{
let factory = new RpcStub(new CounterFactory());
let obj = factory.getNative();
expect(await obj.increment()).toBe(1);
expect(await obj.increment()).toBe(2);
expect(await obj.value).toBe(2);
}
{
let factory = new RpcStub(new CounterFactory());
let obj = factory.getNativeEmbedded();
expect(await obj.stub.increment()).toBe(1);
expect(await obj.stub.increment()).toBe(2);
expect(await obj.stub.value).toBe(2);
}
})
it("can wrap a userspace stub in a native stub", async () => {
let stub = new NativeRpcStub(new RpcStub(new JsCounter()));
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
})
it("can return a userspace stub from a native call", async () => {
// Returning a bare stub.
{
let factory = new NativeRpcStub(new CounterFactory());
let stub = await factory.getJs();
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
}
// Again with a stub wrapped in an object.
{
let factory = new NativeRpcStub(new CounterFactory());
let obj = await factory.getJsEmbedded();
expect(await obj.stub.increment()).toBe(1);
expect(await obj.stub.increment()).toBe(2);
expect(await obj.stub.value).toBe(2);
}
})
it("can wrap a userspace promise or property in a native stub", async () => {
// Wrap a userspace RpcPromise in a native stub.
{
let factory = new RpcStub(new CounterFactory());
let stub = new NativeRpcStub(factory.getJs());
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
}
// Wrap a userspace property (which is actually also an RpcPromise) in a native stub.
{
let factory = new RpcStub(new CounterFactory());
let stub = new NativeRpcStub(factory.getJsEmbedded().stub);
expect(await stub.increment()).toBe(1);
expect(await stub.increment()).toBe(2);
expect(await stub.value).toBe(2);
}
})
it("can pipeline on a userspace stub returned from a native call", async () => {
{
let factory = new NativeRpcStub(new CounterFactory());
let obj = factory.getJs();
expect(await obj.increment()).toBe(1);
expect(await obj.increment()).toBe(2);
expect(await obj.value).toBe(2);
}
{
let factory = new NativeRpcStub(new CounterFactory());
let obj = factory.getJsEmbedded();
expect(await obj.stub.increment()).toBe(1);
expect(await obj.stub.increment()).toBe(2);
expect(await obj.stub.value).toBe(2);
}
})
it("can wrap a ServiceStub in an RpcStub", async () => {
let result = await new RpcStub((<any>env).testServer).greet("World");
expect(result).toBe("Hello, World!");
});
});
interface Env {
testServer: Fetcher
}
interface TestDo extends DurableObject {
setValue(val: any): void;
getValue(): any;
}
interface WorkerdTestTarget extends TestTarget {
getDurableObject(name: string): DurableObjectStub<TestDo>;
}
describe("workerd RPC server", () => {
it("can accept WebSocket RPC connections", async () => {
let resp = await (<Env>env).testServer.fetch("http://foo", {headers: {Upgrade: "websocket"}});
let ws = resp.webSocket;
expect(ws).toBeTruthy();
ws!.accept();
let cap = newWebSocketRpcSession<WorkerdTestTarget>(ws!);
expect(await cap.square(5)).toBe(25);
{
let counter = cap.makeCounter(2);
expect(await counter.increment(3)).toBe(5);
}
{
let counter = new Counter(4);
expect(await cap.incrementCounter(counter, 9)).toBe(13);
}
// Test that we can pass a Durable Object stub over RPC.
{
let foo = cap.getDurableObject("foo");
foo.setValue(123);
let bar = await cap.getDurableObject("bar");
bar.setValue("abc");
expect(await foo.getValue()).toBe(123);
expect(await bar.getValue()).toBe("abc");
}
})
it("can accept HTTP batch RPC connections", async () => {
let cap = newHttpBatchRpcSession<TestTarget>(
new Request("http://foo", {fetcher: (<Env>env).testServer}));
let promise1 = cap.square(6);
let counter = cap.makeCounter(2);
let promise2 = counter.increment(3);
let promise3 = cap.incrementCounter(counter, 4);
expect(await Promise.all([promise1, promise2, promise3]))
.toStrictEqual([36, 5, 9]);
})
});