refactor: extract helpers into kmeContentSourceAdapterHelpers.js

Move getValidToken, validateSettings, extractHydraItems, and buildSitemapXml
out of the proxy IIFE into src/globalVariables/kmeContentSourceAdapterHelpers.js
following the literal function body pattern (auto-loaded by server.js, injected
as 'kmeContentSourceAdapterHelpers' into VM context).

oidcAuthFlow() and sitemapFlow() remain in the proxy script as they own req/res.

Update unit and contract tests to evaluate the helpers file with the same mock
dependencies used in each VM context, ensuring error-throwing axios overrides
are correctly seen by the helpers' closures.

All 31 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-22 22:21:00 -05:00
parent 979521800c
commit 07c3cc72cc
4 changed files with 251 additions and 177 deletions

View File

@@ -13,6 +13,19 @@ const proxyPath = join(__dirname, '../../src/proxyScripts/kmeContentSourceAdapte
const proxyCode = readFileSync(proxyPath, 'utf-8');
const proxyScript = new vm.Script(proxyCode, { filename: 'kmeContentSourceAdapter.js' });
const helpersPath = join(__dirname, '../../src/globalVariables/kmeContentSourceAdapterHelpers.js');
const helpersCode = readFileSync(helpersPath, 'utf-8');
const helpersWrapped = `(function() {\n${helpersCode}\n})()`;
const helpersScript = new vm.Script(helpersWrapped, { filename: 'kmeContentSourceAdapterHelpers.js' });
/**
* Evaluate the helpers file in a context built from the provided deps, returning
* the helpers object. Mirrors how server.js loads globalVariables/ JS files.
*/
function makeHelpers(deps) {
return helpersScript.runInContext(vm.createContext(deps));
}
/**
* Build a minimal VM context satisfying the vm-context contract.
* @param {import('node:test').TestContext} t
@@ -43,7 +56,7 @@ function makeContext(t, overrides = {}) {
get headers() { return headers; },
};
const kme_CSA_settings = {
const defaultSettings = {
tokenUrl: 'https://auth.example.com/token',
username: 'testuser',
password: 'testpass',
@@ -51,22 +64,39 @@ function makeContext(t, overrides = {}) {
scope: 'openid',
};
const axiosMock = {
const defaultAxiosMock = {
post: t.mock.fn(async () => ({
data: { id_token: 'mock-token', expires_in: 9_999_999_999 },
})),
get: t.mock.fn(async () => ({
data: { items: [] },
data: { 'hydra:member': [] },
})),
};
// Resolve the final axios and settings — overrides take precedence.
// Helpers must close over the SAME axios/settings that the VM context will use,
// otherwise tests that pass error-throwing axios overrides would get helpers
// that still use the success-returning default.
const resolvedAxios = overrides.axios ?? defaultAxiosMock;
const resolvedSettings = overrides.kme_CSA_settings ?? defaultSettings;
const kmeContentSourceAdapterHelpers = makeHelpers({
URLSearchParams,
console,
axios: resolvedAxios,
redis,
kme_CSA_settings: resolvedSettings,
xmlBuilder,
});
const ctx = vm.createContext({
URLSearchParams,
console,
axios: axiosMock,
axios: resolvedAxios,
redis,
kme_CSA_settings,
kme_CSA_settings: defaultSettings,
xmlBuilder,
kmeContentSourceAdapterHelpers,
req: { url: '/', method: 'GET', headers: {} },
res,
...overrides,
@@ -76,7 +106,7 @@ function makeContext(t, overrides = {}) {
ctx._redis = redis;
ctx._res = res;
ctx._store = _store;
ctx._axios = axiosMock;
ctx._axios = resolvedAxios;
return ctx;
}
@@ -291,15 +321,23 @@ describe('stampede guard', () => {
const res1 = makeRes(t);
const res2 = makeRes(t);
// Helpers must share the same redis/kme_CSA_settings/axios so the stampede guard works
const sharedHelpers = makeHelpers({
URLSearchParams, console, axios: sharedAxios,
redis, kme_CSA_settings, xmlBuilder,
});
const ctx1 = vm.createContext({
URLSearchParams, console, axios: sharedAxios,
redis, kme_CSA_settings, xmlBuilder,
kmeContentSourceAdapterHelpers: sharedHelpers,
req: { url: '/', method: 'GET', headers: {} },
res: res1,
});
const ctx2 = vm.createContext({
URLSearchParams, console, axios: sharedAxios,
redis, kme_CSA_settings, xmlBuilder,
kmeContentSourceAdapterHelpers: sharedHelpers,
req: { url: '/', method: 'GET', headers: {} },
res: res2,
});