392 lines
25 KiB
JavaScript
392 lines
25 KiB
JavaScript
const assert = require("node:assert/strict");
|
|
const { test } = require("node:test");
|
|
const { loadBackground, response } = require("./support/characterization.cjs");
|
|
const { loadApplication } = require("./support/application.cjs");
|
|
function assertDebugVisibility(result, secret) {
|
|
const app = loadApplication("content");
|
|
const entries = result.debugRequests || [];
|
|
const render = () => entries.map(entry => app.debugContentLog.formatHeaderBlock(entry.requestHeaders)
|
|
+ app.debugContentLog.formatDebugBody(entry.responseBody, entry.name)).join("\n");
|
|
assert.ok(!render().includes(secret));
|
|
app.debugState.debugSecretsVisible = true;
|
|
assert.equal(render().includes(secret), JSON.stringify(entries).includes(secret));
|
|
}
|
|
const type = "opportunitiesExtension.requestPostServiceRequest";
|
|
const emailAddress = "signed.in+sr@example.invalid";
|
|
const internalType = "opportunitiesExtension.requestPostInternalServiceRequest";
|
|
const internalInput = () => ({ Title: "Internal request title", ServiceNew_Id_c: "404", ProblemDescription: "Form description",
|
|
CustomerDeadline_c: "2026-11-29", TechPlatformLookUp_c: "DATABASE", InternalDeadline_c: "2027-01-31",
|
|
ServiceRequest_Id_SRToInternalSR: "505" });
|
|
const parentInfo = () => ({ SrId: 505, SrNumber: "SR505", AccountPartyId: 100000693906507, OpportunityName_c: "A8YPN8",
|
|
OpportunityName_Id_c: 302959906052470, RegionText_c: "LAD", SvcHealth_c: "SVC_HEALTH_YELLOW", Language_c: "PORTUGUESE",
|
|
CountryText_c: "BR", PrimaryPillarNew_c: "Tech_Cloud_Platform", SecondaryPillarNew_c: "Tech_Cloud_Infrastructure", ServiceNew_Id_c: 300010753841648 });
|
|
|
|
for (const family of ["chrome", "firefox"]) {
|
|
test(`${family}: internal SR reads parent SRInfo, resolves user and posts form values with Session and observed client ID`, async () => {
|
|
const calls = [];
|
|
const h = loadBackground({ family, fetch: async (url, options) => {
|
|
calls.push({ url, options });
|
|
if (options.method === "GET" && url.includes("/serviceRequests?")) return withClientId({ items: [parentInfo()] }, 200, "internal-client");
|
|
if (url.includes("/resourceUsers?")) return response({ resourcePartyId: 303 });
|
|
assert.ok(!url.includes("Catalogue_c"), "Internal SR must not use catalog signing");
|
|
return response({ SrNumber: "ISR1" }, 201);
|
|
} });
|
|
const input = { ...internalInput(), AccountPartyId: 1, OpportunityName_c: "STALE", OpportunityName_Id_c: 2, RegionText_c: "APAC",
|
|
SvcHealth_c: "SVC_HEALTH_GREEN", Language_c: "ENGLISH", CountryText_c: "US", PrimaryPillarNew_c: "STALE", SecondaryPillarNew_c: null, ReportedByPartyId: 999 };
|
|
const result = await h.dispatch({ type: internalType, emailAddress, payload: input });
|
|
assert.equal(result.ok, true);
|
|
assert.equal(calls.length, 3);
|
|
const info = new URL(calls[0].url);
|
|
assert.match(info.pathname, /SeaaS_SR_management.*\/oalsetCRMRestAPI\/.*resources\/latest\/serviceRequests$/);
|
|
assert.equal(info.searchParams.get("q"), "SrId=505");
|
|
assert.equal(info.searchParams.get("onlyData"), "true");
|
|
assert.equal(info.searchParams.get("orderBy"), "LastUpdateDate:desc");
|
|
assert.equal(info.searchParams.get("fields"), "PrimaryPillarNew_c,SecondaryPillarNew_c,SvcHealth_c,SrNumber,AccountPartyId,CountryText_c,Language_c,OpportunityName_Id_c,OpportunityName_c,RegionText_c,ServiceNew_Id_c");
|
|
const post = calls[2];
|
|
assert.equal(new URL(calls[1].url).searchParams.get("emailAddress"), emailAddress);
|
|
assert.match(post.url, /SeaaS_SR_management.*oalsetCRMRestAPICloudAccount.*resources\/latest\/serviceRequests$/);
|
|
assert.equal(post.options.method, "POST");
|
|
const { SrId, SrNumber, ...parent } = parentInfo();
|
|
assert.deepEqual(JSON.parse(post.options.body), { ...parent, ...internalInput(),
|
|
ServiceRequest_Id_SRToInternalSR: "505", ServiceNew_Id_c: 404,
|
|
InternalDeadline_c: "2027-01-31T00:00:00.000Z", ReportedByPartyId: 303, SvcQuestionsCollection_c: [] });
|
|
assert.ok(calls.every(call => call.options.headers.Authorization === "Session" && call.options.credentials === "include"));
|
|
const origin = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com";
|
|
assert.deepEqual({ ...post.options.headers }, { Accept: "application/json", "Content-Type": "application/json",
|
|
Authorization: "Session", Origin: origin, "x-appbuilder-client-id": "internal-client" });
|
|
assertDebugVisibility(result, "internal-client");
|
|
assert.equal(calls[0].options.headers.Origin, undefined);
|
|
assert.equal(result.debugRequests[2].requestHeaders.Origin, origin);
|
|
assert.equal(h.headerRules.length, 1);
|
|
const { removeRuleIds, addRules } = h.headerRules[0];
|
|
assert.deepEqual(removeRuleIds, [7203]);
|
|
assert.equal(addRules.length, 1);
|
|
const rule = addRules[0];
|
|
assert.deepEqual(rule.action, { type: "modifyHeaders", requestHeaders: [{ header: "Origin", operation: "set", value: origin }] });
|
|
assert.deepEqual(rule.condition.initiatorDomains, ["synthetic-extension"]);
|
|
assert.deepEqual(rule.condition.requestMethods, ["post"]);
|
|
assert.deepEqual(rule.condition.resourceTypes, ["xmlhttprequest"]);
|
|
const pattern = new RegExp(rule.condition.regexFilter);
|
|
assert.ok(pattern.test(post.url));
|
|
assert.ok(!pattern.test(calls[0].url));
|
|
assert.ok(!pattern.test(post.url + "/other"));
|
|
assert.ok(!pattern.test(post.url + "?other=1"));
|
|
assert.deepEqual(result.debugRequests.map(entry => entry.name), ["requestSRInfo", "requestResourceUser", "requestPostInternalServiceRequest"]);
|
|
});
|
|
}
|
|
|
|
test("internal SR queries the current parent on every Save and reuses captured client ID after worker restart", async () => {
|
|
const sessionStorage = {};
|
|
const queries = [], posts = [];
|
|
const fetch = async (url, options) => {
|
|
if (url.includes("/serviceRequests?")) {
|
|
const id = Number(new URL(url).searchParams.get("q").slice("SrId=".length));
|
|
queries.push(id);
|
|
const parent = { ...parentInfo(), SrId: id, SecondaryPillarNew_c: null, ServiceNew_Id_c: id + 1000 };
|
|
if (id === 606) delete parent.ServiceNew_Id_c;
|
|
return id === 505 ? withClientId({ items: [parent] }, 200, "saved-internal-client") : response({ items: [parent] });
|
|
}
|
|
if (url.includes("/resourceUsers?")) return response({ resourcePartyId: 303 });
|
|
posts.push(options);
|
|
return response({ SrNumber: "ISR1" }, 201);
|
|
};
|
|
const h = loadBackground({ fetch, sessionStorage });
|
|
assert.equal((await h.dispatch({ type: internalType, emailAddress, payload: internalInput() })).ok, true);
|
|
const restarted = loadBackground({ fetch, sessionStorage });
|
|
assert.equal((await restarted.dispatch({ type: internalType, emailAddress,
|
|
payload: { ...internalInput(), ServiceRequest_Id_SRToInternalSR: "606" } })).ok, true);
|
|
assert.deepEqual(queries, [505, 606]);
|
|
assert.equal(posts[1].headers["x-appbuilder-client-id"], "saved-internal-client");
|
|
assert.equal(JSON.parse(posts[1].body).ServiceRequest_Id_SRToInternalSR, "606", "links to parent SrId, independently of the parent's service ID");
|
|
assert.equal(JSON.parse(posts[1].body).SecondaryPillarNew_c, null);
|
|
});
|
|
|
|
test("internal SR save validates form fields and parent ID before requesting SRInfo", async () => {
|
|
let calls = 0;
|
|
const h = loadBackground({ fetch: async () => { calls++; return response({}, 403); } });
|
|
for (const change of [{ Title: "" }, { ProblemDescription: "" }, { TechPlatformLookUp_c: "" }, { InternalDeadline_c: "2026-02-30" },
|
|
{ CustomerDeadline_c: "2026-02-30" }, { ServiceNew_Id_c: "bad" }, { ServiceRequest_Id_SRToInternalSR: "bad" }]) {
|
|
assert.equal((await h.dispatch({ type: internalType, emailAddress, payload: { ...internalInput(), ...change } })).ok, false);
|
|
}
|
|
assert.equal(calls, 0);
|
|
assert.equal((await h.dispatch({ type: internalType, emailAddress, payload: internalInput() })).ok, false);
|
|
assert.equal(calls, 1);
|
|
});
|
|
|
|
test("internal SR stops after missing or rejected parent data, failed user lookup or POST and never retries creation", async () => {
|
|
for (const mode of ["info-401", "info-500", "empty", "multiple", "wrong-parent", "missing-country", "user", "post-403", "post-500", "network"]) {
|
|
const calls = [];
|
|
const h = loadBackground({ fetch: async (url, options) => {
|
|
calls.push({ url, options });
|
|
if (url.includes("/serviceRequests?")) {
|
|
const parent = parentInfo();
|
|
if (mode === "wrong-parent") parent.SrId = 999;
|
|
if (mode === "missing-country") delete parent.CountryText_c;
|
|
return response({ items: mode === "empty" ? [] : mode === "multiple" ? [parent, parent] : [parent] }, mode === "info-401" ? 401 : mode === "info-500" ? 500 : 200);
|
|
}
|
|
if (url.includes("/resourceUsers?")) return response({ resourcePartyId: 303 }, mode === "user" ? 403 : 200);
|
|
if (mode === "network") throw new Error("Synthetic network failure");
|
|
return response({}, mode === "post-403" ? 403 : 500);
|
|
} });
|
|
const result = await h.dispatch({ type: internalType, emailAddress, payload: internalInput() });
|
|
assert.equal(result.ok, false, mode);
|
|
const hasPost = ["post-403", "post-500", "network"].includes(mode);
|
|
assert.equal(calls.length, hasPost ? 3 : mode === "user" ? 2 : 1, mode);
|
|
assert.equal(calls.filter(call => call.options.method === "POST").length, hasPost ? 1 : 0);
|
|
assert.equal(result.debugRequests.at(-1).status, ["empty", "multiple", "wrong-parent", "missing-country"].includes(mode) ? "success" : "failed");
|
|
}
|
|
});
|
|
const payload = () => ({ AccountPartyId: 100000000000101, OpportunityName_Id_c: 202,
|
|
ServiceNew_Id_c: 404, Title: "Service-OP1-Customer", RegionText_c: "LAD", CountryText_c: "BR",
|
|
ProblemDescription: "Test description", Language_c: "ENGLISH", CustomerDeadline_c: "2027-01-31",
|
|
TechPlatformLookUp_c: "NO_ALL_PLATFORM", PrimaryPillarNew_c: "Tech_Cloud_Infrastructure" });
|
|
const withClientId = (body, status, value) => {
|
|
const result = response(body, status);
|
|
result.headers.set("X-AppBuilder-Client-Id", value);
|
|
return result;
|
|
};
|
|
|
|
for (const family of ["chrome", "firefox"]) {
|
|
test(`${family}: response client ID from resource lookup appears in confirmation and token request`, async () => {
|
|
const calls = [];
|
|
const h = loadBackground({ family, fetch: async (url, options) => {
|
|
calls.push({ url, options });
|
|
if (url.includes("/resourceUsers?")) return withClientId({ resourcePartyId: 303 }, 200, "response-client");
|
|
if (url.endsWith("/Catalogue_c/505")) return response({ result: "test-token" });
|
|
return response({ SrNumber: "SR1" }, 201);
|
|
} });
|
|
const prepared = await h.dispatch({ type, phase: "prepare", emailAddress, payload: payload() });
|
|
assert.equal(prepared.ok, true);
|
|
assert.equal(prepared.headers.requestSrTokenCatalog["x-appbuilder-client-id"], "response-client");
|
|
assert.deepEqual(prepared.headers.requestPostServiceRequest, { ...prepared.headers.requestSrTokenCatalog,
|
|
"vb-proxy-header-x-oracle-ectoken": "(generated after confirmation)" });
|
|
assert.equal(calls.length, 1);
|
|
const result = await h.dispatch({ type, phase: "confirm", catalogId: 505, payload: prepared.payload });
|
|
assert.equal(result.ok, true);
|
|
assert.equal(calls[1].options.headers["x-appbuilder-client-id"], "response-client");
|
|
assert.deepEqual({ ...calls[2].options.headers }, { ...calls[1].options.headers, "vb-proxy-header-x-oracle-ectoken": "test-token" });
|
|
assert.equal(calls.filter(call => call.url.endsWith("/mgmt/serviceRequests")).length, 1);
|
|
});
|
|
}
|
|
|
|
test("response observation includes background responses and excludes other apps and private requests", async () => {
|
|
const h = loadBackground();
|
|
assert.equal(h.responseHeaderListeners.length, 1);
|
|
const capture = h.responseHeaderListeners[0];
|
|
assert.deepEqual(capture.options, ["responseHeaders"]);
|
|
const details = { tabId: -1,
|
|
url: "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live;profile=PROD/services/auth/1.1/proxy/example",
|
|
responseHeaders: [{ name: "X-AppBuilder-Client-ID", value: "background-response-client" }, { name: "Set-Cookie", value: "not-retained" }] };
|
|
capture.listener({ ...details, incognito: true });
|
|
capture.listener({ ...details, url: details.url.replace("SeaaS_initiation", "unrelated") });
|
|
assert.deepEqual(h.sessionStorage, {});
|
|
capture.listener(details);
|
|
assert.equal(Object.values(h.sessionStorage)[0].value, "background-response-client");
|
|
assert.ok(!JSON.stringify(h.sessionStorage).includes("not-retained"));
|
|
});
|
|
|
|
test("token 403 retries once only with a newly returned client ID; SR POST is never retried", async () => {
|
|
for (const mode of ["recovered", "still-forbidden", "unchanged", "no-header", "save-failure"]) {
|
|
const calls = [];
|
|
let tokens = 0;
|
|
const h = loadBackground({ fetch: async (url, options) => {
|
|
calls.push({ url, options });
|
|
if (url.includes("/resourceUsers?")) return withClientId({ resourcePartyId: 303 }, 200, "old-client");
|
|
if (url.endsWith("/Catalogue_c/505")) {
|
|
tokens += 1;
|
|
if (tokens === 1) return mode === "no-header" ? response({}, 403)
|
|
: withClientId({}, 403, mode === "unchanged" ? "old-client" : "new-client");
|
|
if (mode === "still-forbidden") return withClientId({}, 403, "another-client");
|
|
return response({ result: "retry-token" });
|
|
}
|
|
return response({ SrNumber: "SR1" }, mode === "save-failure" ? 403 : 201);
|
|
} });
|
|
const prepared = await h.dispatch({ type, phase: "prepare", emailAddress, payload: payload() });
|
|
const result = await h.dispatch({ type, phase: "confirm", catalogId: 505, payload: prepared.payload });
|
|
assert.equal(result.ok, mode === "recovered");
|
|
const expectedTokens = ["unchanged", "no-header"].includes(mode) ? 1 : 2;
|
|
assert.equal(tokens, expectedTokens);
|
|
const tokenCalls = calls.filter(call => call.url.endsWith("/Catalogue_c/505"));
|
|
assert.equal(tokenCalls[0].options.headers["x-appbuilder-client-id"], "old-client");
|
|
if (expectedTokens === 2) assert.equal(tokenCalls[1].options.headers["x-appbuilder-client-id"], "new-client");
|
|
assert.equal(calls.filter(call => call.url.endsWith("/mgmt/serviceRequests")).length, ["recovered", "save-failure"].includes(mode) ? 1 : 0);
|
|
if (["recovered", "save-failure"].includes(mode)) {
|
|
assert.deepEqual({ ...calls.at(-1).options.headers }, { ...tokenCalls.at(-1).options.headers, "vb-proxy-header-x-oracle-ectoken": "retry-token" });
|
|
}
|
|
assertDebugVisibility(result, "retry-token");
|
|
}
|
|
});
|
|
|
|
for (const family of ["chrome", "firefox"]) {
|
|
test(`${family}: Save resolves current user, discovers catalog, obtains token and creates SR once with protected diagnostics`, async () => {
|
|
const calls = [];
|
|
const h = loadBackground({ family, fetch: async (url, options) => {
|
|
calls.push({ url, options });
|
|
if (calls.length === 1) return response({ emailAddress, resourceEmail: emailAddress, resourcePartyId: 303, resourceProfileId: 999 });
|
|
if (calls.length === 2) return response({ items: [{ Id: 505 }] });
|
|
if (calls.length === 3) return response({ result: "synthetic-secret-token" });
|
|
return response({ SrId: 606, SrNumber: "SR606" }, 201);
|
|
} });
|
|
const prepared = await h.dispatch({ type, phase: "prepare", emailAddress, payload: { ...payload(), ReportedByPartyId: 999 } });
|
|
assert.equal(prepared.ok, true);
|
|
assert.equal(calls.length, 1, "preview only resolves the user, never generates a token or creates SR");
|
|
assert.equal(h.headerRules.length, 0, "preview does not install request modifications");
|
|
assert.equal(prepared.headers.requestSrTokenCatalog["Content-Type"], "application/vnd.oracle.adf.action+json");
|
|
assert.equal(prepared.headers.requestPostServiceRequest["vb-proxy-header-x-oracle-ectoken"], "(generated after confirmation)");
|
|
assert.equal(prepared.payload.ReportedByPartyId, 303);
|
|
const result = await h.dispatch({ type, phase: "confirm", payload: prepared.payload });
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.result.SrNumber, "SR606");
|
|
assert.equal(calls.length, 4);
|
|
assert.equal(calls[0].options.method, "GET");
|
|
assert.equal(new URL(calls[0].url).searchParams.get("emailAddress"), emailAddress);
|
|
assert.ok(calls[0].url.includes("/set/seaas/crm/resourceUsers?"));
|
|
assert.equal(calls[1].options.method, "GET");
|
|
assert.ok(calls[2].url.endsWith("/Catalogue_c/505"));
|
|
assert.deepEqual(JSON.parse(calls[2].options.body), { name: "getJwtToken" });
|
|
assert.equal(calls[2].options.headers["Content-Type"], "application/vnd.oracle.adf.action+json");
|
|
assert.equal(calls[2].options.headers.Origin, new URL(calls[2].url).origin);
|
|
assert.deepEqual({ ...calls[3].options.headers }, { ...calls[2].options.headers, "vb-proxy-header-x-oracle-ectoken": "synthetic-secret-token" });
|
|
assert.equal(h.headerRules.length, 1);
|
|
const rule = h.headerRules[0].addRules[0];
|
|
const serviceRule = h.headerRules[0].addRules[1];
|
|
assert.ok(new RegExp(serviceRule.condition.regexFilter).test(calls[3].url));
|
|
assert.ok(!new RegExp(serviceRule.condition.regexFilter).test(calls[2].url));
|
|
assert.ok(!new RegExp(serviceRule.condition.regexFilter).test(calls[3].url + "/other"));
|
|
assert.deepEqual(serviceRule.condition.initiatorDomains, ["synthetic-extension"]);
|
|
assert.deepEqual(serviceRule.condition.requestMethods, ["post"]);
|
|
assert.deepEqual(serviceRule.action, rule.action);
|
|
assert.deepEqual(rule.condition.initiatorDomains, ["synthetic-extension"]);
|
|
assert.deepEqual(rule.condition.requestMethods, ["post"]);
|
|
assert.ok(new RegExp(rule.condition.regexFilter).test(calls[2].url));
|
|
assert.ok(!new RegExp(rule.condition.regexFilter).test(calls[3].url));
|
|
assert.ok(!new RegExp(rule.condition.regexFilter).test(calls[2].url + "/other"));
|
|
assert.deepEqual(rule.action.requestHeaders, [{ header: "Origin", operation: "set", value: new URL(calls[2].url).origin }]);
|
|
assert.equal(calls[3].options.method, "POST");
|
|
assert.ok(calls[3].url.endsWith("/set/seaas/mgmt/serviceRequests"));
|
|
assert.equal(calls[3].options.headers["vb-proxy-header-x-oracle-ectoken"], "synthetic-secret-token");
|
|
assert.ok(calls.slice(0, 3).every(call => !call.options.headers["vb-proxy-header-x-oracle-ectoken"]));
|
|
assert.ok(calls.every(call => call.options.credentials === "include" && call.options.headers.Authorization === "Session"));
|
|
assert.deepEqual(JSON.parse(calls[3].options.body), { ...payload(), ReportedByPartyId: 303, SvcQuestionsCollection_c: [],
|
|
SourceCd: "SVC_OPPORTUNITY_SCREEN", SeverityCd: "ORA_SVC_SEV2", ChannelTypeCd: "ORA_SVC_WEB" });
|
|
assert.equal(calls[3].options.body, JSON.stringify(prepared.payload));
|
|
assertDebugVisibility(result, "synthetic-secret-token");
|
|
assert.equal(h.tabs.length, 0);
|
|
});
|
|
}
|
|
|
|
test("missing region, invalid dates or unsafe IDs stop before token acquisition", async () => {
|
|
const h = loadBackground({ fetch: async () => assert.fail("Unexpected network") });
|
|
for (const change of [{ RegionText_c: null }, { CustomerDeadline_c: "2027-02-30" },
|
|
{ AccountPartyId: "9007199254740993" }, { OpportunityName_Id_c: "bad/id" }]) {
|
|
assert.equal((await h.dispatch({ type, phase: "prepare", emailAddress, payload: { ...payload(), ...change } })).ok, false);
|
|
}
|
|
assert.equal((await h.dispatch({ type, phase: "prepare", payload: payload() })).ok, false);
|
|
assert.equal((await h.dispatch({ type, phase: "confirm", payload: payload() })).ok, false);
|
|
assert.equal((await h.dispatch({ type, emailAddress, payload: payload() })).ok, false);
|
|
});
|
|
|
|
for (const family of ["chrome", "firefox"]) {
|
|
test(`${family}: captures only SeaaS client ID, reuses it after worker restart and expires stale values`, async () => {
|
|
const tokenCalls = [];
|
|
const sessionStorage = {};
|
|
const fetch = async (url, options) => {
|
|
if (url.includes("/resourceUsers?")) return response({ resourcePartyId: 303 });
|
|
if (url.endsWith("/Catalogue_c/505")) {
|
|
tokenCalls.push(options);
|
|
return response({ result: "synthetic-token" });
|
|
}
|
|
return response({ SrNumber: "SR1" }, 201);
|
|
};
|
|
const h = loadBackground({ family, fetch, sessionStorage });
|
|
assert.equal(h.headerListeners.length, 1);
|
|
const capture = h.headerListeners[0];
|
|
assert.deepEqual(capture.options, ["requestHeaders"]);
|
|
assert.deepEqual(capture.filter.types, ["xmlhttprequest"]);
|
|
const request = { tabId: 12, url: "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live;profile=PROD/services/auth/1.1/proxy/example",
|
|
requestHeaders: [{ name: "X-AppBuilder-Client-Id", value: "synthetic-client" },
|
|
{ name: "Cookie", value: "do-not-store-cookie" }, { name: "Authorization", value: "do-not-store-auth" }] };
|
|
capture.listener({ ...request, url: request.url.replace("SeaaS_initiation", "otherApp") });
|
|
capture.listener({ ...request, url: request.url.replace("comcipapic-oalprod.integration.ocp.oraclecloud.com", "other.example") });
|
|
capture.listener({ ...request, tabId: -1 });
|
|
capture.listener({ ...request, incognito: true });
|
|
capture.listener({ ...request, requestHeaders: [{ name: "x-appbuilder-client-id", value: "invalid\r\nheader" }] });
|
|
assert.deepEqual(sessionStorage, {});
|
|
capture.listener(request);
|
|
assert.equal(Object.keys(sessionStorage).length, 1);
|
|
assert.ok(!JSON.stringify(sessionStorage).includes("do-not-store"));
|
|
const prepared = await h.dispatch({ type, phase: "prepare", emailAddress, payload: payload() });
|
|
assert.equal(prepared.headers.requestSrTokenCatalog["x-appbuilder-client-id"], "synthetic-client");
|
|
assert.equal(tokenCalls.length, 0);
|
|
const restarted = loadBackground({ family, fetch, sessionStorage });
|
|
const result = await restarted.dispatch({ type, phase: "confirm", catalogId: 505, payload: prepared.payload });
|
|
assert.equal(result.ok, true);
|
|
assert.equal(tokenCalls[0].headers["x-appbuilder-client-id"], "synthetic-client");
|
|
assert.equal(result.debugRequests[0].metadata.appBuilderClientId, "Observed in SeaaS requests");
|
|
restarted.advance(60 * 60 * 1000 + 1);
|
|
const expired = await restarted.dispatch({ type, phase: "confirm", catalogId: 505, payload: prepared.payload });
|
|
assert.equal(expired.ok, true);
|
|
assert.equal(tokenCalls[1].headers["x-appbuilder-client-id"], undefined);
|
|
assert.equal(expired.debugRequests[0].metadata.appBuilderClientId, "Not observed in SeaaS requests");
|
|
capture.listener({ ...request, requestHeaders: [{ name: "x-appbuilder-client-id", value: "renewed-client" }] });
|
|
const refreshed = await h.dispatch({ type, phase: "prepare", emailAddress, payload: payload() });
|
|
assert.equal(refreshed.headers.requestSrTokenCatalog["x-appbuilder-client-id"], "renewed-client");
|
|
assert.equal(h.tabs.length, 0, "header discovery does not add background page loads");
|
|
});
|
|
}
|
|
|
|
test("Origin rule failure blocks token acquisition and SR creation", async () => {
|
|
const h = loadBackground({ ruleError: true, fetch: async () => assert.fail("No request expected") });
|
|
const result = await h.dispatch({ type, phase: "confirm", catalogId: 505, payload: { ...payload(), ReportedByPartyId: 303 } });
|
|
assert.equal(result.ok, false);
|
|
assert.match(result.error, /Rule permission denied/);
|
|
});
|
|
|
|
test("Origin rule failure prevents the Internal SR POST", async () => {
|
|
const calls = [];
|
|
const h = loadBackground({ ruleError: true, fetch: async url => {
|
|
calls.push(url);
|
|
if (url.includes("/serviceRequests?")) return response({ items: [parentInfo()] });
|
|
assert.match(url, /\/resourceUsers\?/);
|
|
return response({ resourcePartyId: 303 });
|
|
} });
|
|
const result = await h.dispatch({ type: internalType, emailAddress, payload: internalInput() });
|
|
assert.equal(result.ok, false);
|
|
assert.match(result.error, /Rule permission denied/);
|
|
assert.equal(calls.length, 2);
|
|
});
|
|
|
|
test("resource lookup failures or invalid resourcePartyId never obtain a token or create SR", async () => {
|
|
for (const body of [{ resourcePartyId: null }, { resourceProfileId: 999 }, { resourcePartyId: "9007199254740993" }, { resourcePartyId: 0 }, "unauthorized"]) {
|
|
let calls = 0;
|
|
const h = loadBackground({ fetch: async () => {
|
|
calls += 1;
|
|
return response(body, body === "unauthorized" ? 401 : 200);
|
|
} });
|
|
const result = await h.dispatch({ type, phase: "prepare", emailAddress, payload: payload() });
|
|
assert.equal(result.ok, false);
|
|
assert.equal(calls, 1);
|
|
assert.equal(result.debugRequests[0].name, "requestResourceUser");
|
|
}
|
|
});
|
|
|
|
test("token rejection never creates SR; creation failures are never automatically retried", async () => {
|
|
for (const failure of ["token", "create", "empty-token", "network"]) {
|
|
let calls = 0;
|
|
const h = loadBackground({ fetch: async () => {
|
|
calls += 1;
|
|
if (calls === 1) return response({ resourcePartyId: 303 });
|
|
if (calls === 2) return failure === "token" ? response({}, 401)
|
|
: response({ result: failure === "empty-token" ? "" : "secret-token" });
|
|
if (failure === "network") throw new Error("Network error");
|
|
return response({}, 500);
|
|
} });
|
|
const prepared = await h.dispatch({ type, phase: "prepare", emailAddress, payload: payload() });
|
|
const result = await h.dispatch({ type, phase: "confirm", catalogId: 505, payload: prepared.payload });
|
|
assert.equal(result.ok, false);
|
|
assert.equal(calls, failure === "token" || failure === "empty-token" ? 2 : 3);
|
|
assertDebugVisibility(result, "secret-token");
|
|
assert.equal(h.tabs.length, 0);
|
|
}
|
|
});
|