Ctrl K
ComunidadGuías…

electron/lend-own-server.node-test.mjs

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

Archivo original ↗Preguntar a KiBot
// A server of the person's own (this desktop paired there as its admin) gets this computer once the person allows it, with
// no maintainer flag: the popup after pairing and Settings' switch (server-lending.mjs), what is lent, Not now, Stop,
// Pause, a new pairing, and the pairings that never count as their own.
import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import { mkdtemp, readFile, realpath, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { test } from "node:test";
import { setTimeout as delay } from "node:timers/promises";

import { createComputerSharing, ownServerSession } from "./computer-sharing.mjs";
import { createLendingIndicator } from "./lending-indicator.mjs";
import { installServerLending } from "./server-lending.mjs";

const GARAGE = "http://192.168.1.20:8799";
const garage = { id: "garage", name: "Garage", origin: GARAGE, kind: "remote" };

const until = async (check, what, ms = 5000) => {
  const deadline = Date.now() + ms;
  while (Date.now() < deadline) { if (await check()) return; await delay(10); }
  assert.fail(`timed out waiting for ${what}`);
};

async function scratch(t) {
  const dir = await realpath(await mkdtemp(path.join(tmpdir(), "omb-lend-own-server-")));
  t.after(() => rm(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 200 }));
  return dir;
}

/** A self-hosted server as this desktop's sign-in there sees it: `scopes`, whether nobody else signs in there (`own`, which
 * the server says only to its one owner, with what it takes from them), and whether it is a team's or a Cloud. Its public
 * description offers no lending, as before pairing. */
function selfHosted({ scopes = ["admin", "client"], own = true, extra = {}, mcp = true } = {}) {
  const json = (value, status = 200) => ({ ok: status < 400, status, headers: { get: () => null }, body: (async function* () { yield Buffer.from(JSON.stringify(value)); })() });
  const state = { environmentId: randomUUID(), sessionId: randomUUID(), scopes, own, extra, connects: [], disconnects: [], polls: 0 };
  state.fetch = async (url, init) => {
    const route = new URL(url).pathname;
    const owner = state.own && state.scopes.includes("admin");
    if (route === "/api/auth/session") return json({ kind: "session", id: state.sessionId, scopes: state.scopes, ...(owner ? { own: true, lending: { sharedComputers: true, lendingDetails: true, fileEdits: true, ...(mcp ? { lentMcp: true } : {}) } } : {}), ...state.extra });
    if (route === "/.well-known/openmausbot/environment") return json({ environmentId: state.environmentId, capabilities: { remoteSessions: true } });
    const body = init?.body ? JSON.parse(init.body) : {};
    if (route === "/api/shared-computers/connect") { state.connects.push(body); return json({ ok: true }); }
    if (route.endsWith("/poll")) {
      state.polls++;
      await delay(50, undefined, { signal: init?.signal }).catch(() => {});
      init?.signal?.throwIfAborted();
      return json({ job: null });
    }
    if (route.endsWith("/disconnect")) { state.disconnects.push(body); return json({ ok: true }); }
    return json({ ok: true });
  };
  return state;
}

/** This desktop with the maintainer flag off, lending as main wires it; `restart()` is the app opening again. */
async function desktop(t, server, { ownClouds = () => [] } = {}) {
  const dir = await scratch(t);
  const file = path.join(dir, "profile", "computer-sharing.json");
  const summaries = [];
  const make = () => createComputerSharing({ file, environments: () => [garage], enabled: async () => false, home: path.join(dir, "home"), cuaConnection: async () => null,
    fetch: (url, init) => server.fetch(url, init), ownClouds, localEnvironments: () => [server.environmentId], onChange: summary => summaries.push(summary) });
  let sharing = make();
  t.after(() => sharing.close());
  return { file, summaries, get sharing() { return sharing; }, restart: () => { sharing.close(); sharing = make(); sharing.start({ maintainer: false }); return sharing; } };
}

test("only this desktop's admin pairing on a server that is no Cloud, team or hosted workspace counts as the person's own", () => {
  const owner = { kind: "session", id: randomUUID(), scopes: ["admin", "client"], own: true };
  assert.equal(ownServerSession(owner), true);
  assert.equal(ownServerSession({ ...owner, own: undefined }), false, "a server others sign in to");
  assert.equal(ownServerSession({ ...owner, scopes: ["client"] }), false, "a narrower pairing");
  assert.equal(ownServerSession({ ...owner, scopes: undefined }), false);
  assert.equal(ownServerSession({ ...owner, team: { id: "t", role: "admin" } }), false, "a team's server");
  assert.equal(ownServerSession({ ...owner, cloudHome: true }), false, "a Cloud is lent by its own rules");
  assert.equal(ownServerSession({ ...owner, hosted: true }), false);
  assert.equal(ownServerSession({ ...owner, kind: "loopback" }), false);
});

test("once the person allows it, a server of their own gets the apps on the screen and its MCP servers, with the maintainer flag off; no folder or terminal unless chosen", async t => {
  const server = selfHosted();
  const mac = await desktop(t, server);
  const info = await mac.sharing.ownOffer(garage);
  assert.deepEqual(info, { sessionId: server.sessionId, environmentId: server.environmentId });
  assert.equal(server.connects.length, 0, "asking lends nothing");
  const lent = await mac.sharing.lendOwn(garage, { folders: [], terminal: false, computer: true });
  assert.equal(lent.own, true);
  assert.equal(lent.enabled, true);
  await until(() => server.connects.length === 1 && mac.sharing.state(garage.id).connected, "the server to take this computer");
  const [connect] = server.connects;
  assert.deepEqual({ computer: connect.computer, terminal: connect.terminal, folders: connect.folders, mcpServers: connect.mcpServers, environments: connect.environments, fileEdits: connect.fileEdits },
    { computer: true, terminal: false, folders: [], mcpServers: true, environments: [server.environmentId], fileEdits: true });
  assert.equal("internet" in connect || "chrome" in connect, false, "the device link is a Cloud's alone");
  assert.equal(await mac.sharing.ownOffer(garage), null, "answered for this pairing");
  assert.deepEqual(mac.summaries.at(-1).lending, [garage.id]);
  assert.deepEqual(mac.summaries.at(-1).clouds, ["Garage"], "the menu-bar item names it like a Cloud");
  assert.equal(mac.summaries.at(-1).terminalSwitch, false, "the Clouds' terminal switch stays out of the menu without a Cloud");
});

test("a narrower pairing, a server others sign in to, a team's server or a Cloud is never asked about and is never lent", async t => {
  for (const [what, server] of [["client scope", selfHosted({ scopes: ["client"] })], ["others sign in there", selfHosted({ own: false })], ["team", selfHosted({ extra: { team: { id: "t", role: "admin" } } })], ["Cloud", selfHosted({ extra: { cloudHome: true } })]]) {
    const mac = await desktop(t, server);
    assert.equal(await mac.sharing.ownServer(garage), null, what);
    assert.equal(await mac.sharing.ownOffer(garage), false, what);
    await assert.rejects(mac.sharing.lendOwn(garage, { computer: true }), /Only your own server can use this computer/, what);
    await delay(30);
    assert.equal(server.connects.length, 0, what);
    assert.equal(mac.sharing.state(garage.id).enabled, false, what);
  }
});

test("Not now lends nothing and this pairing is not asked again; pairing again asks again", async t => {
  const server = selfHosted();
  const mac = await desktop(t, server);
  const info = await mac.sharing.ownOffer(garage);
  mac.sharing.declineOwn(garage, info);
  assert.equal(await mac.sharing.ownOffer(garage), null);
  mac.restart();
  await delay(50);
  assert.equal(server.connects.length, 0);
  assert.equal(mac.sharing.state(garage.id).enabled, false);
  server.sessionId = randomUUID();
  assert.deepEqual(await mac.sharing.ownOffer(garage), { sessionId: server.sessionId, environmentId: server.environmentId });
});

test("Stop ends it at once: the server is told, polling stops, and it stays off after the app opens again", async t => {
  const server = selfHosted();
  const mac = await desktop(t, server);
  await mac.sharing.lendOwn(garage, { computer: true });
  await until(() => server.polls > 0, "the first poll");
  const stopped = mac.sharing.revoke(garage);
  assert.equal(stopped.enabled, false);
  await until(() => server.disconnects.length === 1, "the server to be told");
  const polls = server.polls;
  await delay(150);
  assert.equal(server.polls, polls, "nothing polls after Stop");
  mac.restart();
  await delay(80);
  assert.equal(server.connects.length, 1, "Stop lasts across restarts");
  assert.equal(await mac.sharing.ownOffer(garage), null, "Stop is an answer too");
});

test("a new pairing of the same server keeps lending; that server getting other people, or another server behind the address, ends it", async t => {
  const server = selfHosted();
  const mac = await desktop(t, server);
  await mac.sharing.lendOwn(garage, { computer: true });
  await until(() => server.connects.length === 1, "lending to start");
  server.sessionId = randomUUID();
  mac.restart();
  await until(() => server.connects.length === 2, "lending to go on after pairing again");
  assert.equal(JSON.parse(await readFile(mac.file, "utf8")).records.garage.sessionId, server.sessionId, "bound to the new pairing");

  server.own = false;
  mac.restart();
  await until(() => mac.sharing.state(garage.id).problem === "not-own", "lending to end");
  assert.equal(mac.sharing.state(garage.id).enabled, false);
  assert.equal(server.connects.length, 2, "nothing is lent to a narrower pairing");

  const other = selfHosted();
  const moved = await desktop(t, other);
  await moved.sharing.lendOwn(garage, { computer: true });
  await until(() => other.connects.length === 1, "lending to start");
  other.environmentId = randomUUID();
  moved.restart();
  await until(() => moved.sharing.state(garage.id).problem === "machine-changed", "lending to another server to end");
  assert.equal(other.connects.length, 1);
});

test("Pause holds a server of the person's own as it holds Clouds, across restarts, and Resume lends again", async t => {
  const server = selfHosted();
  const mac = await desktop(t, server);
  await mac.sharing.lendOwn(garage, { computer: true });
  await until(() => server.connects.length === 1, "lending to start");
  mac.sharing.pause();
  assert.equal(mac.sharing.state(garage.id).problem, "paused", "Pause is instant, as for a Cloud");
  await until(() => server.disconnects.length === 1, "the server to be told");
  assert.equal(mac.sharing.state(garage.id).paused, true);
  assert.deepEqual({ paused: mac.summaries.at(-1).paused, clouds: mac.summaries.at(-1).clouds, lending: mac.summaries.at(-1).lending }, { paused: true, clouds: ["Garage"], lending: [] });
  mac.restart();
  await delay(80);
  assert.equal(server.connects.length, 1, "still paused after the app opens again");
  assert.equal(server.disconnects.length, 1, "nothing even dials it while paused");
  await mac.sharing.resume();
  await until(() => server.connects.length === 2, "Resume to lend again");
});

test("the menu-bar item keeps its Clouds' terminal switch only while a Cloud is signed in here", () => {
  const menus = [];
  const tray = createLendingIndicator({ Tray: class { setToolTip() {} setTitle() {} setContextMenu(menu) { menus.push(menu); } on() {} isDestroyed() { return false; } destroy() {} },
    Menu: { buildFromTemplate: items => items }, nativeImage: { createFromPath: () => ({ resize: () => ({}) }) }, iconPath: "", onPause() {}, onResume() {}, onActivity() {}, onTerminal() {}, device: "Mac" });
  const labels = () => menus.at(-1).map(item => item.label);
  tray.update({ clouds: ["Garage"], terminalSwitch: false });
  assert.deepEqual(labels(), ["Garage can use this Mac", undefined, "Pause", "See activity"]);
  tray.update({ clouds: ["My Cloud", "Garage"], terminalSwitch: true });
  assert.ok(labels().includes("Let Clouds run commands on this Mac"));
});

/** main's channels and popup, with this desktop's lending and native dialogs stood in for. */
function mainLending({ response = 1, own = true, flag = false, offer } = {}) {
  const handlers = new Map(), calls = { lent: [], declined: [], saved: [], revoked: [], boxes: [] };
  const win = { isDestroyed: () => false, webContents: { getURL: () => `${GARAGE}/` } };
  const info = { sessionId: randomUUID(), environmentId: randomUUID() };
  const records = {};
  const controller = {
    state: id => records[id] ?? { enabled: false, folders: [], terminal: false, computer: false },
    ownServer: async () => (own ? info : null),
    ownOffer: async () => (offer !== undefined ? offer : own ? info : false),
    lendOwn: async (env, grant) => { calls.lent.push([env.id, grant]); records[env.id] = { enabled: true, own: true, ...grant }; return records[env.id]; },
    declineOwn: (env, seen) => { calls.declined.push([env.id, seen]); },
    revoke: env => { calls.revoked.push(env.id); records[env.id] = { ...records[env.id], enabled: false }; return records[env.id]; },
    identity: async () => info, save: async (env, grant) => { calls.saved.push([env.id, grant]); return {}; }, observe: async () => info, decline: () => {}, activity: () => [],
  };
  const dialog = { showMessageBox: async (...args) => { calls.boxes.push(args.at(-1)); return { response }; } };
  const lending = installServerLending({ ipcMain: { handle: (channel, handler) => handlers.set(channel, handler) }, guard: (_channel, handler) => handler, sharing: () => controller,
    dialogs: { chooseFolder: async () => null, confirmServerAccess: async () => true }, validate: async folders => folders ?? [], dialog, window: () => win,
    saved: id => { if (id !== garage.id) throw new Error("unknown"); return garage; }, candidate: env => !env.cloud && !env.team && !env.home, flag: async () => flag,
    environments: () => [garage, { id: "acme", name: "Acme", origin: "https://omb-t-acme.fly.dev", team: { id: "t" } }], active: () => garage });
  return { handlers, calls, lending, win, info };
}

test("right after pairing a server of the person's own, one popup in main's own dialog: Allow lends the apps on the screen, Not now nothing", async () => {
  const allowed = mainLending({ response: 0 });
  await allowed.lending.offer(allowed.win);
  assert.equal(allowed.calls.boxes.length, 1);
  assert.deepEqual(allowed.calls.boxes[0], { type: "question", message: "Let Garage use this computer?", detail: allowed.calls.boxes[0].detail, buttons: ["Allow", "Not now"], defaultId: 1, cancelId: 1 });
  assert.match(allowed.calls.boxes[0].detail, /apps on your screen.*folders you choose and run commands in a terminal/s);
  assert.deepEqual(allowed.calls.lent, [["garage", { folders: [], terminal: false, computer: true }]]);

  const notNow = mainLending({ response: 1 });
  await notNow.lending.offer(notNow.win);
  assert.deepEqual(notNow.calls.lent, []);
  assert.deepEqual(notNow.calls.declined, [["garage", notNow.info]]);

  const answered = mainLending({ response: 0, offer: null });
  await answered.lending.offer(answered.win);
  assert.deepEqual(answered.calls.boxes, [], "a pairing already answered is never asked again");

  const notOwn = mainLending({ response: 0, own: false });
  await notOwn.lending.offer(notOwn.win);
  assert.deepEqual(notOwn.calls.boxes, [], "another server is asked about only while the maintainer flag is on");
  assert.deepEqual(notOwn.calls.lent, []);
});

test("Settings: each server of the person's own has the switch, which asks the same popup; turning it off needs no flag", async () => {
  const { handlers, calls } = mainLending({ response: 0 });
  assert.deepEqual(await handlers.get("sharing:servers")({}), [{ id: "garage", enabled: false, connected: false, paused: false }], "never the team's server");
  assert.equal((await handlers.get("sharing:state")({}, "garage")).own, true, "its state reads without the maintainer flag");
  const lent = await handlers.get("sharing:allow")({}, "garage");
  assert.equal(lent.enabled, true);
  assert.equal(calls.boxes[0].message, "Let Garage use this computer?");
  assert.deepEqual(calls.lent, [["garage", { folders: [], terminal: false, computer: true }]]);
  await handlers.get("sharing:save")({}, "garage", { folders: [], terminal: true, computer: true });
  assert.deepEqual(calls.lent.at(-1), ["garage", { folders: [], terminal: true, computer: true }], "a change lends as theirs, never through the maintainer path");
  assert.deepEqual(calls.saved, []);
  await handlers.get("sharing:revoke")({}, "garage");
  assert.deepEqual(calls.revoked, ["garage"]);

  const declined = mainLending({ response: 1 });
  assert.equal(await declined.handlers.get("sharing:allow")({}, "garage"), null);
  assert.deepEqual(declined.calls.lent, []);

  const other = mainLending({ own: false });
  await assert.rejects(other.handlers.get("sharing:allow")({}, "garage"), /Only your own server/);
  await assert.rejects(other.handlers.get("sharing:state")({}, "garage"), /turned off on this server/, "another server still needs the maintainer flag");
  assert.deepEqual(await other.handlers.get("sharing:servers")({}), []);
  await other.handlers.get("sharing:revoke")({}, "garage");
  assert.deepEqual(other.calls.revoked, ["garage"], "stopping never needs a flag");
});