Implemented the functionality to create Service Requests
This commit is contained in:
244
tests/srCreate.test.cjs
Normal file
244
tests/srCreate.test.cjs
Normal file
@@ -0,0 +1,244 @@
|
||||
const assert = require("node:assert/strict");
|
||||
const { test } = require("node:test");
|
||||
const { loadBackground, response } = require("./support/characterization.cjs");
|
||||
const type = "opportunitiesExtension.requestPostServiceRequest";
|
||||
const emailAddress = "signed.in+sr@example.invalid";
|
||||
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" });
|
||||
}
|
||||
assert.ok(!JSON.stringify(result).includes("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));
|
||||
assert.ok(!JSON.stringify(result).includes("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("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);
|
||||
assert.ok(!JSON.stringify(result).includes("secret-token"));
|
||||
assert.equal(h.tabs.length, 0);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user