Updated the process for retrieving records from CRMServiceRequests
This commit is contained in:
@@ -1,145 +1,69 @@
|
||||
const assert = require("node:assert/strict");
|
||||
const { test } = require("node:test");
|
||||
const fs = require("node:fs");
|
||||
const vm = require("node:vm");
|
||||
const path = require("node:path");
|
||||
const factory = vm.runInNewContext(`${fs.readFileSync(path.join(__dirname, "../src/features/opportunities/internal-sr-session.js"), "utf8")}\ncreateInternalSrSession`);
|
||||
function setup(query, options = {}) {
|
||||
let elapsed = 0;
|
||||
const frames = [], removed = [], delays = [], stopped = [];
|
||||
const body = { append(frame) { frame.initialSrc = frame.src; frames.push(frame); } };
|
||||
const document = { body, createElement(tag) {
|
||||
assert.equal(tag, "iframe");
|
||||
return { contentWindow: { stop() { stopped.push(true); } }, style: {}, attributes: {}, listeners: {}, setAttribute(k, v) { this.attributes[k] = v; },
|
||||
addEventListener(k, fn) { this.listeners[k] = fn; }, remove() { removed.push(this); } };
|
||||
} };
|
||||
const api = factory({ ...options, query, environment: { document, performance: { now: () => elapsed }, setTimeout: (fn, ms) => { delays.push(ms); elapsed += ms; return setImmediate(fn); } } });
|
||||
return { api, frames, removed, delays, stopped };
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const factory = vm.runInNewContext(fs.readFileSync('src/features/opportunities/internal-sr-session.js', 'utf8') + '\ncreateInternalSrSession');
|
||||
test('without a document, session recovery stops without polling', async () => {
|
||||
for (const result of [{ok:false,httpStatus:401}, {ok:false,debugRequests:[{httpStatus:401}]}, {ok:false,error:'Request failed with status 401.'}]) {
|
||||
let calls=0;
|
||||
const service=factory({query:async()=>{calls++; return result;},environment:{document:{createElement(){assert.fail('No iframe');}},setTimeout(){assert.fail('No polling');}}});
|
||||
await assert.rejects(service.request('123'), /Internal SR|status 401/);
|
||||
await new Promise(resolve=>setImmediate(resolve));
|
||||
assert.equal(calls,1);
|
||||
}
|
||||
});
|
||||
|
||||
function harness() {
|
||||
const timers = [], listeners = new Map();
|
||||
let appended = 0, removed = 0;
|
||||
const frame = {style:{},setAttribute(){},addEventListener(k,v){listeners.set(k,v);},removeEventListener(k){listeners.delete(k);},remove(){removed++;}};
|
||||
return { timers, frame, listeners, counts:()=>({appended,removed}), environment:{
|
||||
document:{createElement:()=>frame,body:{append(){appended++;}}},
|
||||
setTimeout(fn,delay){timers.push({fn,delay});}
|
||||
}};
|
||||
}
|
||||
|
||||
test("lookup requests recover together using the configured page and preserve each result", async () => {
|
||||
let authenticated = false;
|
||||
let calls = 0;
|
||||
const h = setup(async message => {
|
||||
if (++calls <= 4) return { ok: false, httpStatus: 401 };
|
||||
authenticated = true;
|
||||
return { ok: true, items: [message.type] };
|
||||
}, { sessionUrl: "https://eeho.fa.us2.oraclecloud.com/?view=Show%20Service%20Request", sessionLabel: "Service Request" });
|
||||
const types = ["pillar", "language", "platform", "services"];
|
||||
const results = await Promise.all(types.map(type => h.api.request({ type })));
|
||||
assert.deepEqual(results, types.map(type => [type]));
|
||||
assert.equal(authenticated, true);
|
||||
assert.equal(h.frames.length, 1);
|
||||
assert.match(h.frames[0].initialSrc, /Show%20Service%20Request$/);
|
||||
assert.equal(h.frames[0].title, "Service Request session initialization");
|
||||
assert.equal(h.removed.length, 1);
|
||||
assert.equal(h.stopped.length, 1);
|
||||
const flush = () => new Promise(resolve=>setImmediate(resolve));
|
||||
test('hidden iframe remains alive after load and closes only after successful session query',async()=>{
|
||||
const h=harness(); let calls=0;
|
||||
const api=factory({environment:h.environment,query:async()=>++calls<3?{ok:false,httpStatus:401}:{ok:true,items:[{SrId:1}]}});
|
||||
const first=api.request('123'); assert.equal(first,api.request(123));
|
||||
await flush();
|
||||
assert.equal(h.counts().appended,1);
|
||||
h.listeners.get('load')?.();
|
||||
assert.equal(h.counts().removed,0);
|
||||
assert.equal(calls,1);
|
||||
assert.equal(h.timers[0].delay,2000);
|
||||
h.timers.shift().fn(); await flush();
|
||||
assert.equal(h.counts().removed,0);
|
||||
assert.equal(h.timers[0].delay,4000);
|
||||
h.timers.shift().fn();
|
||||
assert.equal((await first)[0].SrId,1);
|
||||
assert.equal(h.counts().removed,1);
|
||||
});
|
||||
test("401 initializes a hidden iframe in the calling document and removes it on success", async () => {
|
||||
let calls = 0;
|
||||
const h = setup(async () => ++calls < 3 ? { ok: false, httpStatus: 401 } : { ok: true, items: [{ SrId: 1 }] });
|
||||
const items = await h.api.request("1");
|
||||
assert.equal(items[0].SrId, 1);
|
||||
assert.equal(h.frames.length, 1);
|
||||
const frame = h.frames[0];
|
||||
assert.equal(frame.tabIndex, -1);
|
||||
assert.equal(frame.attributes["aria-hidden"], "true");
|
||||
assert.match(frame.style.cssText, /pointer-events:none/);
|
||||
assert.ok(!frame.attributes.sandbox.includes("allow-top-navigation"));
|
||||
assert.ok(frame.initialSrc.endsWith("&view=showisr"));
|
||||
assert.equal(frame.src, "about:blank");
|
||||
assert.equal(h.stopped.length, 1);
|
||||
assert.deepEqual(h.delays, [250]);
|
||||
assert.equal(h.removed[0], frame);
|
||||
});
|
||||
|
||||
test("an immediately available session stops the iframe without waiting for load or a timer", async () => {
|
||||
let calls = 0;
|
||||
const h = setup(async () => ++calls === 1 ? { ok: false, httpStatus: 401 } : { ok: true, items: [] });
|
||||
await h.api.request("1");
|
||||
assert.equal(calls, 2);
|
||||
assert.equal(h.delays.length, 0);
|
||||
assert.equal(h.stopped.length, 1);
|
||||
assert.equal(h.removed.length, 1);
|
||||
});
|
||||
|
||||
test("lookup 401 errors from older background responses still initialize the session", async () => {
|
||||
for (const failure of [
|
||||
{ ok: false, error: "requestSrLookupLanguage failed with status 401." },
|
||||
{ ok: false, debugRequests: [{ httpStatus: 401 }] },
|
||||
{ ok: false, httpStatus: "401" }
|
||||
]) {
|
||||
let calls = 0;
|
||||
const h = setup(async () => ++calls <= 2 ? failure : { ok: true, items: ["ready"] });
|
||||
assert.deepEqual(await h.api.request("language"), ["ready"]);
|
||||
assert.equal(h.frames.length, 1);
|
||||
assert.equal(h.removed.length, 1);
|
||||
test('persistent 401 is bounded to five recovery checks and removes iframe',async()=>{
|
||||
const h=harness(); let calls=0;
|
||||
const api=factory({environment:h.environment,query:async()=>{calls++;return {ok:false,httpStatus:401};}});
|
||||
const failed=assert.rejects(api.request('123'),/still unavailable/);
|
||||
for(const delay of [2000,4000,8000,12000,16000]) {
|
||||
await flush(); assert.equal(h.timers[0].delay,delay); h.timers.shift().fn();
|
||||
}
|
||||
await failed;
|
||||
assert.equal(calls,6);
|
||||
assert.equal(h.counts().removed,1);
|
||||
assert.equal(h.timers.length,0);
|
||||
});
|
||||
|
||||
test("a lookup that still returns 401 after shared recovery initializes its own session", async () => {
|
||||
const calls = new Map();
|
||||
const h = setup(async id => {
|
||||
const count = (calls.get(id) || 0) + 1;
|
||||
calls.set(id, count);
|
||||
return count <= (id === "language" ? 1 : 2)
|
||||
? { ok: false, httpStatus: 401 } : { ok: true, items: [id] };
|
||||
});
|
||||
assert.deepEqual(await Promise.all([h.api.request("language"), h.api.request("platform")]), [["language"], ["platform"]]);
|
||||
assert.equal(h.frames.length, 2);
|
||||
assert.equal(h.removed.length, 2);
|
||||
test('concurrent requests share work and explicit retry can recover', async () => {
|
||||
let calls=0, complete;
|
||||
const service=factory({query:()=>{calls++; return new Promise(resolve=>{complete=resolve;});}});
|
||||
const first=service.request('123'); const second=service.request(123);
|
||||
assert.equal(first,second); await Promise.resolve();
|
||||
complete({ok:false,httpStatus:401}); await assert.rejects(first);
|
||||
const retry=service.request('123'); await Promise.resolve(); complete({ok:true,items:[{SrId:'456'}]});
|
||||
assert.equal((await retry)[0].SrId,'456'); assert.equal(calls,2);
|
||||
});
|
||||
|
||||
test("cross-origin redirects still allow iframe removal when stop is blocked", async () => {
|
||||
let calls = 0;
|
||||
const h = setup(async () => {
|
||||
if (++calls === 1) return { ok: false, httpStatus: 401 };
|
||||
h.frames[0].contentWindow.stop = () => { throw new Error("Cross-origin"); };
|
||||
return { ok: true, items: [] };
|
||||
});
|
||||
await h.api.request("1");
|
||||
assert.equal(h.frames[0].src, "about:blank");
|
||||
assert.equal(h.removed.length, 1);
|
||||
});
|
||||
test("concurrent queries share one iframe without mixing SR results", async () => {
|
||||
let calls = 0;
|
||||
const h = setup(async id => ++calls <= 2 ? { ok: false, httpStatus: 401 } : { ok: true, items: [id] });
|
||||
const results = await Promise.all([h.api.request("1"), h.api.request("2")]);
|
||||
assert.deepEqual(results, [["1"], ["2"]]);
|
||||
assert.equal(h.frames.length, 1);
|
||||
assert.equal(h.removed.length, 1);
|
||||
});
|
||||
test("normal success does not mount an iframe; errors and timeout always clean it up", async () => {
|
||||
const success = setup(async () => ({ ok: true, items: [] }));
|
||||
await success.api.request("1");
|
||||
assert.equal(success.frames.length, 0);
|
||||
for (const status of [401, 500]) {
|
||||
let calls = 0;
|
||||
const h = setup(async () => ({ ok: false, httpStatus: ++calls === 1 ? 401 : status }));
|
||||
await assert.rejects(h.api.request("1"));
|
||||
if (status === 401) {
|
||||
assert.ok(calls > 31);
|
||||
assert.equal(h.delays.reduce((sum, ms) => sum + ms, 0), 60000);
|
||||
} else assert.equal(calls, 2);
|
||||
assert.equal(h.removed.length, 1);
|
||||
}
|
||||
});
|
||||
|
||||
test("slow session initialization can succeed after the old thirty-attempt limit", async () => {
|
||||
let calls = 0;
|
||||
const h = setup(async () => ++calls < 45 ? { ok: false, httpStatus: 401 } : { ok: true, items: ["ready"] });
|
||||
assert.deepEqual(await h.api.request("1"), ["ready"]);
|
||||
assert.equal(calls, 45);
|
||||
assert.equal(h.removed.length, 1);
|
||||
assert.ok(h.delays.reduce((sum, ms) => sum + ms, 0) < 60000);
|
||||
});
|
||||
|
||||
test("iframe load alone never confirms authentication", async () => {
|
||||
let calls = 0;
|
||||
const h = setup(async () => {
|
||||
if (++calls > 1) h.frames[0].listeners.load();
|
||||
return { ok: false, httpStatus: 401 };
|
||||
});
|
||||
await assert.rejects(h.api.request("1"), /HTTP 401.*load event, but authentication was not confirmed/);
|
||||
assert.equal(h.removed.length, 1);
|
||||
test('other errors do not retry and successful empty results are preserved', async()=>{
|
||||
const failed=factory({query:async()=>({ok:false,error:'Forbidden',httpStatus:403})});
|
||||
await assert.rejects(failed.request('123'),/Forbidden/);
|
||||
const ok=factory({query:async()=>({ok:true,items:[]})}); assert.equal((await ok.request('123')).length,0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user