Skip to content
File

Blob: src/workerd/server/tests/unsafe-module/unsafe-module-test.js

javascript169 lines
1// Copyright (c) 2023 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 assert from 'node:assert';
5import unsafe from 'workerd:unsafe';
6import { DurableObject } from 'cloudflare:workers';
7 
8function createTestObject(type) {
9 return class {
10 id = crypto.randomUUID();
11 fetch() {
12 return new Response(`${type}:${this.id}`);
13 }
14 };
15}
16export const TestDurableObject = createTestObject('durable');
17export const TestDurableObjectPreventEviction = createTestObject(
18 'durable-prevent-eviction'
19);
20export const TestEphemeralObject = createTestObject('ephemeral');
21export const TestEphemeralObjectPreventEviction = createTestObject(
22 'ephemeral-prevent-eviction'
23);
24 
25// DO with persistent storage for verifying deleteAllDurableObjects() wipes data.
26export class StorageObject extends DurableObject {
27 async getValue() {
28 return (await this.ctx.storage.get('key')) ?? null;
29 }
30 async setValue(value) {
31 await this.ctx.storage.put('key', value);
32 }
33}
34 
35// DO with an alarm for verifying deleteAllDurableObjects() cancels alarms.
36let alarmTriggers = 0;
37export class AlarmObject extends DurableObject {
38 get scheduledTime() {
39 return this.ctx.storage.getAlarm();
40 }
41 async scheduleIn(delay) {
42 await this.ctx.storage.setAlarm(Date.now() + delay);
43 }
44 alarm() {
45 alarmTriggers++;
46 }
47}
48 
49export const test_abort_all_durable_objects = {
50 async test(ctrl, env, ctx) {
51 const durableId = env.DURABLE.newUniqueId();
52 const durablePreventEvictionId = env.DURABLE_PREVENT_EVICTION.newUniqueId();
53 
54 let durableStub = env.DURABLE.get(durableId);
55 const durablePreventEvictionStub = env.DURABLE_PREVENT_EVICTION.get(
56 durablePreventEvictionId
57 );
58 let ephemeralStub = env.EPHEMERAL.get('thing');
59 const ephemeralPreventEvictionStub =
60 env.EPHEMERAL_PREVENT_EVICTION.get('thing');
61 
62 const durableRes1 = await (await durableStub.fetch('http://x')).text();
63 const durablePreventEvictionRes1 = await (
64 await durablePreventEvictionStub.fetch('http://x')
65 ).text();
66 const ephemeralRes1 = await (await ephemeralStub.fetch('http://x')).text();
67 const ephemeralPreventEvictionRes1 = await (
68 await ephemeralPreventEvictionStub.fetch('http://x')
69 ).text();
70 
71 await unsafe.abortAllDurableObjects();
72 
73 // Since the objects were aborted, trying to use their stubs now should reject.
74 await assert.rejects(() => durableStub.fetch('http://x'), {
75 name: 'Error',
76 message: 'Application called abortAllDurableObjects().',
77 });
78 await assert.rejects(() => ephemeralStub.fetch('http://x'), {
79 name: 'Error',
80 message: 'Application called abortAllDurableObjects().',
81 });
82 
83 // Recreate the stubs because they are broken.
84 durableStub = env.DURABLE.get(durableId);
85 ephemeralStub = env.EPHEMERAL.get('thing');
86 
87 const durableRes2 = await (await durableStub.fetch('http://x')).text();
88 const durablePreventEvictionRes2 = await (
89 await durablePreventEvictionStub.fetch('http://x')
90 ).text();
91 const ephemeralRes2 = await (await ephemeralStub.fetch('http://x')).text();
92 const ephemeralPreventEvictionRes2 = await (
93 await ephemeralPreventEvictionStub.fetch('http://x')
94 ).text();
95 
96 // Irrespective of abort status, verify responses start with expected prefix
97 assert.match(durableRes1, /^durable:/);
98 assert.match(durableRes2, /^durable:/);
99 assert.match(durablePreventEvictionRes1, /^durable-prevent-eviction:/);
100 assert.match(ephemeralRes1, /^ephemeral:/);
101 assert.match(ephemeralRes2, /^ephemeral:/);
102 assert.match(ephemeralPreventEvictionRes1, /^ephemeral-prevent-eviction:/);
103 
104 // Response from aborted objects should change
105 assert.notStrictEqual(durableRes1, durableRes2);
106 assert.notStrictEqual(ephemeralRes1, ephemeralRes2);
107 
108 // Response from objects in namespaces that have prevent eviction set shouldn't change
109 assert.strictEqual(durablePreventEvictionRes1, durablePreventEvictionRes2);
110 assert.strictEqual(
111 ephemeralPreventEvictionRes1,
112 ephemeralPreventEvictionRes2
113 );
114 },
115};
116 
117export const test_delete_all_durable_objects = {
118 async test(ctrl, env, ctx) {
119 // Write some data to a durable object.
120 const id = env.STORAGE.idFromName('test-delete');
121 let stub = env.STORAGE.get(id);
122 await stub.setValue('hello');
123 assert.strictEqual(await stub.getValue(), 'hello');
124 
125 // Delete all durable objects.
126 await unsafe.deleteAllDurableObjects();
127 
128 // Old stub should be broken.
129 await assert.rejects(() => stub.getValue(), {
130 name: 'Error',
131 message: 'Application called deleteAllDurableObjects().',
132 });
133 
134 // Recreate the stub โ€” storage should be gone (files were deleted from disk).
135 stub = env.STORAGE.get(id);
136 assert.strictEqual(await stub.getValue(), null);
137 },
138};
139 
140export const test_delete_all_durable_objects_alarms = {
141 async test(ctrl, env, ctx) {
142 const id = env.ALARM.newUniqueId();
143 const stub = env.ALARM.get(id);
144 
145 const alarmsBefore = alarmTriggers;
146 await stub.scheduleIn(500);
147 assert.notStrictEqual(await stub.scheduledTime, null);
148 
149 // Delete everything โ€” alarms should be cancelled and not fire.
150 await unsafe.deleteAllDurableObjects();
151 await scheduler.wait(1000);
152 assert.strictEqual(alarmTriggers, alarmsBefore); // alarm did not fire
153 },
154};
155 
156export const test_delete_all_durable_objects_respects_prevent_eviction = {
157 async test(ctrl, env, ctx) {
158 const id = env.DURABLE_PREVENT_EVICTION.newUniqueId();
159 const stub = env.DURABLE_PREVENT_EVICTION.get(id);
160 const res1 = await (await stub.fetch('http://x')).text();
161 
162 await unsafe.deleteAllDurableObjects();
163 
164 // preventEviction namespace should be untouched โ€” same response (same instance).
165 const res2 = await (await stub.fetch('http://x')).text();
166 assert.strictEqual(res1, res2);
167 },
168};