the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 163 lines 5.2 kB view raw
1import { vi, describe, it, expect, beforeEach } from "vitest"; 2import consola from "consola"; 3import { Sandbox } from "@pocketenv/sdk"; 4import createSandbox from "./create"; 5 6vi.mock("../lib/sdk", () => ({ configureSdk: vi.fn() })); 7vi.mock("../lib/expandRepo", () => ({ 8 expandRepo: vi.fn((repo: string) => `https://github.com/${repo}`), 9})); 10vi.mock("../lib/sodium", () => ({ 11 default: vi.fn().mockResolvedValue("encrypted-token"), 12})); 13vi.mock("../lib/redact", () => ({ 14 default: vi.fn((val: string) => val), 15})); 16vi.mock("./ssh", () => ({ default: vi.fn().mockResolvedValue(undefined) })); 17vi.mock("../theme", () => ({ 18 c: { 19 primary: (s: string | number) => String(s), 20 }, 21})); 22vi.mock("consola", () => ({ 23 default: { log: vi.fn(), success: vi.fn(), error: vi.fn() }, 24})); 25vi.mock("@pocketenv/sdk", () => ({ 26 Sandbox: { get: vi.fn(), configure: vi.fn(), create: vi.fn() }, 27})); 28 29describe("createSandbox", () => { 30 const mockExit = vi 31 .spyOn(process, "exit") 32 .mockImplementation(() => undefined as never); 33 34 beforeEach(() => { 35 vi.clearAllMocks(); 36 delete process.env.SPRITE_TOKEN; 37 delete process.env.DAYTONA_API_KEY; 38 delete process.env.DAYTONA_ORGANIZATION_ID; 39 delete process.env.DENO_DEPLOY_TOKEN; 40 delete process.env.VERCEL_API_TOKEN; 41 delete process.env.VERCEL_PROJECT_ID; 42 delete process.env.VERCEL_TEAM_ID; 43 delete process.env.MODAL_TOKEN_ID; 44 delete process.env.MODAL_TOKEN_SECRET; 45 }); 46 47 it("creates a sandbox with default provider and logs success", async () => { 48 vi.mocked(Sandbox.create).mockResolvedValue({ 49 data: { name: "my-sandbox" }, 50 } as any); 51 52 await createSandbox("my-sandbox", {}); 53 54 expect(Sandbox.create).toHaveBeenCalledWith( 55 expect.objectContaining({ name: "my-sandbox" }), 56 ); 57 expect(consola.success).toHaveBeenCalledWith( 58 expect.stringContaining("my-sandbox"), 59 ); 60 }); 61 62 it("exits with error for unsupported provider", async () => { 63 await createSandbox("my-sandbox", { provider: "unsupported" }); 64 65 expect(consola.error).toHaveBeenCalledWith( 66 expect.stringContaining("Unsupported provider"), 67 ); 68 expect(mockExit).toHaveBeenCalledWith(1); 69 }); 70 71 it("exits with error when SPRITE_TOKEN is missing for sprites provider", async () => { 72 await createSandbox("my-sandbox", { provider: "sprites" }); 73 74 expect(consola.error).toHaveBeenCalledWith( 75 expect.stringContaining("SPRITE_TOKEN"), 76 ); 77 expect(mockExit).toHaveBeenCalledWith(1); 78 }); 79 80 it("creates sandbox with sprites provider when token is set", async () => { 81 process.env.SPRITE_TOKEN = "test-sprite-token"; 82 vi.mocked(Sandbox.create).mockResolvedValue({ 83 data: { name: "my-sandbox" }, 84 } as any); 85 86 await createSandbox("my-sandbox", { provider: "sprites" }); 87 88 expect(Sandbox.create).toHaveBeenCalledWith( 89 expect.objectContaining({ 90 provider: "sprites", 91 providerOptions: expect.objectContaining({ 92 spriteToken: "encrypted-token", 93 }), 94 }), 95 ); 96 expect(consola.success).toHaveBeenCalledOnce(); 97 }); 98 99 it("exits with error when DAYTONA vars are missing", async () => { 100 await createSandbox("my-sandbox", { provider: "daytona" }); 101 102 expect(consola.error).toHaveBeenCalledWith( 103 expect.stringContaining("DAYTONA_API_KEY"), 104 ); 105 expect(mockExit).toHaveBeenCalledWith(1); 106 }); 107 108 it("exits with error when DENO_DEPLOY_TOKEN is missing", async () => { 109 await createSandbox("my-sandbox", { provider: "deno" }); 110 111 expect(consola.error).toHaveBeenCalledWith( 112 expect.stringContaining("DENO_DEPLOY_TOKEN"), 113 ); 114 expect(mockExit).toHaveBeenCalledWith(1); 115 }); 116 117 it("exits with error when VERCEL vars are missing", async () => { 118 await createSandbox("my-sandbox", { provider: "vercel" }); 119 120 expect(consola.error).toHaveBeenCalledWith( 121 expect.stringContaining("VERCEL_API_TOKEN"), 122 ); 123 expect(mockExit).toHaveBeenCalledWith(1); 124 }); 125 126 it("expands repo shorthand when provided", async () => { 127 const { expandRepo } = await import("../lib/expandRepo"); 128 vi.mocked(Sandbox.create).mockResolvedValue({ 129 data: { name: "my-sandbox" }, 130 } as any); 131 132 await createSandbox("my-sandbox", { repo: "owner/repo" }); 133 134 expect(expandRepo).toHaveBeenCalledWith("owner/repo"); 135 expect(Sandbox.create).toHaveBeenCalledWith( 136 expect.objectContaining({ 137 repo: "https://github.com/owner/repo", 138 }), 139 ); 140 }); 141 142 it("connects via SSH after creation when ssh option is set", async () => { 143 const connectToSandbox = (await import("./ssh")).default; 144 vi.mocked(Sandbox.create).mockResolvedValue({ 145 data: { name: "my-sandbox" }, 146 waitUntilRunning: vi.fn().mockResolvedValue(undefined), 147 } as any); 148 149 await createSandbox("my-sandbox", { ssh: true }); 150 151 expect(connectToSandbox).toHaveBeenCalledWith("my-sandbox"); 152 }); 153 154 it("logs error when sandbox creation throws", async () => { 155 vi.mocked(Sandbox.create).mockRejectedValue(new Error("API error")); 156 157 await createSandbox("my-sandbox", {}); 158 159 expect(consola.error).toHaveBeenCalledWith( 160 expect.stringContaining("Failed to create sandbox"), 161 ); 162 }); 163});