Files
RustProxy/tools/checks/h3_cache_mobile_0315_test.mjs
T
2026-10-13 00:57:19 +02:00

79 lines
5.2 KiB
JavaScript

// Source contract tests only: no Rust compilation, live H3 peer or UI browser.
// Keep these checks aligned with Rust unit tests in src/http3/tests.rs and
// src/runtime/tests.rs; do not interpret source checks as wire acceptance.
import assert from 'node:assert/strict';
import fs from 'node:fs';
import vm from 'node:vm';
const read = path => fs.readFileSync(new URL(`../../${path}`, import.meta.url), 'utf8');
const h3 = read('src/http3.rs');
const identity = read('src/http3/identity.rs');
const body = read('src/http3/body.rs');
const h3Tests = read('src/http3/tests.rs');
const cache = read('src/runtime/types.rs');
const cacheTests = read('src/runtime/tests.rs');
const css = read('src/admin/web/style.css');
const html = read('src/admin/web/index.html');
const logs = read('src/admin/web/file-logs.js');
const en = JSON.parse(read('langs/en_US.json')).translations;
const pl = JSON.parse(read('langs/pl_pl.json')).translations;
// A peer-initiated RESET_STREAM or STOP_SENDING must not be logged as a
// fabricated HTTP 400 when the browser aborts a prior navigation on F5.
assert.match(identity, /fn client_cancelled_code\(code: Code\)/);
assert.match(identity, /code == Code::H3_REQUEST_CANCELLED \|\| code == Code::H3_NO_ERROR/);
assert.match(identity, /StreamError::RemoteTerminate \{ code, \.\. \} if client_cancelled_code\(\*code\)/);
assert.match(h3, /Ok\(Err\(error\)\)\s*=>\s*\{\s*if client_cancelled_request\(&error\)/);
assert.match(h3, /\} else \{\s*log_protocol_error\(\s*&state, "http3_request_error", error, peer, local, 400/);
assert.match(h3, /"http3_invalid_headers"[^\n]*\n\s*peer, local, 400/);
assert.match(h3, /error\.downcast_ref::<h3::error::StreamError>\(\)\s*\.is_some_and\(client_cancelled_request\)/);
assert.match(h3, /if !cancelled \{[\s\S]*?send\.stop_stream\(Code::H3_INTERNAL_ERROR\)/);
const bodyDrop = body.slice(body.indexOf('impl Drop for ReceiveBody'));
assert.ok(!bodyDrop.includes('stop_sending('), 'Do not reintroduce a receive-stream Drop panic');
assert.match(h3Tests, /client_cancelled_code\(Code::H3_MESSAGE_ERROR\)/);
// H2/H3 client transport selection is not a cached representation property.
const revision = cache.slice(cache.indexOf('fn cache_revision('), cache.indexOf('fn cache_invalidation('));
assert.match(revision, /for vhost in &mut vhosts/);
assert.match(revision, /vhost\.http2_enabled = true;/);
assert.match(revision, /vhost\.http3_enabled = true;/);
assert.ok(revision.indexOf('vhost.http3_enabled = true;') < revision.indexOf('serde_json::to_vec('));
assert.match(cacheTests, /cache_invalidation\(&base, &protocol_only\), CacheInvalidation::None/);
assert.match(cacheTests, /cache_invalidation\(&protocol_only, &base\), CacheInvalidation::None/);
assert.match(cacheTests, /cache_revision\(&both_protocols\), identity/);
assert.match(cacheTests, /CacheInvalidation::All\(value\) if value == cache_identity/);
// Every row is still keyboard-expandable and all selected columns are
// represented on the mobile card rather than removed by display:none.
assert.match(html, /id="panel-filelogs"/);
assert.match(logs, /row\.tabIndex = 0/);
assert.match(logs, /row\.setAttribute\("aria-expanded"/);
assert.match(logs, /event\.key === "Enter" \|\| event\.key === " "/);
assert.match(logs, /cell\.dataset\.mobileColumn = id/);
assert.match(logs, /cell\.dataset\.mobileLabel = label/);
assert.match(logs, /if \(selectedIndex === index\) nodes\.push\(detailRow\(entry\)\)/);
assert.match(css, /@media \(max-width: 700px\)/);
assert.match(css, /#panel-filelogs \.file-logs-table tbody tr:not\(\.file-log-detail-row\) \{\s*display: grid/);
assert.match(css, /#panel-filelogs \.file-logs-table tbody tr:not\(\.file-log-detail-row\)\s*>\s*td \{\s*display: block/);
assert.match(css, /overflow-wrap: anywhere/);
assert.match(css, /#panel-filelogs \.file-logs-table,\s*#panel-filelogs \.file-log-aggregate-table \{[^}]*overflow-x: auto/s);
assert.match(css, /#panel-filelogs \.file-log-inline-detail pre \{[^}]*max-width:\s*100%;[^}]*overflow-x:\s*auto;[^}]*\}/s);
// Exercise the actual browser code's entry-summary function with a logged
// H3 cancellation and a normal access event; this is a JS unit simulation.
const start = logs.indexOf(' function entrySummary(entry) {');
const end = logs.indexOf(' function nestedValue(', start);
assert.ok(start >= 0 && end > start, 'entrySummary function exists');
const context = vm.createContext({});
vm.runInContext(logs.slice(start, end), context);
assert.equal(vm.runInContext('entrySummary({reason:"http3_request_error",error:"Remote reset: H3_REQUEST_CANCELLED"})', context), 'http3_request_error');
assert.equal(vm.runInContext('entrySummary({method:"GET",host:"rp.example.test",path:"/admin"})', context), 'GET rp.example.test/admin');
for (const [language, pack] of [['en_US', en], ['pl_pl', pl]]) {
assert.match(pack.administrationPublicOriginHelp, /HTTPS/i, `${language}: origin scheme`);
assert.match(pack.administrationPublicOriginHelp, /Origin\/CSRF/i, `${language}: security purpose`);
assert.ok(pack.administrationHostsAdvanced.length > 10, `${language}: advanced DNS hosts`);
}
console.log('PASS: H3 cancellation classification, cache stability, mobile log-card/keyboard contracts and origin help.');
console.log('NOT VERIFIED: Rust build, actual QUIC navigation or full authenticated mobile UI.');