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:
@@ -15,6 +15,16 @@ 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 with the provided deps (mirrors server.js loadGlobalVariables). */
|
||||
function makeHelpers(deps) {
|
||||
return helpersScript.runInContext(vm.createContext(deps));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a minimal HTTP server that handles all requests with a fixed JSON body.
|
||||
* @param {number} statusCode
|
||||
@@ -78,19 +88,18 @@ describe('proxy HTTP contract: 200 OK', () => {
|
||||
|
||||
try {
|
||||
const res = makeRes();
|
||||
const redis = makeRedisFake();
|
||||
const kme_CSA_settings = {
|
||||
tokenUrl: mock.url,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
clientId: 'client',
|
||||
scope: 'openid',
|
||||
};
|
||||
const deps = { URLSearchParams, console, axios, xmlBuilder, redis, kme_CSA_settings };
|
||||
const ctx = vm.createContext({
|
||||
URLSearchParams,
|
||||
console,
|
||||
axios,
|
||||
xmlBuilder,
|
||||
redis: makeRedisFake(),
|
||||
kme_CSA_settings: {
|
||||
tokenUrl: mock.url,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
clientId: 'client',
|
||||
scope: 'openid',
|
||||
},
|
||||
...deps,
|
||||
kmeContentSourceAdapterHelpers: makeHelpers(deps),
|
||||
req: { url: '/', method: 'GET', headers: {} },
|
||||
res,
|
||||
});
|
||||
@@ -116,19 +125,18 @@ describe('proxy HTTP contract: 401 Unauthorized', () => {
|
||||
|
||||
try {
|
||||
const res = makeRes();
|
||||
const redis = makeRedisFake();
|
||||
const kme_CSA_settings = {
|
||||
tokenUrl: mock.url,
|
||||
username: 'bad-user',
|
||||
password: 'bad-pass',
|
||||
clientId: 'client',
|
||||
scope: 'openid',
|
||||
};
|
||||
const deps = { URLSearchParams, console, axios, xmlBuilder, redis, kme_CSA_settings };
|
||||
const ctx = vm.createContext({
|
||||
URLSearchParams,
|
||||
console,
|
||||
axios,
|
||||
xmlBuilder,
|
||||
redis: makeRedisFake(),
|
||||
kme_CSA_settings: {
|
||||
tokenUrl: mock.url,
|
||||
username: 'bad-user',
|
||||
password: 'bad-pass',
|
||||
clientId: 'client',
|
||||
scope: 'openid',
|
||||
},
|
||||
...deps,
|
||||
kmeContentSourceAdapterHelpers: makeHelpers(deps),
|
||||
req: { url: '/', method: 'GET', headers: {} },
|
||||
res,
|
||||
});
|
||||
@@ -160,22 +168,20 @@ describe('sitemap endpoint', () => {
|
||||
redis.hSet('authorization', 'expiry', '9999999999');
|
||||
|
||||
const res = makeRes();
|
||||
const kme_CSA_settings = {
|
||||
tokenUrl: tokenUrl ?? 'http://127.0.0.1:1', // not used (cache hit)
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
clientId: 'client',
|
||||
scope: 'openid',
|
||||
searchApiBaseUrl: searchUrl,
|
||||
tenant: 'test',
|
||||
proxyBaseUrl: 'https://proxy.example.com',
|
||||
};
|
||||
const deps = { URLSearchParams, console, axios, xmlBuilder, redis, kme_CSA_settings };
|
||||
const ctx = vm.createContext({
|
||||
URLSearchParams,
|
||||
console,
|
||||
axios,
|
||||
xmlBuilder,
|
||||
redis,
|
||||
kme_CSA_settings: {
|
||||
tokenUrl: tokenUrl ?? 'http://127.0.0.1:1', // not used (cache hit)
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
clientId: 'client',
|
||||
scope: 'openid',
|
||||
searchApiBaseUrl: searchUrl,
|
||||
tenant: 'test',
|
||||
proxyBaseUrl: 'https://proxy.example.com',
|
||||
},
|
||||
...deps,
|
||||
kmeContentSourceAdapterHelpers: makeHelpers(deps),
|
||||
req: { url: '/sitemap.xml', method: 'GET', headers: {} },
|
||||
res,
|
||||
});
|
||||
@@ -273,19 +279,18 @@ describe('non-sitemap endpoint (regression)', () => {
|
||||
|
||||
try {
|
||||
const res = makeRes();
|
||||
const redis = makeRedisFake();
|
||||
const kme_CSA_settings = {
|
||||
tokenUrl: mock.url,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
clientId: 'client',
|
||||
scope: 'openid',
|
||||
};
|
||||
const deps = { URLSearchParams, console, axios, xmlBuilder, redis, kme_CSA_settings };
|
||||
const ctx = vm.createContext({
|
||||
URLSearchParams,
|
||||
console,
|
||||
axios,
|
||||
xmlBuilder,
|
||||
redis: makeRedisFake(),
|
||||
kme_CSA_settings: {
|
||||
tokenUrl: mock.url,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
clientId: 'client',
|
||||
scope: 'openid',
|
||||
},
|
||||
...deps,
|
||||
kmeContentSourceAdapterHelpers: makeHelpers(deps),
|
||||
req: { url: '/', method: 'GET', headers: {} },
|
||||
res,
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user