Ctrl K
ComunidadGuías…

electron/control-plane-client.test.mjs

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

Archivo original ↗Preguntar a KiBot
import { runInNewContext } from "node:vm";

import { describe, expect, it, vi } from "vitest";

import {
  ControlPlaneError,
  createControlPlaneClient,
  normalizeAccountEmail,
  normalizeControlPlaneURL,
} from "./control-plane-client.mjs";

const ACCOUNT = `signed.${"a".repeat(40)}`;
const INSTALL = `omb_install_${"a".repeat(22)}.${"b".repeat(43)}`;
const INSTALL_ID = "11111111-1111-4111-8111-111111111111";
const jsonResponse = (body, init = {}) =>
  new Response(JSON.stringify(body), {
    status: init.status ?? 200,
    headers: { "content-type": "application/json", ...init.headers },
  });

describe("control-plane desktop client", () => {
  it("accepts exact HTTPS and loopback development origins only", () => {
    expect(normalizeControlPlaneURL("https://accounts.openmausbot.com/")).toBe(
      "https://accounts.openmausbot.com",
    );
    expect(normalizeControlPlaneURL("http://127.0.0.1:8787/")).toBe("http://127.0.0.1:8787");
    expect(normalizeControlPlaneURL("http://accounts.openmausbot.com")).toBe("");
    expect(normalizeControlPlaneURL("https://accounts.openmausbot.com/api")).toBe("");
    expect(normalizeControlPlaneURL("https://user:secret@accounts.openmausbot.com")).toBe("");
  });

  it("normalizes an email without accepting malformed input", () => {
    expect(normalizeAccountEmail(" Ada@Example.COM ")).toBe("ada@example.com");
    expect(normalizeAccountEmail("not-an-email")).toBe("");
    expect(normalizeAccountEmail(new String("ada@example.com"))).toBe("");
    expect(normalizeControlPlaneURL({ toString: () => "https://accounts.openmausbot.com" })).toBe("");
  });

  it("accepts plain cross-realm response records", async () => {
    const payload = runInNewContext(
      "({ user: { id: 'user-1', email: 'ada@example.com' } })",
    );
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl: vi.fn(async () => ({
        status: 200,
        ok: true,
        headers: new Headers({ "set-auth-token": ACCOUNT }),
        json: async () => payload,
      })),
    });

    await expect(client.verifyOTP("ada@example.com", "12345678")).resolves.toEqual({
      accountToken: ACCOUNT,
      user: { id: "user-1", email: "ada@example.com" },
    });
  });

  it("rejects non-plain response records instead of coercing them", async () => {
    class Payload {
      constructor() {
        this.user = { id: "user-1", email: "ada@example.com" };
      }
    }
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl: vi.fn(async () => ({
        status: 200,
        ok: true,
        headers: new Headers(),
        json: async () => new Payload(),
      })),
    });

    await expect(client.me(ACCOUNT)).rejects.toMatchObject({ code: "invalid_response" });
  });

  it("requires the exact healthy control-plane identity before onboarding", async () => {
    const timeoutSignal = vi.fn(() => new AbortController().signal);
    const fetchImpl = vi
      .fn()
      .mockResolvedValueOnce(jsonResponse({ ok: true, service: "openmausbot-control-plane" }))
      .mockResolvedValueOnce(jsonResponse({ ok: true, service: "some-other-service" }));
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl,
      timeoutSignal,
    });

    await expect(client.health()).resolves.toBe(true);
    await expect(client.health()).rejects.toMatchObject({
      code: "control_plane_unavailable",
    });
    expect(fetchImpl.mock.calls[0][0]).toBe("https://accounts.openmausbot.com/healthz");
    expect(fetchImpl.mock.calls[0][1].redirect).toBe("error");
    expect(fetchImpl.mock.calls[0][1].headers.get("origin")).toBeNull();
    expect(timeoutSignal).toHaveBeenNthCalledWith(1, 3_000);
  });

  it("uses the signed Better Auth bearer header, never its raw JSON token", async () => {
    const fetchImpl = vi.fn(async (_url, init) => {
      expect(JSON.parse(init.body)).toEqual({
        email: "ada@example.com",
        otp: "12345678",
        name: "ada",
      });
      return jsonResponse(
        { token: "raw-database-token-must-not-be-used", user: { id: "user-1", email: "ada@example.com" } },
        { headers: { "set-auth-token": ACCOUNT } },
      );
    });
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl,
    });

    await expect(client.verifyOTP("Ada@Example.com", "1234-5678")).resolves.toEqual({
      accountToken: ACCOUNT,
      user: { id: "user-1", email: "ada@example.com" },
    });
    expect(JSON.stringify(fetchImpl.mock.calls)).not.toContain("raw-database-token-must-not-be-used");
  });

  it("identifies native Better Auth mutations with the trusted control-plane origin", async () => {
    const fetchImpl = vi.fn(async (_url, init) => {
      // Undici adds Fetch Metadata after our wrapper; Better Auth 1.7 forbids a browser-shaped request without a trusted Origin.
      const wireHeaders = new Headers(init.headers);
      wireHeaders.set("sec-fetch-mode", "cors");
      if (wireHeaders.has("sec-fetch-mode") && wireHeaders.get("origin") !== "https://accounts.openmausbot.com") {
        return jsonResponse({ error: "forbidden" }, { status: 403 });
      }
      return jsonResponse({ success: true });
    });
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl,
    });

    await expect(client.requestOTP("ada@example.com")).resolves.toEqual({
      email: "ada@example.com",
    });
    expect(fetchImpl.mock.calls[0][1].headers.get("origin")).toBe(
      "https://accounts.openmausbot.com",
    );
  });

  it("keeps a valid installation credential without rotating it", async () => {
    const fetchImpl = vi.fn(async (url) => {
      expect(url).toBe("https://accounts.openmausbot.com/v1/installations/self");
      return jsonResponse({
        installation: {
          id: INSTALL_ID,
          clientInstanceId: "client-1",
          name: "Mac",
          platform: "darwin",
          appVersion: "1.0.0",
        },
        credentialExpiresAt: Date.now() + 10_000,
      });
    });
    const client = createControlPlaneClient({ baseURL: "https://accounts.openmausbot.com", fetchImpl });
    const result = await client.ensureInstallation({
      accountToken: ACCOUNT,
      currentCredential: INSTALL,
      clientInstanceId: "client-1",
      name: "Mac",
      platform: "darwin",
      appVersion: "1.0.0",
    });
    expect(result.credential).toBe(INSTALL);
    expect(fetchImpl).toHaveBeenCalledTimes(1);
  });

  it("recovers a lost installation credential by rotating the matching identity", async () => {
    const rotated = `omb_install_${"c".repeat(22)}.${"d".repeat(43)}`;
    const fetchImpl = vi.fn(async (url, init) => {
      if (url.endsWith("/v1/installations")) {
        return jsonResponse({
          installations: [{ id: INSTALL_ID, clientInstanceId: "client-1", name: "Mac", platform: "darwin" }],
        });
      }
      expect(url).toContain(`/v1/installations/${INSTALL_ID}/credentials/rotate`);
      expect(init.method).toBe("POST");
      return jsonResponse({ credential: rotated, credentialExpiresAt: Date.now() + 10_000 }, { status: 201 });
    });
    const client = createControlPlaneClient({ baseURL: "https://accounts.openmausbot.com", fetchImpl });
    await expect(client.ensureInstallation({
      accountToken: ACCOUNT,
      clientInstanceId: "client-1",
      name: "Mac",
      platform: "darwin",
      appVersion: "1.0.0",
    })).resolves.toMatchObject({ credential: rotated, installation: { id: INSTALL_ID } });
  });

  it("lists validated active installations for response-loss cleanup", async () => {
    const fetchImpl = vi.fn(async (url, init) => {
      expect(url).toBe("https://accounts.openmausbot.com/v1/installations");
      expect(init.headers.get("authorization")).toBe(`Bearer ${ACCOUNT}`);
      return jsonResponse({
        installations: [{
          id: INSTALL_ID,
          clientInstanceId: "client-1",
          name: "Mac",
          platform: "darwin",
          appVersion: "1.0.0",
        }],
      });
    });
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl,
    });

    await expect(client.listInstallations(ACCOUNT)).resolves.toEqual([{
      id: INSTALL_ID,
      clientInstanceId: "client-1",
      name: "Mac",
      platform: "darwin",
      appVersion: "1.0.0",
    }]);
  });

  it("rejects malformed installation lists instead of skipping cleanup targets", async () => {
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl: vi.fn(async () => jsonResponse({
        installations: [{ id: "not-an-installation", clientInstanceId: "client-1" }],
      })),
    });

    await expect(client.listInstallations(ACCOUNT)).rejects.toMatchObject({
      code: "invalid_response",
    });
  });

  it("validates endpoint material without leaking the connector token into the URL", async () => {
    const connectorToken = `eyJ${"x".repeat(80)}`;
    const fetchImpl = vi.fn(async (url, init) => {
      expect(url).toBe("https://accounts.openmausbot.com/v1/installations/self/endpoint");
      expect(init.headers.get("authorization")).toBe(`Bearer ${INSTALL}`);
      expect(url).not.toContain(connectorToken);
      return jsonResponse({ endpoint: { url: "https://c-opaque.openmausbot.com" }, connectorToken });
    });
    const client = createControlPlaneClient({ baseURL: "https://accounts.openmausbot.com", fetchImpl });
    await expect(client.ensureEndpoint(INSTALL)).resolves.toEqual({
      endpoint: { url: "https://c-opaque.openmausbot.com" },
      connectorToken,
    });
  });

  it("reports the app's release with an endpoint request only when it is a printable version", async () => {
    const connectorToken = `eyJ${"x".repeat(80)}`;
    const fetchImpl = vi.fn(async () =>
      jsonResponse({ endpoint: { url: "https://c-opaque.mausbot.si" }, connectorToken }),
    );
    const client = createControlPlaneClient({ baseURL: "https://accounts.openmausbot.com", fetchImpl });
    const sent = () => {
      const [, init] = fetchImpl.mock.lastCall;
      return { body: init.body, contentType: init.headers.get("content-type") };
    };

    await expect(client.ensureEndpoint(INSTALL, { appVersion: " 0.1.104 " })).resolves.toEqual({
      endpoint: { url: "https://c-opaque.mausbot.si" },
      connectorToken,
    });
    expect(sent()).toEqual({ body: JSON.stringify({ appVersion: "0.1.104" }), contentType: "application/json" });

    for (const options of [undefined, {}, { appVersion: "" }, { appVersion: "   " }, { appVersion: "0.1\n.104" },
      { appVersion: "9".repeat(65) }, { appVersion: 104 }, { appVersion: new String("0.1.104") }]) {
      await client.ensureEndpoint(INSTALL, options);
      expect(sent(), JSON.stringify(options)).toEqual({ body: undefined, contentType: null });
    }
    expect(fetchImpl).toHaveBeenCalledTimes(9);
  });

  it("says a self-hosted server is one only when told to, on registration, its address and its relay token", async () => {
    const fetchImpl = vi.fn(async (url) => {
      const path = new URL(url).pathname;
      if (path === "/v1/installations" ) return jsonResponse({ installation: { id: INSTALL_ID, clientInstanceId: "server-1", name: "vps", platform: "linux" }, credential: INSTALL }, { status: 201 });
      if (path === "/v1/installations/self/endpoint") return jsonResponse({ endpoint: { url: "https://c-opaque.mausbot.si" }, connectorToken: `eyJ${"x".repeat(80)}` });
      return jsonResponse({ token: "a.b", hostId: "h", nodes: [], expiresAt: 1, refreshAfter: 1 });
    });
    const client = createControlPlaneClient({ baseURL: "https://accounts.openmausbot.com", fetchImpl });
    const body = () => JSON.parse(fetchImpl.mock.lastCall[1].body);
    const register = (kind) => client.ensureInstallation({ accountToken: ACCOUNT, currentCredential: "", clientInstanceId: "server-1", name: "vps", platform: "linux", appVersion: "0.1.153", kind });
    fetchImpl.mockImplementationOnce(async () => jsonResponse({ installations: [] }));
    await register("server");
    expect(body()).toEqual({ clientInstanceId: "server-1", name: "vps", platform: "linux", appVersion: "0.1.153", kind: "server" });
    fetchImpl.mockImplementationOnce(async () => jsonResponse({ installations: [] }));
    await register(undefined);
    expect(body()).toEqual({ clientInstanceId: "server-1", name: "vps", platform: "linux", appVersion: "0.1.153" });
    await client.ensureEndpoint(INSTALL, { appVersion: "0.1.153", kind: "server" });
    expect(body()).toEqual({ appVersion: "0.1.153", kind: "server" });
    await client.ensureEndpoint(INSTALL, { appVersion: "0.1.153", kind: "laptop" });
    expect(body()).toEqual({ appVersion: "0.1.153" });
    await client.relayToken(INSTALL, { appVersion: "0.1.153", devices: { e2e: 1, legacy: 0 }, kind: "server" });
    expect(body()).toEqual({ appVersion: "0.1.153", devices: { e2e: 1, legacy: 0 }, kind: "server" });
    await client.relayToken(INSTALL, { appVersion: "0.1.153", devices: { e2e: 1, legacy: 0 } });
    expect(body()).toEqual({ appVersion: "0.1.153", devices: { e2e: 1, legacy: 0 } });
  });

  it("asks once more without its kind when a control plane older than the field refuses it, and only then", async () => {
    const bodies = [];
    let answers = [];
    const fetchImpl = vi.fn(async (_url, init) => {
      bodies.push(init.body === undefined ? undefined : JSON.parse(init.body));
      const next = answers.shift() ?? 200;
      return next === 200
        ? jsonResponse({ endpoint: { url: "https://c-opaque.mausbot.si" }, connectorToken: `eyJ${"x".repeat(80)}`, token: "a.b" })
        : jsonResponse({ error: next === 400 ? "invalid_request" : "rate_limited" }, { status: next });
    });
    const client = createControlPlaneClient({ baseURL: "https://accounts.openmausbot.com", fetchImpl });

    answers = [400];
    await client.ensureEndpoint(INSTALL, { appVersion: "0.1.153", kind: "server" });
    expect(bodies).toEqual([{ appVersion: "0.1.153", kind: "server" }, { appVersion: "0.1.153" }]);
    bodies.length = 0;
    answers = [400];
    await client.relayToken(INSTALL, { appVersion: "0.1.153", devices: { e2e: 1, legacy: 0 }, kind: "server" });
    expect(bodies).toEqual([{ appVersion: "0.1.153", devices: { e2e: 1, legacy: 0 }, kind: "server" }, { appVersion: "0.1.153", devices: { e2e: 1, legacy: 0 } }]);

    // Once only, only for that refusal, and never for a request that carried no kind.
    for (const [kind, refusals, calls] of [["server", [400, 400], 2], ["server", [429], 1], [undefined, [400], 1]]) {
      bodies.length = 0;
      answers = refusals;
      await expect(client.ensureEndpoint(INSTALL, { appVersion: "0.1.153", kind })).rejects.toBeInstanceOf(ControlPlaneError);
      expect(bodies, `${kind} ${refusals}`).toHaveLength(calls);
    }
  });

  it("reads why a relay-capable computer goes without a tunnel, and nothing else from the body", async () => {
    for (const [body, tunnel] of [
      [{ error: "relay_capable", tunnel: "retired" }, "retired"],
      [{ error: "relay_capable", tunnel: "full" }, "full"],
      [{ error: "relay_capable" }, null],
      [{ error: "relay_capable", tunnel: "<b>anything</b>" }, null],
      [{ error: "endpoint_capacity", tunnel: "retired" }, null],
    ]) {
      const client = createControlPlaneClient({
        baseURL: "https://accounts.openmausbot.com",
        fetchImpl: vi.fn(async () => jsonResponse(body, { status: 409 })),
      });
      await expect(client.ensureEndpoint(INSTALL, { appVersion: "0.1.152", legacyDevices: 1 })).rejects.toMatchObject({ code: body.error, tunnel });
    }
  });

  it("maps bounded server error codes and hides arbitrary response text", async () => {
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl: vi.fn(async () => jsonResponse({ error: "rate_limited", detail: "secret detail" }, { status: 429 })),
    });
    await expect(client.requestOTP("ada@example.com")).rejects.toMatchObject({
      name: "ControlPlaneError",
      code: "rate_limited",
      status: 429,
    });
  });

  it("falls back from Better Auth's message-only 429 without exposing its prose", async () => {
    const requestId = "44444444-4444-4444-8444-444444444444";
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl: vi.fn(async () => jsonResponse(
        { message: "Too many requests. Please try again later." },
        { status: 429, headers: { "x-request-id": requestId } },
      )),
    });

    await expect(client.requestOTP("ada@example.com")).rejects.toMatchObject({
      name: "ControlPlaneError",
      code: "rate_limited",
      status: 429,
      requestId,
    });
  });

  it("uses stable status errors when a response has no public error contract", async () => {
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl: vi.fn(async () => jsonResponse(
        { code: "INTERNAL_DEPENDENCY_DETAIL", message: "do not expose this" },
        { status: 400, headers: { "x-request-id": "not-a-safe-request-id" } },
      )),
    });

    await expect(client.requestOTP("ada@example.com")).rejects.toMatchObject({
      code: "invalid_request",
      status: 400,
      requestId: "",
    });
  });

  it("fails closed on redirects and network errors", async () => {
    const client = createControlPlaneClient({
      baseURL: "https://accounts.openmausbot.com",
      fetchImpl: vi.fn(async () => {
        throw new TypeError("redirect blocked");
      }),
    });
    await expect(client.requestOTP("ada@example.com")).rejects.toEqual(
      expect.objectContaining({ code: "network_unavailable" }),
    );
    expect(() => createControlPlaneClient({ baseURL: "http://remote.example" })).toThrow(
      ControlPlaneError,
    );
  });

  it("reads the endpoint without a connector token and maps a removed endpoint to null", async () => {
    const fetchImpl = vi
      .fn()
      .mockResolvedValueOnce(jsonResponse({ endpoint: null }))
      .mockResolvedValueOnce(jsonResponse({
        endpoint: { url: "https://c-opaque.openmausbot.com", hostname: "c-opaque.openmausbot.com", status: "deleting" },
      }))
      .mockResolvedValueOnce(jsonResponse({
        endpoint: { url: "https://c-opaque.openmausbot.com", status: "renamed-by-a-future-server" },
      }))
      .mockResolvedValueOnce(jsonResponse({ endpoint: { url: "http://c-opaque.openmausbot.com" } }));
    const client = createControlPlaneClient({ baseURL: "https://accounts.openmausbot.com", fetchImpl });

    await expect(client.getEndpoint(INSTALL)).resolves.toBeNull();
    await expect(client.getEndpoint(INSTALL)).resolves.toEqual({
      url: "https://c-opaque.openmausbot.com",
      status: "deleting",
    });
    await expect(client.getEndpoint(INSTALL)).resolves.toEqual({
      url: "https://c-opaque.openmausbot.com",
      status: "unknown",
    });
    await expect(client.getEndpoint(INSTALL)).rejects.toMatchObject({ code: "invalid_response" });
    const [url, init] = fetchImpl.mock.calls[0];
    expect(url).toBe("https://accounts.openmausbot.com/v1/installations/self/endpoint");
    expect(init.method).toBe("GET");
    expect(init.headers.get("authorization")).toBe(`Bearer ${INSTALL}`);
    await expect(client.getEndpoint(ACCOUNT)).rejects.toMatchObject({ code: "signed_out", status: 401 });
  });

  it("carries a capacity error code and the server's Retry-After delay", async () => {
    const fetchImpl = vi
      .fn()
      .mockResolvedValueOnce(jsonResponse({ error: "endpoint_capacity" }, {
        status: 503,
        headers: { "retry-after": "600", "x-request-id": "44444444-4444-4444-8444-444444444444" },
      }))
      .mockResolvedValueOnce(jsonResponse({ error: "endpoint_capacity" }, {
        status: 503,
        headers: { "retry-after": "Wed, 21 Oct 2026 07:28:00 GMT" },
      }));
    const client = createControlPlaneClient({ baseURL: "https://accounts.openmausbot.com", fetchImpl });

    const first = await client.ensureEndpoint(INSTALL).catch((error) => error);
    expect(first).toBeInstanceOf(ControlPlaneError);
    expect(first).toMatchObject({
      code: "endpoint_capacity",
      status: 503,
      retryAfterMs: 600_000,
      requestId: "44444444-4444-4444-8444-444444444444",
    });
    await expect(client.ensureEndpoint(INSTALL)).rejects.toMatchObject({
      code: "endpoint_capacity",
      retryAfterMs: 0,
    });
  });
});