Ctrl K
ComunidadGuías…

server/boat-trial.test.ts

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

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

describe("Boat trial provisioning", () => {
  let api: Server;
  let provisionBoat: typeof import("./boat.ts").provisionBoat;
  const createBodies: Array<{ ttlSeconds: number; noEnv: boolean }> = [];
  const createKeys: string[] = [];

  beforeAll(async () => {
    api = createServer((req, res) => {
      const url = new URL(req.url ?? "/", "http://boat.test");
      let raw = "";
      req.on("data", (chunk) => (raw += chunk));
      req.on("end", () => {
        res.setHeader("content-type", "application/json");
        if (url.pathname === "/api/v1/sandboxes" && req.method === "GET") {
          return res.end(JSON.stringify({ ok: true, type: "sandbox.list", sandboxes: [] }));
        }
        if (url.pathname === "/api/v1/sandboxes" && req.method === "POST") {
          const body = JSON.parse(raw);
          createBodies.push(body);
          createKeys.push(String(req.headers["idempotency-key"] ?? ""));
          if (createBodies.length === 1) {
            res.writeHead(400);
            return res.end(JSON.stringify({
              ok: false,
              error: { code: "trial_auto_stop_required", details: { maxTtlSeconds: 7200 } },
            }));
          }
          res.writeHead(202);
          return res.end(JSON.stringify({ ok: true, type: "sandbox.created", sandbox: { id: "bx_23456789", state: "ready" } }));
        }
        if (url.pathname === "/api/v1/sandboxes/bx_23456789" && req.method === "PATCH") {
          return res.end(JSON.stringify({ ok: true }));
        }
        if (url.pathname === "/api/v1/sandboxes/bx_23456789" && req.method === "GET") {
          return res.end(JSON.stringify({ ok: true, sandbox: { id: "bx_23456789", state: "ready" } }));
        }
        if (url.pathname.endsWith("/commands")) {
          return res.end(JSON.stringify({ ok: true, exitCode: 0, stdout: "", stderr: "" }));
        }
        if (url.pathname.endsWith("/desktop")) {
          return res.end(JSON.stringify({ ok: true, desktopUrl: "https://desktop.example/vnc" }));
        }
        res.writeHead(404).end(JSON.stringify({ ok: false, message: "unexpected request" }));
      });
    });
    await new Promise<void>((resolve) => api.listen(0, "127.0.0.1", resolve));
    // SAFETY: the test server was bound as TCP above, not to a Unix socket.
    const port = (api.address() as AddressInfo).port;
    vi.stubEnv("OMB_BOAT_API", `http://127.0.0.1:${port}/api/v1`);
    vi.resetModules();
    ({ provisionBoat } = await import("./boat.ts"));
  });

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

  it("retries the structured trial TTL refusal exactly once at the allowed ceiling", async () => {
    // SAFETY: AppConfig's remaining sections are optional; this test supplies
    // the only credential the Boat path reads.
    const result = await provisionBoat({ box: { token: "box_trial" } } as any, "trial-bot", "Trial Bot");
    expect(result.boxId).toBe("bx_23456789");
    expect(createBodies).toEqual([
      { ttlSeconds: 8 * 60 * 60, noEnv: true },
      { ttlSeconds: 2 * 60 * 60, noEnv: true },
    ]);
    expect(createKeys[0]).toMatch(/^[0-9a-f-]{36}$/);
    expect(createKeys[1]).toMatch(/^[0-9a-f-]{36}$/);
    expect(createKeys[1]).not.toBe(createKeys[0]);
  });
});