Skip to content
File

Blob: src/workerd/api/tests/stub-storage-test.js

javascript107 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 { WorkerEntrypoint, DurableObject } from 'cloudflare:workers';
5import assert from 'node:assert';
6 
7// This test verifies that we can store ServiceStubs (Fetchers) and DurableObjectClasses in DO KV
8// storage.
9 
10export class MyActor extends DurableObject {
11 async get(name) {
12 return await this.ctx.storage.get(name);
13 }
14 async put(name, value) {
15 await this.ctx.storage.put(name, value);
16 }
17 
18 async useFacet(name) {
19 let facet = this.ctx.facets.get(name, async () => {
20 let cls = await this.ctx.storage.get(name);
21 return { class: cls, id: name };
22 });
23 
24 await facet.setName('Bob');
25 return await facet.greet();
26 }
27}
28 
29export class Greeter extends WorkerEntrypoint {
30 async greet(name) {
31 return `${this.ctx.props.greeting}, ${name}!`;
32 }
33}
34 
35export class MyFacet extends DurableObject {
36 async setName(name) {
37 await this.ctx.storage.put('name', name);
38 }
39 
40 async greet() {
41 let name = await this.ctx.storage.get('name');
42 return `${this.ctx.props.greeting}, ${name}?`;
43 }
44}
45 
46export let storeServiceBinding = {
47 async test(controller, env, ctx) {
48 let id = ctx.exports.MyActor.idFromName('foo');
49 let stub = ctx.exports.MyActor.get(id);
50 
51 {
52 await stub.put('foo', ctx.exports.Greeter({ props: { greeting: 'Yo' } }));
53 let greeter = await stub.get('foo');
54 assert.strictEqual(await greeter.greet('Alice'), 'Yo, Alice!');
55 }
56 
57 {
58 await stub.put(
59 'bar',
60 ctx.exports.MyFacet({ props: { greeting: 'Hiya' } })
61 );
62 
63 assert.strictEqual(await stub.useFacet('bar'), 'Hiya, Bob?');
64 }
65 },
66};
67 
68// Test that service stubs and actor classes can both be encoded into `props`, the stub with the
69// props can be sent over RPC, and it all still works.
70export class UsePropsTest extends WorkerEntrypoint {
71 async run() {
72 let id = this.ctx.exports.MyActor.idFromName('bar');
73 let stub = this.ctx.exports.MyActor.get(id);
74 
75 {
76 await stub.put('foo', this.ctx.props.Greeter);
77 let greeter = await stub.get('foo');
78 assert.strictEqual(await greeter.greet('Alice'), 'Yo, Alice!');
79 }
80 
81 {
82 await stub.put('bar', this.ctx.props.MyFacet);
83 
84 assert.strictEqual(await stub.useFacet('bar'), 'Hiya, Bob?');
85 }
86 }
87}
88 
89export let bindingsInProps = {
90 async test(controller, env, ctx) {
91 let stub = ctx.exports.UsePropsTest({
92 props: {
93 Greeter: ctx.exports.Greeter({ props: { greeting: 'Yo' } }),
94 MyFacet: ctx.exports.MyFacet({ props: { greeting: 'Hiya' } }),
95 },
96 });
97 
98 // Send stub over RPC so the props get encoded into a channel token with nested channel
99 // tokens.
100 await ctx.exports.bindingsInProps.run(stub);
101 },
102 
103 async run(stub) {
104 await stub.run();
105 },
106};