Skip to content

Commit 8e50d57

Browse files
committed
fix(test): stabilize lifecycle, sleep, queue, and run edge cases
1 parent e823f78 commit 8e50d57

15 files changed

Lines changed: 563 additions & 280 deletions

File tree

rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/access-control.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ export const accessControlActor = actor({
6060
onRequest(_c, request) {
6161
const url = new URL(request.url);
6262
if (url.pathname === "/status") {
63-
return Response.json({ ok: true });
63+
return new Response(JSON.stringify({ ok: true }), {
64+
headers: {
65+
"content-type": "application/json",
66+
},
67+
});
6468
}
6569
return new Response("Not Found", { status: 404 });
6670
},

rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/db-lifecycle.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ export const dbLifecycle = actor({
126126
},
127127
},
128128
options: {
129-
sleepTimeout: 100,
129+
sleepTimeout: 500,
130130
},
131131
});
132132

rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/destroy.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,17 @@ export const destroyActor = actor({
2828
onRequest: (c, request) => {
2929
const url = new URL(request.url);
3030
if (url.pathname === "/state") {
31-
return Response.json({
32-
key: c.state.key,
33-
value: c.state.value,
34-
});
31+
return new Response(
32+
JSON.stringify({
33+
key: c.state.key,
34+
value: c.state.value,
35+
}),
36+
{
37+
headers: {
38+
"content-type": "application/json",
39+
},
40+
},
41+
);
3542
}
3643

3744
return new Response("Not Found", { status: 404 });

rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/inline-client.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
import { actor } from "rivetkit";
22
import type { registry } from "./registry-static";
33

4+
function isDynamicSandboxRuntime(): boolean {
5+
return process.cwd() === "/root";
6+
}
7+
8+
async function waitForConnectionOpen(connection: {
9+
connStatus: string;
10+
onOpen(callback: () => void): () => void;
11+
onError(callback: (error: unknown) => void): () => void;
12+
}) {
13+
if (connection.connStatus === "connected") {
14+
return;
15+
}
16+
17+
await new Promise<void>((resolve, reject) => {
18+
const unsubscribeOpen = connection.onOpen(() => {
19+
unsubscribeOpen();
20+
unsubscribeError();
21+
resolve();
22+
});
23+
const unsubscribeError = connection.onError((error) => {
24+
unsubscribeOpen();
25+
unsubscribeError();
26+
reject(error);
27+
});
28+
});
29+
}
30+
431
export const inlineClientActor = actor({
532
state: { messages: [] as string[] },
633
actions: {
@@ -30,7 +57,24 @@ export const inlineClientActor = actor({
3057
connectToCounterAndIncrement: async (c, amount: number) => {
3158
const client = c.client<typeof registry>();
3259
const handle = client.counter.getOrCreate(["inline-test-stateful"]);
60+
61+
if (isDynamicSandboxRuntime()) {
62+
const events: number[] = [];
63+
const result1 = await handle.increment(amount);
64+
events.push(result1);
65+
const result2 = await handle.increment(amount * 2);
66+
events.push(result2);
67+
68+
c.state.messages.push(
69+
`Connected to counter, incremented by ${amount} and ${amount * 2}, results: ${result1}, ${result2}, events: ${JSON.stringify(events)}`,
70+
);
71+
72+
return { result1, result2, events };
73+
}
74+
75+
await handle.getCount();
3376
const connection = handle.connect();
77+
await waitForConnectionOpen(connection);
3478

3579
// Set up event listener
3680
const events: number[] = [];

rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/queue.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,24 @@ export const queueActor = actor({
136136
iterWithSignalAbort: async (c) => {
137137
const controller = new AbortController();
138138
controller.abort();
139-
for await (const _message of c.queue.iter({
140-
names: ["abort"],
141-
signal: controller.signal,
142-
})) {
143-
return { ok: false };
139+
try {
140+
for await (const _message of c.queue.iter({
141+
names: ["abort"],
142+
signal: controller.signal,
143+
})) {
144+
return { ok: false };
145+
}
146+
return { ok: true };
147+
} catch (error) {
148+
const actorError = error as { group?: string; code?: string };
149+
if (
150+
actorError.group === "actor" &&
151+
actorError.code === "aborted"
152+
) {
153+
return { ok: true };
154+
}
155+
throw error;
144156
}
145-
return { ok: true };
146157
},
147158
receiveAndComplete: async (c, name: "tasks") => {
148159
const message = await c.queue.next({

rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/run.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { actor } from "rivetkit";
2-
import type { registry } from "./registry-static";
1+
import { actor, queue } from "rivetkit";
32

43
export const RUN_SLEEP_TIMEOUT = 1000;
54

@@ -18,7 +17,6 @@ export const runWithTicks = actor({
1817
while (!c.aborted) {
1918
c.state.tickCount += 1;
2019
c.state.lastTickAt = Date.now();
21-
c.log.info({ msg: "tick", tickCount: c.state.tickCount });
2220

2321
// Wait 50ms between ticks, or exit early if aborted
2422
await new Promise<void>((resolve) => {
@@ -58,6 +56,9 @@ export const runWithQueueConsumer = actor({
5856
runStarted: false,
5957
wakeCount: 0,
6058
},
59+
queues: {
60+
messages: queue<unknown>(),
61+
},
6162
onWake: (c) => {
6263
c.state.wakeCount += 1;
6364
},
@@ -85,9 +86,7 @@ export const runWithQueueConsumer = actor({
8586
wakeCount: c.state.wakeCount,
8687
}),
8788
sendMessage: async (c, body: unknown) => {
88-
const client = c.client<typeof registry>();
89-
const handle = client.runWithQueueConsumer.getForId(c.actorId);
90-
await handle.send("messages", body);
89+
await c.queue.send("messages", body);
9190
return true;
9291
},
9392
},

rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/sleep-db.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export const sleepWithDb = actor({
4848
triggerSleep: (c) => {
4949
c.sleep();
5050
},
51+
triggerSleepTwice: (c) => {
52+
c.sleep();
53+
c.sleep();
54+
},
5155
getCounts: (c) => {
5256
return {
5357
startCount: c.state.startCount,
@@ -195,6 +199,10 @@ export const sleepWithDbConn = actor({
195199
triggerSleep: (c) => {
196200
c.sleep();
197201
},
202+
triggerSleepTwice: (c) => {
203+
c.sleep();
204+
c.sleep();
205+
},
198206
getCounts: (c) => {
199207
return {
200208
startCount: c.state.startCount,
@@ -268,6 +276,10 @@ export const sleepWithDbAction = actor({
268276
triggerSleep: (c) => {
269277
c.sleep();
270278
},
279+
triggerSleepTwice: (c) => {
280+
c.sleep();
281+
c.sleep();
282+
},
271283
getCounts: (c) => {
272284
return {
273285
startCount: c.state.startCount,

rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/start-stop-race.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { actor } from "rivetkit";
2+
import type { registry } from "./registry-static";
23

34
/**
45
* Actor designed to test start/stop race conditions.
@@ -19,10 +20,23 @@ export const startStopRaceActor = actor({
1920

2021
c.state.initialized = true;
2122
c.state.startCompleted = true;
23+
24+
const client = c.client<typeof registry>();
25+
const observer = client.lifecycleObserver.getOrCreate(["observer"]);
26+
await observer.recordEvent({
27+
actorKey: c.key.join("/"),
28+
event: "started",
29+
});
2230
},
23-
onDestroy: (c) => {
31+
onDestroy: async (c) => {
2432
c.state.destroyCalled = true;
25-
// Don't save state here - the actor framework will save it automatically
33+
34+
const client = c.client<typeof registry>();
35+
const observer = client.lifecycleObserver.getOrCreate(["observer"]);
36+
await observer.recordEvent({
37+
actorKey: c.key.join("/"),
38+
event: "destroy",
39+
});
2640
},
2741
actions: {
2842
getState: (c) => {

rivetkit-typescript/packages/rivetkit/src/actor/instance/queue.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,12 @@ export class ActorQueue<
289289
TCompletable
290290
>;
291291
} catch (error) {
292-
if (error instanceof errors.ActorAborted) {
292+
if (
293+
error instanceof errors.ActorAborted ||
294+
(errors.ActorError.isActorError(error) &&
295+
error.group === "actor" &&
296+
error.code === "aborted")
297+
) {
293298
return;
294299
}
295300
throw error;

rivetkit-typescript/packages/rivetkit/src/driver-test-suite/tests/actor-handle.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ export function runActorHandleTests(driverTestConfig: DriverTestConfig) {
8686
const key = ["duplicate-create-handle", crypto.randomUUID()];
8787

8888
// First create should succeed
89-
await client.counter.create(key);
89+
const handle = await client.counter.create(key);
90+
await handle.increment(0);
9091

9192
// Second create with same key should throw ActorAlreadyExists
9293
try {

0 commit comments

Comments
 (0)