const assert = require("node:assert/strict"); const { test } = require("node:test"); const { loadApplication } = require("./support/application.cjs"); const type = "opportunitiesExtension.requestCurrentUser"; const identity = { user: { id: "test@example.invalid", name: "Test User", roles: [] }, token: "synthetic-token" }; const flush = () => new Promise(resolve => setImmediate(resolve)); function harness({ validAfter = 0 } = {}) { let now = Date.UTC(2026, 9, 21); let sequence = 0; let backgroundCalls = 0; let tabCalls = 0; let listener; let page = { status: "loading", documentId: "first-document" }; const timers = new Map(); const tabs = []; const updates = []; const removed = []; function event() { const listeners = new Set(); return { listeners, addListener: callback => listeners.add(callback), removeListener: callback => listeners.delete(callback), emit: value => [...listeners].forEach(callback => callback(value)) }; } const headers = event(); const cookies = event(); class ClockDate extends Date { constructor(...args) { super(...(args.length ? args : [now])); } static now() { return now; } } const app = loadApplication("background", { chrome: { runtime: { onMessage: { addListener: callback => { listener = callback; } } }, webRequest: { onHeadersReceived: headers }, cookies: { onChanged: cookies } }, URL, Date: ClockDate, setTimeout: function(callback, delay) { "use strict"; assert.equal(this, undefined); const id = ++sequence; timers.set(id, { callback, at: now + delay }); return id; }, clearTimeout: id => timers.delete(id), fetch: async () => { backgroundCalls++; return new Response("SSO"); } }); app.platformBackgroundApi.tabsCreate = async details => { tabs.push(details); return { id: 91 }; }; app.platformBackgroundApi.tabsUpdate = async (id, details) => { updates.push({ id, details }); return { id }; }; app.platformBackgroundApi.tabsRemove = async id => { removed.push(id); }; app.platformBackgroundApi.tabsSendMessage = async (id, message, options) => { assert.equal(id, 91); assert.equal(options.frameId, 0); if (message.type === "opportunitiesExtension.inspectSpaPage") return page; assert.equal(message.type, "opportunitiesExtension.readSpaIdentity"); assert.equal(message.documentId, page.documentId); assert.equal(page.status, "ready"); const valid = ++tabCalls > validAfter; return { status: 200, contentType: valid ? "application/json" : "text/html", body: valid ? JSON.stringify(identity) : "SSO" }; }; app.start(); function dispatch() { return new Promise(resolve => listener({ type }, { tab: { id: 7, windowId: 3, cookieStoreId: "store-a" } }, resolve)); } async function advance(ms) { const until = now + ms; for (;;) { const next = [...timers].filter(([, timer]) => timer.at <= until).sort((a, b) => a[1].at - b[1].at)[0]; if (!next) break; now = next[1].at; timers.delete(next[0]); next[1].callback(); await flush(); } now = until; await flush(); } function navigation(overrides = {}) { headers.emit({ tabId: 91, type: "main_frame", statusCode: 200, url: "https://spa.oracle.com/oalcrm/web/api/g2m-consumer-application/ui/index.html?ojr=whitespace", ...overrides }); } function cookie() { cookies.emit({ removed: false, cookie: { name: "synthetic-session", value: "never-log-this-cookie", domain: "spa.oracle.com", storeId: "store-a", secure: true, httpOnly: true } }); } return { dispatch, advance, navigation, cookie, tabs, updates, removed, timers, cookies, headers, setPage: (status, documentId = "first-document") => { page = { status, documentId }; }, get backgroundCalls() { return backgroundCalls; }, get tabCalls() { return tabCalls; } }; } test("HTTP 200, cookies and sixty seconds do not release identity GETs on a login page", async () => { const h = harness(); const first = h.dispatch(); const second = h.dispatch(); await flush(); h.setPage("login"); h.navigation(); h.cookie(); await h.advance(60000); assert.equal(h.backgroundCalls, 1); assert.equal(h.tabCalls, 0); h.setPage("ready"); await h.advance(1500); const [result, shared] = await Promise.all([first, second]); assert.equal(result.ok, true); assert.deepEqual(result, shared); assert.equal(h.backgroundCalls, 1); assert.equal(h.tabCalls, 1, "identity is read in the verified SPA tab, not in the background"); assert.equal(h.tabs.length, 1); assert.deepEqual(h.removed, [91]); assert.equal(h.timers.size, 0); assert.equal(h.cookies.listeners.size, 0); assert.equal(h.headers.listeners.size, 1, "only the unrelated SR observer remains"); assert.equal(result.debugRequests[0].metadata.sessionWaitReason, "spa-document-verified"); assert.equal(result.debugRequests[1].metadata.executionContext, "spa-tab"); assert.doesNotMatch(JSON.stringify(result), /never-log-this-cookie/); }); test("callback 401 restarts only the owned SPA home; another HTTP 200 error page still blocks GETs", async () => { const h = harness(); const pending = h.dispatch(); await flush(); h.setPage("error"); h.navigation({ statusCode: 401, url: "https://spa.oracle.com/cloudgate/v1/oauth2/callback?code=never-replay" }); await h.advance(1000); assert.equal(h.updates.length, 1); assert.equal(h.updates[0].details.url, "https://spa.oracle.com/oalcrm/web/api/g2m-consumer-application/ui/index.html?ojr=whitespace"); assert.equal(h.updates[0].details.active, false); h.navigation(); await h.advance(10000); assert.equal(h.tabCalls, 0); assert.equal(h.updates.length, 1, "do not repeatedly restart a document already being navigated away from"); h.setPage("ready", "new-document"); await h.advance(1500); assert.equal((await pending).ok, true); assert.equal(h.tabCalls, 1); }); test("timeout fails closed with no fallback API request", async () => { const h = harness(); const pending = h.dispatch(); await flush(); h.navigation(); h.cookie(); await h.advance(120000); const result = await pending; assert.equal(result.ok, false); assert.match(result.error, /did not finish authentication/); assert.equal(result.debugRequests.length, 1); assert.equal(h.backgroundCalls, 1); assert.equal(h.tabCalls, 0); assert.deepEqual(h.removed, [91]); assert.equal(h.timers.size, 0); }); test("invalid identity requires a new verified document; the old ready document is never probed twice", async () => { const h = harness({ validAfter: 1 }); h.setPage("ready"); const pending = h.dispatch(); await flush(); await h.advance(500); assert.equal(h.tabCalls, 1); assert.equal(h.updates.length, 1); await h.advance(30000); assert.equal(h.tabCalls, 1); assert.equal(h.updates.length, 1); h.setPage("ready", "reloaded-document"); await h.advance(1500); assert.equal((await pending).ok, true); assert.equal(h.tabCalls, 2); assert.equal(h.backgroundCalls, 1); }); test("a redirect during stable-document confirmation does not trigger a GET", async () => { const h = harness(); h.setPage("ready"); const pending = h.dispatch(); await flush(); h.setPage("login", "redirect-document"); await h.advance(2000); assert.equal(h.tabCalls, 0); h.setPage("ready", "final-document"); await h.advance(2000); assert.equal((await pending).ok, true); assert.equal(h.tabCalls, 1); });