Files
worldmonitor/tests/helpers/runtime-config-panel-harness.mjs
Elie Habib 9de81ecb2c test(resilience): add dedicated scorer and ranking suites (#2681)
* test(resilience): add dedicated scorer and ranking suites

Rebased replacement for #2671, based on main after #2673 merged.

- Extract shared fake Upstash Redis harness into
  tests/helpers/fake-upstash-redis.mts (ZADD, ZRANGE, ZREMRANGEBYRANK,
  EXPIRE support)
- Add tests/resilience-scorers.test.mts: scorer range validation,
  zero-coverage check, weighted overall score contract
- Add tests/resilience-ranking.test.mts: sort order, cache-hit
  behavior, synchronous warmup (adapted for await-warmup from #2673)
- Slim tests/resilience-handlers.test.mts to use shared harness
- Stub Dodo checkout modules in runtime-config-panel-harness.mjs

Closes #2490

Co-authored-by: Lucas Passos <lspassos1@users.noreply.github.com>

* fix(tests): address P2 review findings on fake Redis harness

- Use nullish coalescing (??) instead of logical OR (||) for numeric
  arg fallbacks in ZADD, ZRANGE, ZREMRANGEBYRANK
- Return real count from ZADD (new members) and ZREMRANGEBYRANK
  (removed members) instead of hardcoded 1
- Extract installRedis into shared fake-upstash-redis.mts, remove
  duplicate from resilience-ranking and resilience-scorers tests

---------

Co-authored-by: Lucas Passos <lspassos1@users.noreply.github.com>
2026-04-04 17:01:22 +04:00

661 lines
17 KiB
JavaScript

import { build } from 'esbuild';
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, '..', '..');
const entry = resolve(root, 'src/components/RuntimeConfigPanel.ts');
class MiniClassList {
constructor() {
this.values = new Set();
}
add(...tokens) {
tokens.forEach((token) => this.values.add(token));
}
remove(...tokens) {
tokens.forEach((token) => this.values.delete(token));
}
contains(token) {
return this.values.has(token);
}
toggle(token, force) {
if (force === true) {
this.values.add(token);
return true;
}
if (force === false) {
this.values.delete(token);
return false;
}
if (this.values.has(token)) {
this.values.delete(token);
return false;
}
this.values.add(token);
return true;
}
setFromString(value) {
this.values = new Set(String(value).split(/\s+/).filter(Boolean));
}
toString() {
return Array.from(this.values).join(' ');
}
}
class MiniNode extends EventTarget {
constructor() {
super();
this.childNodes = [];
this.parentNode = null;
this.parentElement = null;
}
appendChild(child) {
if (child instanceof MiniDocumentFragment) {
const children = [...child.childNodes];
children.forEach((node) => this.appendChild(node));
return child;
}
if (child.parentNode) {
child.parentNode.removeChild(child);
}
child.parentNode = this;
child.parentElement = this instanceof MiniElement ? this : null;
this.childNodes.push(child);
return child;
}
removeChild(child) {
const index = this.childNodes.indexOf(child);
if (index >= 0) {
this.childNodes.splice(index, 1);
child.parentNode = null;
child.parentElement = null;
}
return child;
}
insertBefore(child, referenceNode) {
if (referenceNode == null) {
return this.appendChild(child);
}
if (child.parentNode) {
child.parentNode.removeChild(child);
}
const index = this.childNodes.indexOf(referenceNode);
if (index === -1) {
return this.appendChild(child);
}
child.parentNode = this;
child.parentElement = this instanceof MiniElement ? this : null;
this.childNodes.splice(index, 0, child);
return child;
}
get firstChild() {
return this.childNodes[0] ?? null;
}
get lastChild() {
return this.childNodes.at(-1) ?? null;
}
get textContent() {
return this.childNodes.map((child) => child.textContent ?? '').join('');
}
set textContent(value) {
this.childNodes = [new MiniText(value ?? '')];
}
}
class MiniText extends MiniNode {
constructor(value) {
super();
this.value = String(value);
}
get textContent() {
return this.value;
}
set textContent(value) {
this.value = String(value);
}
get outerHTML() {
return this.value;
}
}
class MiniDocumentFragment extends MiniNode {
get outerHTML() {
return this.childNodes.map((child) => child.outerHTML ?? child.textContent ?? '').join('');
}
}
class MiniElement extends MiniNode {
constructor(tagName) {
super();
this.tagName = tagName.toUpperCase();
this.attributes = new Map();
this.classList = new MiniClassList();
this.dataset = {};
this.style = {};
this._innerHTML = '';
this.id = '';
this.title = '';
this.disabled = false;
}
get className() {
return this.classList.toString();
}
set className(value) {
this.classList.setFromString(value);
}
get innerHTML() {
if (this._innerHTML) return this._innerHTML;
return this.childNodes.map((child) => child.outerHTML ?? child.textContent ?? '').join('');
}
set innerHTML(value) {
this._innerHTML = String(value);
this.childNodes = [];
}
appendChild(child) {
this._innerHTML = '';
return super.appendChild(child);
}
insertBefore(child, referenceNode) {
this._innerHTML = '';
return super.insertBefore(child, referenceNode);
}
removeChild(child) {
this._innerHTML = '';
return super.removeChild(child);
}
setAttribute(name, value) {
const stringValue = String(value);
this.attributes.set(name, stringValue);
if (name === 'class') {
this.className = stringValue;
} else if (name === 'id') {
this.id = stringValue;
} else if (name.startsWith('data-')) {
const key = name
.slice(5)
.split('-')
.map((part, index) => (index === 0 ? part : `${part[0]?.toUpperCase() ?? ''}${part.slice(1)}`))
.join('');
this.dataset[key] = stringValue;
}
}
getAttribute(name) {
return this.attributes.get(name) ?? null;
}
hasAttribute(name) {
return this.attributes.has(name);
}
removeAttribute(name) {
this.attributes.delete(name);
if (name === 'class') this.className = '';
}
querySelector() {
return null;
}
querySelectorAll() {
return [];
}
closest() {
return null;
}
remove() {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
}
getBoundingClientRect() {
return { width: 1, height: 1, top: 0, left: 0, right: 1, bottom: 1 };
}
get nextElementSibling() {
if (!this.parentNode) return null;
const siblings = this.parentNode.childNodes.filter((child) => child instanceof MiniElement);
const index = siblings.indexOf(this);
return index >= 0 ? siblings[index + 1] ?? null : null;
}
get isConnected() {
let current = this.parentNode;
while (current) {
if (current === globalThis.document?.body || current === globalThis.document?.documentElement) {
return true;
}
current = current.parentNode;
}
return false;
}
get outerHTML() {
return `<${this.tagName.toLowerCase()}>${this.innerHTML}</${this.tagName.toLowerCase()}>`;
}
}
class MiniStorage {
constructor() {
this.values = new Map();
}
getItem(key) {
return this.values.has(key) ? this.values.get(key) : null;
}
setItem(key, value) {
this.values.set(key, String(value));
}
removeItem(key) {
this.values.delete(key);
}
clear() {
this.values.clear();
}
}
class MiniDocument extends EventTarget {
constructor() {
super();
this.documentElement = new MiniElement('html');
this.documentElement.clientHeight = 800;
this.documentElement.clientWidth = 1200;
this.body = new MiniElement('body');
this.documentElement.appendChild(this.body);
}
createElement(tagName) {
return new MiniElement(tagName);
}
createTextNode(value) {
return new MiniText(value);
}
createDocumentFragment() {
return new MiniDocumentFragment();
}
}
function createBrowserEnvironment() {
const document = new MiniDocument();
const localStorage = new MiniStorage();
const window = {
document,
localStorage,
innerHeight: 800,
innerWidth: 1200,
addEventListener() {},
removeEventListener() {},
open() {},
getComputedStyle() {
return {
display: '',
visibility: '',
gridTemplateColumns: 'none',
columnGap: '0',
};
},
};
return {
document,
localStorage,
window,
requestAnimationFrame() {
return 1;
},
cancelAnimationFrame() {},
};
}
function snapshotGlobal(name) {
return {
exists: Object.prototype.hasOwnProperty.call(globalThis, name),
value: globalThis[name],
};
}
function restoreGlobal(name, snapshot) {
if (snapshot.exists) {
globalThis[name] = snapshot.value;
return;
}
delete globalThis[name];
}
function createRuntimeState() {
return {
features: [],
availableIds: new Set(),
configuredCount: 0,
listeners: new Set(),
};
}
async function loadRuntimeConfigPanel() {
const tempDir = mkdtempSync(join(tmpdir(), 'wm-runtime-config-panel-'));
const outfile = join(tempDir, 'RuntimeConfigPanel.bundle.mjs');
const stubModules = new Map([
['runtime-config-stub', `
const state = globalThis.__wmRuntimeConfigPanelTestState;
export const RUNTIME_FEATURES = state.features;
export function getEffectiveSecrets() {
return [];
}
export function getRuntimeConfigSnapshot() {
const secrets = Object.fromEntries(
Array.from({ length: state.configuredCount }, (_, index) => [
'SECRET_' + (index + 1),
{ value: 'set', source: 'vault' },
]),
);
return { featureToggles: {}, secrets };
}
export function getSecretState() {
return { present: false, valid: false, source: 'missing' };
}
export function isFeatureAvailable(featureId) {
return state.availableIds.has(featureId);
}
export function isFeatureEnabled() {
return true;
}
export function setFeatureToggle() {}
export async function setSecretValue() {}
export function subscribeRuntimeConfig(listener) {
state.listeners.add(listener);
return () => state.listeners.delete(listener);
}
export function validateSecret() {
return { valid: true };
}
export async function verifySecretWithApi() {
return { valid: true };
}
`],
['runtime-stub', `export function isDesktopRuntime() { return true; }`],
['tauri-bridge-stub', `export async function invokeTauri() {}`],
['i18n-stub', `export function t(key) { return key; }`],
['dom-utils-stub', `
function append(parent, child) {
if (child == null || child === false) return;
if (typeof child === 'string' || typeof child === 'number') {
parent.appendChild(document.createTextNode(String(child)));
return;
}
parent.appendChild(child);
}
export function h(tag, propsOrChild, ...children) {
const el = document.createElement(tag);
let allChildren = children;
if (
propsOrChild != null &&
typeof propsOrChild === 'object' &&
!('tagName' in propsOrChild) &&
!('textContent' in propsOrChild)
) {
for (const [key, value] of Object.entries(propsOrChild)) {
if (value == null || value === false) continue;
if (key === 'className') {
el.className = value;
} else if (key === 'style' && typeof value === 'object') {
Object.assign(el.style, value);
} else if (key === 'dataset' && typeof value === 'object') {
Object.assign(el.dataset, value);
} else if (key.startsWith('on') && typeof value === 'function') {
el.addEventListener(key.slice(2).toLowerCase(), value);
} else if (value === true) {
el.setAttribute(key, '');
} else {
el.setAttribute(key, String(value));
}
}
} else {
allChildren = [propsOrChild, ...children];
}
allChildren.forEach((child) => append(el, child));
return el;
}
export function replaceChildren(el, ...children) {
el.innerHTML = '';
children.forEach((child) => append(el, child));
}
export function safeHtml() {
return document.createDocumentFragment();
}
`],
['analytics-stub', `export function trackPanelResized() {} export function trackFeatureToggle() {}`],
['ai-flow-settings-stub', `export function getAiFlowSettings() { return { badgeAnimation: false }; }`],
['sanitize-stub', `export function escapeHtml(value) { return String(value); }`],
['ollama-models-stub', `export async function fetchOllamaModels() { return []; }`],
['settings-constants-stub', `
export const SIGNUP_URLS = {};
export const PLAINTEXT_KEYS = new Set();
export const MASKED_SENTINEL = '***';
`],
['panel-gating-stub', `
export const PanelGateReason = { NONE: 'none', ANONYMOUS: 'anonymous', UNVERIFIED: 'unverified', FREE_TIER: 'free_tier' };
export function getPanelGateReason() { return PanelGateReason.NONE; }
`],
['dodo-checkout-stub', `
export const DodoPayments = {
Initialize() {},
Checkout: {
open() {},
},
};
`],
['dodo-empty-stub', 'export {};'],
]);
const aliasMap = new Map([
['@/services/runtime-config', 'runtime-config-stub'],
['../services/runtime', 'runtime-stub'],
['@/services/runtime', 'runtime-stub'],
['../services/tauri-bridge', 'tauri-bridge-stub'],
['@/services/tauri-bridge', 'tauri-bridge-stub'],
['../services/i18n', 'i18n-stub'],
['@/services/i18n', 'i18n-stub'],
['../utils/dom-utils', 'dom-utils-stub'],
['@/services/analytics', 'analytics-stub'],
['@/services/ai-flow-settings', 'ai-flow-settings-stub'],
['@/utils/sanitize', 'sanitize-stub'],
['@/services/ollama-models', 'ollama-models-stub'],
['@/services/settings-constants', 'settings-constants-stub'],
['@/services/panel-gating', 'panel-gating-stub'],
['dodopayments-checkout', 'dodo-checkout-stub'],
['dodopayments', 'dodo-empty-stub'],
['@dodopayments/core', 'dodo-empty-stub'],
['@dodopayments/convex', 'dodo-empty-stub'],
]);
const plugin = {
name: 'runtime-config-panel-test-stubs',
setup(buildApi) {
buildApi.onResolve({ filter: /.*/ }, (args) => {
const target = aliasMap.get(args.path);
return target ? { path: target, namespace: 'stub' } : null;
});
buildApi.onLoad({ filter: /.*/, namespace: 'stub' }, (args) => ({
contents: stubModules.get(args.path),
loader: 'js',
}));
},
};
const result = await build({
entryPoints: [entry],
bundle: true,
format: 'esm',
platform: 'browser',
target: 'es2020',
write: false,
plugins: [plugin],
});
writeFileSync(outfile, result.outputFiles[0].text, 'utf8');
const mod = await import(`${pathToFileURL(outfile).href}?t=${Date.now()}`);
return {
RuntimeConfigPanel: mod.RuntimeConfigPanel,
cleanupBundle() {
rmSync(tempDir, { recursive: true, force: true });
},
};
}
export async function createRuntimeConfigPanelHarness() {
const originalGlobals = {
document: snapshotGlobal('document'),
window: snapshotGlobal('window'),
localStorage: snapshotGlobal('localStorage'),
requestAnimationFrame: snapshotGlobal('requestAnimationFrame'),
cancelAnimationFrame: snapshotGlobal('cancelAnimationFrame'),
};
const browserEnvironment = createBrowserEnvironment();
const runtimeState = createRuntimeState();
globalThis.document = browserEnvironment.document;
globalThis.window = browserEnvironment.window;
globalThis.localStorage = browserEnvironment.localStorage;
globalThis.requestAnimationFrame = browserEnvironment.requestAnimationFrame;
globalThis.cancelAnimationFrame = browserEnvironment.cancelAnimationFrame;
globalThis.__wmRuntimeConfigPanelTestState = runtimeState;
let RuntimeConfigPanel;
let cleanupBundle;
try {
({ RuntimeConfigPanel, cleanupBundle } = await loadRuntimeConfigPanel());
} catch (error) {
delete globalThis.__wmRuntimeConfigPanelTestState;
restoreGlobal('document', originalGlobals.document);
restoreGlobal('window', originalGlobals.window);
restoreGlobal('localStorage', originalGlobals.localStorage);
restoreGlobal('requestAnimationFrame', originalGlobals.requestAnimationFrame);
restoreGlobal('cancelAnimationFrame', originalGlobals.cancelAnimationFrame);
throw error;
}
const activePanels = [];
function setRuntimeState({
totalFeatures,
availableFeatures,
configuredCount,
}) {
runtimeState.features.splice(
0,
runtimeState.features.length,
...Array.from({ length: totalFeatures }, (_, index) => ({ id: `feature-${index + 1}` })),
);
runtimeState.availableIds = new Set(
runtimeState.features.slice(0, availableFeatures).map((feature) => feature.id),
);
runtimeState.configuredCount = configuredCount;
}
function createPanel(options = { mode: 'alert' }) {
const panel = new RuntimeConfigPanel(options);
activePanels.push(panel);
return panel;
}
function emitRuntimeConfigChange() {
for (const listener of [...runtimeState.listeners]) {
listener();
}
}
function isHidden(panel) {
return panel.getElement().classList.contains('hidden');
}
function getAlertState(panel) {
const match = panel.content.innerHTML.match(/data-alert-state="([^"]+)"/);
return match?.[1] ?? null;
}
function reset() {
while (activePanels.length > 0) {
activePanels.pop()?.destroy();
}
runtimeState.features.length = 0;
runtimeState.availableIds = new Set();
runtimeState.configuredCount = 0;
runtimeState.listeners.clear();
browserEnvironment.localStorage.clear();
}
function cleanup() {
reset();
cleanupBundle();
delete globalThis.__wmRuntimeConfigPanelTestState;
restoreGlobal('document', originalGlobals.document);
restoreGlobal('window', originalGlobals.window);
restoreGlobal('localStorage', originalGlobals.localStorage);
restoreGlobal('requestAnimationFrame', originalGlobals.requestAnimationFrame);
restoreGlobal('cancelAnimationFrame', originalGlobals.cancelAnimationFrame);
}
return {
createPanel,
emitRuntimeConfigChange,
getAlertState,
isHidden,
reset,
cleanup,
setRuntimeState,
};
}