the universal sandbox runtime for agents and humans.
pocketenv.io
sandbox
openclaw
agent
claude-code
vercel-sandbox
deno-sandbox
cloudflare-sandbox
atproto
sprites
daytona
1import { vi, describe, it, expect, beforeEach } from "vitest";
2import consola from "consola";
3import { Sandbox } from "@pocketenv/sdk";
4import { listVolumes, createVolume, deleteVolume } from "./volume";
5
6vi.mock("../lib/sdk", () => ({ configureSdk: vi.fn() }));
7vi.mock("../lib/getAccessToken", () => ({
8 default: vi.fn().mockResolvedValue("test-access-token"),
9}));
10vi.mock("../lib/env", () => ({
11 env: { POCKETENV_TOKEN: "", POCKETENV_API_URL: "https://api.pocketenv.io" },
12}));
13vi.mock("../theme", () => ({
14 c: {
15 primary: (s: string | number) => String(s),
16 secondary: (s: string | number) => String(s),
17 },
18}));
19vi.mock("../client", () => ({
20 client: { post: vi.fn().mockResolvedValue({ data: {} }) },
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() },
27}));
28
29import { client } from "../client";
30
31describe("volume commands", () => {
32 beforeEach(() => {
33 vi.clearAllMocks();
34 });
35
36 describe("listVolumes", () => {
37 it("lists volumes and logs a table", async () => {
38 const mockSandbox = {
39 volume: {
40 list: vi.fn().mockResolvedValue({
41 volumes: [
42 {
43 id: "vol-1",
44 name: "data",
45 path: "/data",
46 createdAt: new Date().toISOString(),
47 },
48 ],
49 }),
50 },
51 };
52 vi.mocked(Sandbox.get).mockResolvedValue(mockSandbox as any);
53
54 await listVolumes("my-sandbox");
55
56 expect(mockSandbox.volume.list).toHaveBeenCalledOnce();
57 expect(consola.log).toHaveBeenCalledOnce();
58 });
59
60 it("logs an empty table when no volumes exist", async () => {
61 const mockSandbox = {
62 volume: { list: vi.fn().mockResolvedValue({ volumes: [] }) },
63 };
64 vi.mocked(Sandbox.get).mockResolvedValue(mockSandbox as any);
65
66 await listVolumes("my-sandbox");
67
68 expect(consola.log).toHaveBeenCalledOnce();
69 });
70 });
71
72 describe("createVolume", () => {
73 it("creates a volume and logs success", async () => {
74 const mockSandbox = {
75 volume: { create: vi.fn().mockResolvedValue(undefined) },
76 };
77 vi.mocked(Sandbox.get).mockResolvedValue(mockSandbox as any);
78
79 await createVolume("my-sandbox", "data", "/data");
80
81 expect(mockSandbox.volume.create).toHaveBeenCalledWith("data", {
82 path: "/data",
83 });
84 expect(consola.success).toHaveBeenCalledWith(
85 expect.stringContaining("data"),
86 );
87 });
88
89 it("logs error when volume creation fails", async () => {
90 const mockSandbox = {
91 volume: {
92 create: vi.fn().mockRejectedValue(new Error("Create failed")),
93 },
94 };
95 vi.mocked(Sandbox.get).mockResolvedValue(mockSandbox as any);
96
97 await createVolume("my-sandbox", "data", "/data");
98
99 expect(consola.error).toHaveBeenCalledWith(
100 "Failed to create volume:",
101 expect.any(Error),
102 );
103 });
104 });
105
106 describe("deleteVolume", () => {
107 it("deletes a volume via API and logs success", async () => {
108 await deleteVolume("vol-1");
109
110 expect(client.post).toHaveBeenCalledWith(
111 "/xrpc/io.pocketenv.volume.deleteVolume",
112 undefined,
113 expect.objectContaining({ params: { id: "vol-1" } }),
114 );
115 expect(consola.success).toHaveBeenCalledWith(
116 expect.stringContaining("vol-1"),
117 );
118 });
119
120 it("logs error when deletion fails", async () => {
121 vi.mocked(client.post).mockRejectedValue(new Error("API error"));
122
123 await deleteVolume("vol-1");
124
125 expect(consola.error).toHaveBeenCalledWith(
126 expect.stringContaining("Failed to delete volume"),
127 );
128 });
129 });
130});