103 lines
5.8 KiB
JavaScript
103 lines
5.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// Exercise the shipped 0.29.7 backend monitor editor with a minimal DOM double.
|
|
// This checks browser form behavior, not health probing or compiled Rust code.
|
|
import fs from 'node:fs';
|
|
import vm from 'node:vm';
|
|
|
|
const root = process.cwd();
|
|
const core = fs.readFileSync(`${root}/src/admin/web/vhosts/core.js`, 'utf8');
|
|
const editor = fs.readFileSync(`${root}/src/admin/web/vhosts/editor.js`, 'utf8');
|
|
const settings = fs.readFileSync(`${root}/src/admin/web/app/settings.js`, 'utf8');
|
|
const html = fs.readFileSync(`${root}/src/admin/web/index.html`, 'utf8');
|
|
const start = core.indexOf(' function memberRow(');
|
|
const end = core.indexOf(' function countryRateEditor(', start);
|
|
if (start < 0 || end <= start) throw new Error('Backend editor code not found');
|
|
const check = (condition, reason) => { if (!condition) throw new Error(reason); };
|
|
|
|
class Element {
|
|
constructor(tag = 'div', value = '', css = '') {
|
|
this.tag = tag;
|
|
this.value = value == null ? '' : String(value);
|
|
this.checked = false;
|
|
this.hidden = false;
|
|
this.disabled = false;
|
|
this.name = '';
|
|
this.children = [];
|
|
this.listeners = new Map();
|
|
this.classes = new Set(css.split(/\s+/).filter(Boolean));
|
|
this.textContent = this.value;
|
|
}
|
|
append(...children) { for (const child of children) { child.parent = this; this.children.push(child); } }
|
|
addEventListener(type, handler) { this.listeners.set(type, handler); }
|
|
dispatch(type) { this.listeners.get(type)?.(); }
|
|
querySelectorAll() {
|
|
const found = [];
|
|
const walk = node => { for (const child of node.children) {
|
|
if (child.tag === 'input' || child.tag === 'select') found.push(child);
|
|
walk(child);
|
|
}};
|
|
walk(this);
|
|
return found;
|
|
}
|
|
}
|
|
const el = (tag, value, css) => new Element(tag, value, css);
|
|
const text = (tag, label, css) => el(tag, label, css);
|
|
const field = (parent, name, label, value, type = 'text') => {
|
|
const wrapper = el('label');
|
|
const input = el(type === 'select' ? 'select' : 'input', value);
|
|
input.name = name;
|
|
input.checked = !!value;
|
|
wrapper.append(input);
|
|
parent.append(wrapper);
|
|
return input;
|
|
};
|
|
const button = (label, handler) => { const node = el('button', label); node.addEventListener('click', handler); return node; };
|
|
const note = (parent, label) => { const node = text('span', label); parent.append(node); return node; };
|
|
const parent = el('div');
|
|
const value = {
|
|
id: 'error_404', url: 'http://127.0.0.1:8282', weight: 1, enabled: true,
|
|
pool_eligible: false, health_check_enabled: false, preserve_host: true,
|
|
health_check: { mode: 'http', path: '/ready', host: 'errors.example.test',
|
|
interval_ms: 10000, timeout_ms: 1000, slow_response_ms: 500, fall: 2,
|
|
rise: 1, expected_status_min: 200, expected_status_max: 399 }
|
|
};
|
|
vm.runInNewContext(`${core.slice(start, end)}\nmemberRow(parent, value);`, {
|
|
parent, value, el, text, field, button, note,
|
|
t: key => key, toast: () => {}, changed: () => {},
|
|
appConfirm: async () => true, adminListenerBackendCandidate: () => false, backendHealthEditorControl: null
|
|
});
|
|
const row = parent.children[0];
|
|
const input = name => (name.startsWith('member-health-') && name !== 'member-health-enabled' ? row.memberHealthPanel : row).querySelectorAll().find(item => item.name === name);
|
|
const fields = [];
|
|
const visit = node => { if (node.classes.has('vh-backend-health-fields')) fields.push(node); for (const child of node.children) visit(child); };
|
|
visit(row.memberHealthPanel);
|
|
check(fields.length === 1, 'Custom backend health panel not created');
|
|
check(input('member-pool').checked === false, 'Internal 404 backend accidentally joined balanced pool');
|
|
check(input('member-enabled').checked === true, 'Backend was disabled when monitoring was disabled');
|
|
check(input('member-health-enabled').checked === false, 'Explicit monitoring off not restored');
|
|
check(input('member-health-custom').checked === true, 'Custom health not restored');
|
|
check(input('member-health-mode').value === 'http', 'Member health mode lost');
|
|
check(input('member-health-path').value === '/ready', 'Member health path lost');
|
|
check(input('member-health-interval').value === '10000', 'Member check interval lost');
|
|
check(fields[0].hidden === false && input('member-health-path').disabled, 'Unmonitored backend settings should be retained but disabled');
|
|
input('member-health-custom').checked = false;
|
|
input('member-health-custom').dispatch('change');
|
|
check(fields[0].hidden && input('member-health-path').disabled, 'Inherit switch did not hide/disable overrides');
|
|
input('member-health-custom').checked = true;
|
|
input('member-health-custom').dispatch('change');
|
|
check(!fields[0].hidden && input('member-health-path').disabled, 'Unmonitored backend unexpectedly became editable');
|
|
input('member-health-enabled').checked = true;
|
|
input('member-health-enabled').dispatch('change');
|
|
check(!input('member-health-path').disabled, 'Monitored custom fields failed to re-enable');
|
|
check(input('member-pool').checked === false, 'Monitoring switch changed balanced-pool eligibility');
|
|
|
|
for (const part of [
|
|
'health_check_enabled: yes("member-health-enabled", row)',
|
|
'health_check: yes("member-health-custom", row.memberHealthPanel)',
|
|
'expose_only_hits: tri("vh-cache-only-hits")',
|
|
]) check((editor + core).includes(part), `Missing saved field: ${part}`);
|
|
check(editor.includes('tri(cacheOptions, "vh-cache-only-hits", "cacheOnlyHits", cache.expose_only_hits)'), 'Vhost cache override does not roundtrip');
|
|
check(html.includes('id="cache-only-hits"') && settings.includes('expose_only_hits: $("cache-only-hits").checked'), 'Global cache checkbox does not save');
|
|
check(settings.includes('$("cache-only-hits").checked = !!c.expose_only_hits'), 'Global cache checkbox does not load');
|
|
console.log('PASS: 0.29.7 backend monitor and custom policy form state, independent pool status, global/vhost cache diagnostics toggles.');
|