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 { listSecrets, putSecret, deleteSecret } from "./secret";
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 highlight: (s: string | number) => String(s),
18 },
19}));
20vi.mock("../client", () => ({
21 client: { post: vi.fn().mockResolvedValue({ data: {} }) },
22}));
23vi.mock("consola", () => ({
24 default: { log: vi.fn(), success: vi.fn(), error: vi.fn() },
25}));
26vi.mock("@pocketenv/sdk", () => ({
27 Sandbox: { get: vi.fn(), configure: vi.fn() },
28}));
29vi.mock("@inquirer/prompts", () => ({
30 password: vi.fn().mockResolvedValue("super-secret-value"),
31}));
32
33import { client } from "../client";
34import { password } from "@inquirer/prompts";
35
36describe("secret commands", () => {
37 beforeEach(() => {
38 vi.clearAllMocks();
39 });
40
41 describe("listSecrets", () => {
42 it("lists secrets and logs a table", async () => {
43 const mockSandbox = {
44 secret: {
45 list: vi.fn().mockResolvedValue({
46 secrets: [
47 {
48 id: "sec-1",
49 name: "API_KEY",
50 createdAt: new Date().toISOString(),
51 },
52 ],
53 }),
54 },
55 };
56 vi.mocked(Sandbox.get).mockResolvedValue(mockSandbox as any);
57
58 await listSecrets("my-sandbox");
59
60 expect(mockSandbox.secret.list).toHaveBeenCalledWith({
61 limit: 100,
62 offset: 0,
63 });
64 expect(consola.log).toHaveBeenCalledOnce();
65 });
66
67 it("logs an empty table when no secrets exist", async () => {
68 const mockSandbox = {
69 secret: { list: vi.fn().mockResolvedValue({ secrets: [] }) },
70 };
71 vi.mocked(Sandbox.get).mockResolvedValue(mockSandbox as any);
72
73 await listSecrets("my-sandbox");
74
75 expect(consola.log).toHaveBeenCalledOnce();
76 });
77 });
78
79 describe("putSecret", () => {
80 beforeEach(() => {
81 Object.defineProperty(process.stdin, "isTTY", {
82 value: true,
83 configurable: true,
84 });
85 });
86
87 it("prompts for value and saves secret", async () => {
88 const mockSandbox = {
89 secret: { put: vi.fn().mockResolvedValue(undefined) },
90 data: { name: "my-sandbox" },
91 };
92 vi.mocked(Sandbox.get).mockResolvedValue(mockSandbox as any);
93
94 await putSecret("my-sandbox", "API_KEY");
95
96 expect(password).toHaveBeenCalledWith({
97 message: "Enter secret value",
98 });
99 expect(mockSandbox.secret.put).toHaveBeenCalledWith(
100 "API_KEY",
101 "super-secret-value",
102 );
103 expect(consola.success).toHaveBeenCalledWith("Secret added successfully");
104 });
105
106 it("logs error when sandbox not found", async () => {
107 vi.mocked(Sandbox.get).mockRejectedValue(new Error("Not found"));
108
109 await putSecret("my-sandbox", "API_KEY");
110
111 expect(consola.error).toHaveBeenCalledWith(
112 "Failed to add secret:",
113 expect.any(Error),
114 );
115 });
116 });
117
118 describe("deleteSecret", () => {
119 it("deletes a secret via API", async () => {
120 await deleteSecret("sec-1");
121
122 expect(client.post).toHaveBeenCalledWith(
123 "/xrpc/io.pocketenv.secret.deleteSecret",
124 undefined,
125 expect.objectContaining({ params: { id: "sec-1" } }),
126 );
127 expect(consola.success).toHaveBeenCalledWith("Secret deleted successfully");
128 });
129
130 it("logs error when deletion fails", async () => {
131 vi.mocked(client.post).mockRejectedValue(new Error("API error"));
132
133 await deleteSecret("sec-1");
134
135 expect(consola.error).toHaveBeenCalledWith(
136 "Failed to delete secret:",
137 expect.any(Error),
138 );
139 });
140 });
141});