const assert = require("node:assert/strict"); const { test } = require("node:test"); const { loadApplication } = require("./support/application.cjs"); const flush = () => new Promise(resolve => setImmediate(resolve)); function setup(family, saved = {}, principal = "user@example.invalid") { const local = { get: async key => ({ [key]: saved[key] }), set: async values => Object.assign(saved, JSON.parse(JSON.stringify(values))) }; const api = family === "firefox" ? { browser: { storage: { local } } } : { chrome: { runtime: {}, storage: { local: { get: (key, done) => local.get(key).then(done), set: (values, done) => local.set(values).then(done) } } } }; const app = loadApplication("content", { ...api, document: { createElement: () => ({ setAttribute() {}, addEventListener() {} }) } }); app.authSessionState.principal = principal; app.currentUser.allowsCrmActivities = () => true; app.preferencesState.opptyProductsCacheHours = 24; app.opportunitiesQueryState.periodRequestVersion = 1; app.activityProgress.update = () => {}; app.productsContentJobs.queueProductsTableRender = () => {}; app.debugContentLog.appendBackgroundRequestLog = () => {}; app.authContentSession.requestConsumerTokenServiceOnce = async () => ({ workloadActions: { value: "synthetic-token" } }); const calls = []; app.fusionCrmServiceRequests.requestCrmServiceRequest = async () => { calls.push("sr"); return [{ SrNumber: "SR1", ReportedHours_c: 4, OpportunityName_Id_c: { OptyNumber: "OP1" } }]; }; app.platformContentMessages.sendRuntimeMessage = async () => { calls.push("actions"); return { ok: true, actions: [{ owner: principal, name: "Test action" }], hasNext: false }; }; async function load(cachedAt, cacheOnly) { app.opportunityCrmServiceRequests.reset(); await app.opportunityCrmServiceRequests.load("synthetic-token", 1, { cachedAt, cacheOnly }); const products = { items: [{ RevnId: "123" }], cachedAt, fromCache: cacheOnly }; app.productsContentState.opptyProductsByNumber.set("OP1", products); app.opportunityWorkloadActions.loadProducts(products, true, { cachedAt, cacheOnly }); await flush(); } return { app, calls, load, saved, local }; } for (const family of ["chrome", "firefox"]) { test(`${family}: activities persist across instances with product timestamps and no credential storage`, async () => { const h = setup(family); const stamp = Date.now() - 1000; await h.load(stamp, false); assert.deepEqual(h.calls, ["sr", "actions"]); assert.equal(Object.keys(h.saved).length, 2); Object.values(h.saved).forEach(entry => assert.equal(entry.fetchedAt, stamp)); assert.doesNotMatch(JSON.stringify(h.saved), /synthetic-token|debugRequests|authorization/i); const restored = setup(family, h.saved); await restored.load(stamp, true); assert.deepEqual(restored.calls, []); assert.equal(restored.app.opportunityCrmServiceRequests.createBadge("OP1").textContent, "\u2705 SR (4h)"); assert.equal(restored.app.opportunityWorkloadActions.createBadge("OP1").textContent, "\u2705 Act"); await restored.load(stamp, false); assert.deepEqual(restored.calls, [], "valid matching cache also deduplicates repeated fresh-product notifications"); const other = setup(family, h.saved, "other@example.invalid"); await other.load(stamp, true); assert.deepEqual(other.calls, []); assert.equal(other.app.opportunityCrmServiceRequests.createBadge("OP1"), null); assert.equal(other.app.opportunityWorkloadActions.createBadge("OP1").textContent, "Act ?"); await restored.load(stamp + 1, false); assert.deepEqual(restored.calls, ["sr", "actions"]); }); } test("TTL follows product preferences, rejects stale/mismatched cache and stores empty success", async () => { const h = setup("firefox"); const stamp = Date.now() - 2 * 3600000; await h.app.activityCache.write("sr", "all", stamp, []); assert.ok(await h.app.activityCache.read("sr", "all", stamp)); h.app.preferencesState.opptyProductsCacheHours = 1; assert.equal(await h.app.activityCache.read("sr", "all", stamp), null); h.app.preferencesState.opptyProductsCacheHours = 24; assert.equal(await h.app.activityCache.read("sr", "all", stamp + 1), null); assert.equal(await h.app.activityCache.read("sr", "all", Date.now() + 100000), null); await h.load(stamp + 1, true); assert.deepEqual(h.calls, []); }); test("storage failure does not lose live activity results and failures are never cached", async () => { const h = setup("firefox"); h.local.get = async () => { throw new Error("Synthetic storage read failure"); }; h.local.set = async () => { throw new Error("Synthetic storage write failure"); }; await h.load(Date.now() - 1000, false); assert.equal(h.app.opportunityCrmServiceRequests.createBadge("OP1").textContent, "\u2705 SR (4h)"); const failed = setup("firefox"); failed.app.fusionCrmServiceRequests.requestCrmServiceRequest = async () => { throw new Error("Synthetic failure"); }; failed.app.platformContentMessages.sendRuntimeMessage = async () => ({ ok: false }); await failed.load(Date.now() - 1000, false); assert.deepEqual(failed.saved, {}); });