Ctrl K
ComunidadGuías…

server/boat-lifecycle.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { createHash } from "node:crypto";
import { createServer, type Server } from "node:http";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";

describe("cloud computer lifecycle", () => {
  let api: Server;
  let sleepBoat: typeof import("./boat.ts").sleepBoat;
  let execOnBoat: typeof import("./boat.ts").execOnBoat;
  const requests: Array<{ method: string; path: string; command?: string }> = [];
  const botId = "browser-session-test";

  beforeAll(async () => {
    const hash = createHash("sha256").update(botId).digest("hex").slice(0, 6);
    const prefix = botId.slice(0, 8).toLowerCase().replace(/[^a-z0-9]/g, "");
    const machineName = `ogb-${prefix}-${hash}`;
    api = createServer((req, res) => {
      const url = new URL(req.url ?? "/", "http://boat.test");
      let body = "";
      req.on("data", (chunk) => (body += chunk));
      req.on("end", () => {
        const parsed = body ? JSON.parse(body) : {};
        requests.push({ method: req.method ?? "GET", path: url.pathname, command: parsed.command });
        res.writeHead(200, { "content-type": "application/json" });
        if (url.pathname === "/api/v1/sandboxes") {
          res.end(JSON.stringify({ ok: true, type: "sandbox.list", sandboxes: [{ id: "bx_23456789", name: machineName, state: "ready" }] }));
        } else if (url.pathname === "/api/v1/sandboxes/bx_23456789" && req.method === "GET") {
          res.end(JSON.stringify({ ok: true, sandbox: { id: "bx_23456789", name: machineName, state: "ready" } }));
        } else if (url.pathname.endsWith("/commands")) {
          res.end(JSON.stringify({ exitCode: 0, stdout: "", stderr: "" }));
        } else {
          res.end(JSON.stringify({ ok: true }));
        }
      });
    });
    await new Promise<void>((resolve) => api.listen(0, "127.0.0.1", resolve));
    const port = (api.address() as any).port;
    vi.stubEnv("OMB_BOAT_API", `http://127.0.0.1:${port}/api/v1`);
    vi.resetModules();
    ({ execOnBoat, sleepBoat } = await import("./boat.ts"));
  });

  afterAll(async () => {
    vi.unstubAllEnvs();
    await new Promise<void>((resolve) => api.close(() => resolve()));
  });

  it("asks Chrome to exit before archiving the computer", async () => {
    await sleepBoat({ box: { token: "box_test" } } as any, botId);

    const commandIndex = requests.findIndex((request) => request.path.endsWith("/commands"));
    const stopIndex = requests.findIndex((request) => request.path.endsWith("/stop"));
    expect(commandIndex).toBeGreaterThan(-1);
    expect(stopIndex).toBeGreaterThan(commandIndex);
    expect(requests[commandIndex]?.command).toContain("kill -TERM");
    expect(requests[commandIndex]?.command).toContain("pgrep -o -x");
  });

  it("runs the owner console in the same clean environment as the bot tool", async () => {
    requests.length = 0;
    await execOnBoat({ box: { token: "box_test" } } as any, botId, `printf '%s' "$BOX_TOKEN"`);

    const command = requests.find((request) => request.path.endsWith("/commands"))?.command ?? "";
    expect(command).toContain('exec env -i HOME="$HOME"');
    expect(command).toContain("/bin/bash -c");
  });

  it("rejects an oversized owner-console command before contacting the provider", async () => {
    requests.length = 0;
    await expect(
      execOnBoat({ box: { token: "box_test" } } as any, botId, "x".repeat(4001)),
    ).rejects.toThrow("maximum 4000 characters");
    expect(requests).toHaveLength(0);
  });
});