import { chromium } from "../../web/node_modules/playwright/index.mjs"; import fs from "node:fs"; import https from "node:https"; import assert from "node:assert/strict"; const browser = await chromium.launch({ executablePath: process.env.CHROMIUM_PATH, headless: true, args: ["--host-resolver-rules=MAP *.tngl.boltless.dev 127.0.0.1:8443"], }); const context = await browser.newContext({ ignoreHTTPSErrors: true }); const page = await context.newPage(); const fixture = JSON.parse(fs.readFileSync("/tmp/web-org-fixture.json")); let popups = 0, account, createToken, wrongMethodChecked = false; page.on("popup", () => popups++); page.on("request", (r) => { if (new URL(r.url()).pathname === "/xrpc/sh.tangled.delegation.createAccount") createToken = r.headers().authorization; }); page.on("response", async (r) => { if ( new URL(r.url()).pathname === "/xrpc/sh.tangled.delegation.createAccount" ) { account = await r.json(); assert.equal(r.status(), 200); console.log("PASS gate created organization"); } }); await page.route("**/oauth/delegation/auth-token", async (route) => { const body = route.request().postData(); const wrong = await new Promise((resolve, reject) => { const req = https.request( { hostname: "127.0.0.1", port: 8443, path: "/oauth/delegation/auth-token", method: "POST", rejectUnauthorized: false, headers: { host: "tranquil.tngl.boltless.dev", "content-type": "application/json", authorization: createToken, }, }, (res) => { let s = ""; res.on("data", (d) => (s += d)); res.on("end", () => { try { resolve(JSON.parse(s)); } catch (e) { reject(e); } }); }, ); req.on("error", reject); req.end(body); }); assert.equal(wrong.error, "AuthorizationError"); wrongMethodChecked = true; console.log("PASS wrong-method service auth rejected before binding"); await route.continue(); }); try { await page.goto("https://web.tngl.boltless.dev/login", { waitUntil: "networkidle" }); await page.getByLabel("Handle", { exact: true }).fill(fixture.handle); await page.getByRole("button", { name: "Login", exact: true }).click(); await page.locator("input[type=password]").fill("Gate-Smoke9!"); await page.getByRole("button", { name: "Sign in", exact: true }).click(); await page.getByRole("button", { name: "Authorize", exact: true }).click(); await page.waitForURL("https://web.tngl.boltless.dev/**", { timeout: 30000 }); await page.waitForFunction( (did) => localStorage.getItem("tangled.currentDid") === did, fixture.did, ); console.log("PASS Bluesky PDS OAuth login"); await page.goto("https://web.tngl.boltless.dev/org/new"); await page .getByLabel("Organization handle", { exact: true }) .fill(fixture.org); await page .getByRole("button", { name: "Create organization", exact: true }) .click(); await page.waitForURL("**/" + fixture.org + ".tranquil.tngl.boltless.dev", { timeout: 45000, }); assert.equal(popups, 0); assert.equal(wrongMethodChecked, true); assert.equal(account.controllerDid, fixture.did); assert.equal( await page.evaluate(() => localStorage.getItem("tangled.currentDid")), fixture.did, ); const response = await page.evaluate(async (did) => { const r = await fetch( "https://tranquil.tngl.boltless.dev/xrpc/com.atproto.repo.getRecord?repo=" + did + "&collection=sh.tangled.actor.profile&rkey=self", ); return r.json(); }, account.did); assert.equal(response.value.isOrganization, true); console.log( "PASS headless OAuth consent, empty org profile write, and navigation with zero popups", ); console.log("PASS controller remains active account"); const checks = await page.evaluate( async ({ did }) => { const source = await (await fetch("/src/lib/api/actAs.ts")).text(); const modulePath = source.match( /from ["']([^"']*atcute_oauth-browser-client[^"']*)["']/, )[1]; const { getSession, OAuthUserAgent } = await import(modulePath); const api = await import("/src/lib/api/tranquil.ts"); const orgAgent = new OAuthUserAgent(await getSession(did)); const controllers = await api.listControllers(orgAgent); const presets = await api.getScopePresets(orgAgent); const resolved = await api.resolveController( orgAgent, controllers[0].did, ); const audit = await api.getAuditLog(orgAgent); const second = await api.resolveController( orgAgent, "bob.pds.tngl.boltless.dev", ); const editor = presets.find((p) => p.name === "editor").scopes; await api.addController(orgAgent, { controllerDid: second.did, grantedScopes: editor, }); const added = await api.listControllers(orgAgent); await api.updateControllerScopes(orgAgent, { controllerDid: second.did, grantedScopes: presets.find((p) => p.name === "viewer").scopes, }); const updated = await api.listControllers(orgAgent); await api.removeController(orgAgent, second.did); const removed = await api.listControllers(orgAgent); return { controllers, presets, resolved, audit, second, editor, added, updated, removed, }; }, { did: account.did, handle: fixture.handle }, ); assert.equal(checks.controllers.length, 1); assert.equal(checks.controllers[0].did, fixture.did); assert.equal(checks.controllers[0].isLocal, false); assert.equal(checks.controllers[0].isActive, true); assert.equal(checks.controllers[0].handle, fixture.handle); assert.equal( checks.controllers[0].grantedScopes, checks.presets.find((p) => p.name === "owner").scopes, ); assert.equal(checks.resolved.did, fixture.did); assert.equal(checks.resolved.isLocal, false); assert.ok(checks.audit.entries.length > 0); assert.equal(checks.added.length, 2); assert.equal( checks.added.find((c) => c.did === checks.second.did).grantedScopes, checks.editor, ); assert.equal( checks.updated.find((c) => c.did === checks.second.did).grantedScopes, checks.presets.find((p) => p.name === "viewer").scopes, ); assert.deepEqual( checks.removed.map((c) => c.did), [fixture.did], ); console.log( "PASS Tranquil controller listing, remote identity resolution, owner scopes, and audit log", ); console.log( "PASS add controller, update scopes, remove controller and list after each change", ); fs.writeFileSync("/tmp/headless-org-result.json", JSON.stringify(account)); } catch (e) { console.log("PAGE", (await page.locator("body").innerText()).slice(-2500)); throw e; } finally { await browser.close(); }