import assert from "node:assert/strict"; import test from "node:test"; import { createServer } from "node:http"; import { createCloudAccountClient } from "./cloud-account.mjs"; const accessToken = `omc_${"T".repeat(43)}`; const NOW = 1_800_000_000_000; const HOME = "https://omb-u-1a2b3c4d5e6f.fly.dev", CLIENT_A = "https://omb-c-a.fly.dev", TEAM = "https://omb-t-acme.fly.dev"; const CHECKOUT = "https://test.checkout.dodopayments.com/session/cks_new"; const entry = (over = {}) => ({ id: "c-a", name: "Client A", color: "green", role: "owner", tier: "pro", runsOn: "cloud", status: "ready", origin: CLIENT_A, teamId: null, seats: null, renewsAt: null, cancelAtPeriodEnd: false, primary: false, ...over }); const PRIMARY = entry({ id: "c-home", name: "My Cloud", color: null, origin: HOME, primary: true }); const paid = { plan: "pro", tier: "pro", status: "active", expiresAt: NOW + 30 * 86400_000, version: 1 }; const grant = (origin) => ({ cloudContractVersion: 1, origin, code: "ABCD-EFGH-JK23", expiresAt: NOW + 60_000 }); /** A fake Admin: the session says `f.session`, pairing answers `f.pairing(body)`, adding a Cloud answers `f.add(body)`, adding * an organization `f.organization(body)`. */ async function admin(t) { const f = { requests: [], browsers: [], timer: null, delay: null, session: { cloud: { state: "ready", origin: HOME, pairingAvailable: true } }, pairing: () => [200, grant(HOME)], add: () => [404, { error: "not_found" }], patch: () => [404, { error: "not_found" }], organization: () => [404, { error: "not_found" }], hide: () => [404, { error: "Not found." }], movedTo: () => [404, { error: "not_found" }] }; const server = createServer(async (req, res) => { let body = ""; for await (const chunk of req) body += chunk; const parsed = body ? JSON.parse(body) : undefined; f.requests.push({ route: req.url, method: req.method, body: parsed }); const send = ([status, data]) => { res.writeHead(status, { "content-type": "application/json" }); res.end(JSON.stringify(data)); }; if (req.headers.authorization !== `Bearer ${accessToken}`) return send([401, { error: "invalid_token" }]); if (req.url === "/api/cloud/desktop/session") return send([200, { cloudContractVersion: 1, expiresAt: NOW + 86400_000, device: { id: "device-1" }, account: { id: "acct-1", email: "person@example.test" }, entitlement: paid, ...f.session }]); if (req.url === "/api/cloud/desktop/pairing" && req.method === "POST") return send(f.pairing(parsed)); if (req.url === "/api/cloud/desktop/clouds" && req.method === "POST") return send(f.add(parsed)); if (req.url === "/api/cloud/desktop/organizations" && req.method === "POST") return send(f.organization(parsed)); if (req.url.startsWith("/api/cloud/desktop/clouds/") && req.method === "PATCH") return send(f.patch(decodeURIComponent(req.url.slice("/api/cloud/desktop/clouds/".length)), parsed)); if (req.url.startsWith("/api/cloud/desktop/clouds/") && req.url.endsWith("/hide") && req.method === "POST") return send(f.hide(decodeURIComponent(req.url.slice("/api/cloud/desktop/clouds/".length, -"/hide".length)), parsed)); if (req.url === "/api/cloud/desktop/moved-to" && req.method === "POST") return send(f.movedTo(parsed)); if (req.url === "/api/public/config") return send([200, { plans: [{ tier: "pro", price: { currency: "USD", interval: "month", amount: 4900 } }], trial: { days: 7 } }]); send([404, { error: "not_found" }]); }); await new Promise(resolve => server.listen(0, "127.0.0.1", resolve)); f.origin = `http://127.0.0.1:${server.address().port}`; const saved = { origin: f.origin, token: accessToken, expiresAt: NOW + 86400_000, device: { id: "device-1" }, account: { id: "acct-1", email: "person@example.test" } }; f.client = createCloudAccountClient({ origin: f.origin, fixture: true, deviceName: "Fixture computer", platform: "darwin", appVersion: "0.1.fixture", now: () => NOW, store: { read: async () => saved, write: async () => {} }, openBrowser: async url => { f.browsers.push(url); }, setTimer: (callback, delay) => { f.timer = callback; f.delay = delay; return 1; }, clearTimer: () => { f.timer = null; f.delay = null; } }); t.after(async () => { f.client.close(); server.closeAllConnections(); await new Promise(resolve => server.close(resolve)); }); return f; } test("an Admin without the list of Clouds: the session reads exactly as before, nothing to add, no other Cloud to pair", async t => { const f = await admin(t); const state = await f.client.start(); assert.equal(state.status, "connected"); for (const key of ["clouds", "addCloud", "adding"]) assert.equal(state[key], undefined, key); assert.equal(f.client.isOwnCloud(HOME), true, "My Cloud is the person's own"); assert.equal(f.client.isOwnCloud(CLIENT_A), false); await assert.rejects(f.client.pairHome("c-a"), error => error.gone === true); assert.equal(f.requests.some(row => row.route.endsWith("/pairing")), false, "never asked"); assert.deepEqual(await f.client.pairHome(), { origin: HOME, code: "ABCD-EFGH-JK23", expiresAt: NOW + 60_000 }); assert.deepEqual(f.requests.at(-1).body, {}, "My Cloud's pairing sends the same empty body an older Admin knows"); assert.deepEqual(await f.client.addCloud({ tier: "pro", name: "Client B" }), { outcome: "unsupported" }); assert.equal(f.requests.some(row => row.route.endsWith("/clouds")), false); }); test("the session's Clouds: each other own Cloud pairs with its id, and only a grant for its own address is taken", async t => { const f = await admin(t); f.session = { ...f.session, clouds: [PRIMARY, entry(), entry({ id: "c-team", name: "Acme", teamId: "team-acme", role: "member", origin: TEAM })], canAdd: true }; const state = await f.client.start(); assert.deepEqual(state.clouds.map(cloud => cloud.id), ["c-home", "c-a", "c-team"]); assert.deepEqual(state.addCloud, { canAdd: true }); assert.equal(state.latestVersion, undefined, "no newest release named: none"); f.session = { ...f.session, latestVersion: "0.1.130" }; assert.equal((await f.client.refresh()).latestVersion, "0.1.130"); f.session = { ...f.session, latestVersion: "