Skip to content
File

Blob: tests/worker/dispatch/shared/sandbox-errors.test.ts

typescript102 lines
1import { describe, expect, it, vi } from "vitest";
2 
3import { deleteSandboxSessionIfExists, disposeRpcStub, getOrCreateSandboxSession } from "@/worker/dispatch/shared";
4 
5const createSessionAlreadyExistsError = (): Error & { code: string } =>
6 Object.assign(new Error("session exists"), {
7 code: "SESSION_ALREADY_EXISTS",
8 name: "SessionAlreadyExistsError",
9 });
10 
11const createSessionDestroyedError = (): Error & { code: string } =>
12 Object.assign(new Error("session destroyed"), {
13 code: "SESSION_DESTROYED",
14 name: "SessionDestroyedError",
15 });
16 
17describe("sandbox process helpers", () => {
18 it("creates a named session and falls back to getSession when it already exists", async () => {
19 const existingSession = { id: "run-session" };
20 const runtime = {
21 sandbox: {
22 createSession: vi.fn().mockRejectedValueOnce(createSessionAlreadyExistsError()),
23 },
24 getSession: vi.fn(async () => existingSession as never),
25 };
26 
27 await expect(
28 getOrCreateSandboxSession(runtime, {
29 id: "run-session",
30 cwd: "/workspace/repo",
31 }),
32 ).resolves.toBe(existingSession);
33 expect(runtime.sandbox.createSession).toHaveBeenCalledWith({
34 id: "run-session",
35 cwd: "/workspace/repo",
36 });
37 expect(runtime.getSession).toHaveBeenCalledWith("run-session");
38 });
39 
40 it("falls back to getSession when the runtime only exposes a prefixed message", async () => {
41 const existingSession = { id: "run-session" };
42 const runtime = {
43 sandbox: {
44 createSession: vi
45 .fn()
46 .mockRejectedValueOnce(new Error("SessionAlreadyExistsError: Session 'run-session' already exists")),
47 },
48 getSession: vi.fn(async () => existingSession as never),
49 };
50 
51 await expect(
52 getOrCreateSandboxSession(runtime, {
53 id: "run-session",
54 cwd: "/workspace/repo",
55 }),
56 ).resolves.toBe(existingSession);
57 expect(runtime.getSession).toHaveBeenCalledWith("run-session");
58 });
59 
60 it("treats deleting an already-destroyed session as success", async () => {
61 const sandbox = {
62 deleteSession: vi.fn(async () => {
63 throw createSessionDestroyedError();
64 }),
65 };
66 
67 await expect(deleteSandboxSessionIfExists(sandbox as never, "run-session")).resolves.toBeUndefined();
68 });
69 
70 it("treats a prefixed destroyed-session message as success", async () => {
71 const sandbox = {
72 deleteSession: vi.fn(async () => {
73 throw new Error("SessionDestroyedError: Session 'run-session' was destroyed");
74 }),
75 };
76 
77 await expect(deleteSandboxSessionIfExists(sandbox as never, "run-session")).resolves.toBeUndefined();
78 });
79 
80 it("treats a missing running container as success when deleting a session", async () => {
81 const sandbox = {
82 deleteSession: vi.fn(async () => {
83 throw new Error("Error: The container is not running, consider calling start()");
84 }),
85 };
86 
87 await expect(deleteSandboxSessionIfExists(sandbox as never, "run-session")).resolves.toBeUndefined();
88 });
89 
90 it("swallows async dispose rejections from proxy-backed stubs", async () => {
91 const flush = vi.fn(async () => {});
92 disposeRpcStub({
93 dispose: async () => {
94 throw new Error("dispose failed");
95 },
96 });
97 
98 await flush();
99 expect(flush).toHaveBeenCalledTimes(1);
100 });
101});