122 lines
6.8 KiB
JavaScript
122 lines
6.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// Real browser form callbacks with fake DOM; protocol/security checks are
|
|
// source-level assertions only. Rust unit and live HTTP tests need Cargo.
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import { resolve, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { runInNewContext } from 'node:vm';
|
|
|
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
|
|
const read = path => readFileSync(resolve(root, path), 'utf8');
|
|
const html = read('src/admin/web/index.html');
|
|
const css = read('src/admin/web/style.css');
|
|
const administration = read('src/admin/web/settings.js');
|
|
const cache = read('src/admin/web/app/settings.js');
|
|
const router = read('src/admin/router.rs');
|
|
const assets = read('src/admin/panel_assets.rs');
|
|
const compression = read('src/admin/panel_content.rs');
|
|
const ip = read('src/cache/model/http_invalidation.rs');
|
|
const cacheMethods = read('src/proxy/cache_http_invalidation.rs');
|
|
|
|
for (const codec of ['zstd', 'br', 'gzip']) {
|
|
assert.match(html, new RegExp(`id="admin-compression-${codec}"[^>]*checked`));
|
|
assert(assets.includes('"/assets/app.js"'));
|
|
}
|
|
for (const path of ['app.js','management.js','vhosts.js','settings.js','cache-panel.js','traffic-panel.js','style.css','langs.json']) {
|
|
if (path === "langs.json") assert(router.includes(`"/assets/${path}", get(panel_assets::serve)`), path);
|
|
else assert(router.includes('"/assets/{fingerprinted}"'), path);
|
|
assert(assets.includes(`"/assets/${path}"`), path);
|
|
}
|
|
assert(compression.includes('preferred_encoding(headers, &settings)'));
|
|
assert(compression.includes('HeaderValue::from_static("Accept-Encoding")'));
|
|
assert(compression.includes('header::CONTENT_LENGTH'));
|
|
assert(compression.includes('header::CONTENT_ENCODING'));
|
|
assert(compression.includes('crate::compression::no_transform(headers)'));
|
|
assert(html.includes('id="cache-http-invalidation-cidrs"'));
|
|
assert(html.includes('class="metric-info cache-maintenance-info"'));
|
|
assert(html.includes('data-i18n-aria="cacheHttpInvalidationCurlLabel"'));
|
|
for (const snippet of ["curl -i -X PURGE", "curl -i -X BAN", "X-RustProxy-Confirm-Wide: 1"]) {
|
|
assert(html.includes(snippet), snippet);
|
|
}
|
|
assert(css.includes('.cache-maintenance-info .metric-info-popover'));
|
|
|
|
assert(cache.includes('http_invalidation_allowed_cidrs: $("cache-http-invalidation-cidrs")'));
|
|
assert(ip.includes('http_invalidation_allowed_cidrs.len() <= 32'));
|
|
assert(ip.includes('net.prefix_len() > 0'));
|
|
assert(cacheMethods.includes('snapshot.envelope.config.cache.permits_http_invalidation(client_ip)'));
|
|
assert(cacheMethods.includes('state.store.audit('));
|
|
assert(cacheMethods.includes('spawn_blocking'));
|
|
assert(cacheMethods.includes('x-rustproxy-confirm-wide'));
|
|
assert(cacheMethods.includes('InvalidationSelector::Exact'));
|
|
assert(cacheMethods.includes('InvalidationSelector::Prefix'));
|
|
assert(read('src/proxy/forward_entry.rs').includes('return cache_http_invalidation('));
|
|
const heading = html.lastIndexOf('data-i18n="systemInformation"');
|
|
assert(heading > 0);
|
|
const headerArea = html.slice(heading, heading + 600);
|
|
assert(headerArea.includes('<span class="pill">API</span>'));
|
|
assert(headerArea.indexOf('<span class="pill">API</span>') < headerArea.indexOf('id="system-info-refresh"'));
|
|
assert.match(css, /\.system-info-heading-actions\s*\{[^}]*display:\s*flex/);
|
|
for (const lang of ['en_US','pl_pl']) {
|
|
const strings = JSON.parse(read(`langs/${lang}.json`));
|
|
const t = strings.translations;
|
|
assert(t.panelCompressionTitle && t.panelCompressionHelp && t.cacheHttpInvalidationHelp, lang);
|
|
assert(t.cacheHttpInvalidationCurlLabel && t.cacheHttpInvalidationCurlIntro && t.cacheHttpInvalidationCurlNote, lang);
|
|
}
|
|
|
|
// Execute the actual callbacks captured from the shipped frontend settings JS.
|
|
const fields = new Map();
|
|
const field = name => {
|
|
if (!fields.has(name)) fields.set(name, { checked: false, value: '' });
|
|
return fields.get(name);
|
|
};
|
|
for (const name of ['zstd', 'br', 'gzip']) field(`admin-compression-${name}`).checked = true;
|
|
field('admin-listen').value = '127.0.0.1:9090';
|
|
field('admin-session-days').value = '30';
|
|
let handlers = new Map();
|
|
const bindSettingsForm = (id, handler) => handlers.set(id, handler);
|
|
const adminStart = administration.indexOf('bindSettingsForm("administration-form", next => {');
|
|
const adminEnd = administration.indexOf('bindSettingsForm("tls-policy-form",', adminStart);
|
|
assert(adminStart > 0 && adminEnd > adminStart);
|
|
// Use the shipped helper in the extracted callback environment as well.
|
|
const hostHelperStart = administration.indexOf(' function pinnedAdminHostname(value) {');
|
|
const hostHelperEnd = administration.indexOf(' const normalizedRequestedTab', hostHelperStart);
|
|
assert(hostHelperStart > 0 && hostHelperEnd > hostHelperStart);
|
|
runInNewContext(administration.slice(hostHelperStart, hostHelperEnd) + administration.slice(adminStart, adminEnd), {
|
|
$: field, bindSettingsForm, latestStatus: { administration: { listen_locked: false } }, URL,
|
|
});
|
|
const adminNext = {};
|
|
handlers.get('administration-form')(adminNext);
|
|
assert.deepEqual(JSON.parse(JSON.stringify(adminNext.administration.panel_compression)),
|
|
{ zstd: true, br: true, gzip: true });
|
|
field('admin-public-origin').value = 'https://panel.example.test';
|
|
field('admin-hosts').value = 'panel.example.test\nother.example.test';
|
|
const pinnedNext = {};
|
|
handlers.get('administration-form')(pinnedNext);
|
|
assert.deepEqual(JSON.parse(JSON.stringify(pinnedNext.administration.allowed_hosts)), ['other.example.test']);
|
|
field('admin-compression-br').checked = false;
|
|
field('admin-compression-zstd').checked = false;
|
|
const offNext = {};
|
|
handlers.get('administration-form')(offNext);
|
|
assert.deepEqual(JSON.parse(JSON.stringify(offNext.administration.panel_compression)),
|
|
{ zstd: false, br: false, gzip: true });
|
|
|
|
const cacheStart = cache.indexOf('bindSettingsForm("cache-form", next => {');
|
|
const cacheEnd = cache.indexOf('bindSettingsForm("transform-form",', cacheStart);
|
|
assert(cacheStart > 0 && cacheEnd > cacheStart);
|
|
handlers = new Map();
|
|
field('cache-http-invalidation-cidrs').value = ' 192.0.2.50\n 2001:db8:2::/64, 198.51.100.9 \n';
|
|
field('cache-sie-statuses').value = '500,502,503,504';
|
|
runInNewContext(cache.slice(cacheStart, cacheEnd), {
|
|
$: field, bindSettingsForm, selectedCacheMemoryPreset: null, selectedCacheColdPreset: null,
|
|
});
|
|
const cacheNext = { cache: { max_old_setting: 'kept' } };
|
|
handlers.get('cache-form')(cacheNext);
|
|
assert.deepEqual(JSON.parse(JSON.stringify(cacheNext.cache.http_invalidation_allowed_cidrs)),
|
|
['192.0.2.50', '2001:db8:2::/64', '198.51.100.9']);
|
|
assert.equal(cacheNext.cache.max_old_setting, 'kept');
|
|
field('cache-http-invalidation-cidrs').value = '\n';
|
|
handlers.get('cache-form')(cacheNext);
|
|
assert.deepEqual(JSON.parse(JSON.stringify(cacheNext.cache.http_invalidation_allowed_cidrs)), []);
|
|
console.log('PASS: cache CIDR/administration codec UI roundtrip, headers, security source wiring, API badge layout');
|