This commit is contained in:
Mateusz Gruszczyński committed 2026-10-13 01:34:42 +02:00
1 parent 101df02a5f
commit bb025dcf44
27 files changed
+541 -85

No files matched your search

+2 -2
View File
@@ -9,8 +9,8 @@ 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);
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); };
+2 -2
View File
@@ -8,8 +8,8 @@ const core = fs.readFileSync('src/admin/web/vhosts/core.js', 'utf8');
const health = fs.readFileSync('src/admin/web/vhosts/health.js', 'utf8');
const editor = fs.readFileSync('src/admin/web/vhosts/editor.js', 'utf8');
const render = fs.readFileSync('src/admin/web/vhosts/render.js', 'utf8');
const first = core.indexOf(' function memberRow(');
const last = core.indexOf(' function countryRateEditor(', first);
const first = core.indexOf('function memberRow(');
const last = core.indexOf('function countryRateEditor(', first);
const assert = (condition, message) => { if (!condition) throw new Error(message); };
assert(first >= 0 && last > first, 'Vhost member editor missing');
class Element {
+2 -2
View File
@@ -7,7 +7,7 @@ import tomllib
root = Path(__file__).resolve().parents[2]
read = lambda path: (root / path).read_text(encoding='utf-8')
version = tomllib.loads(read('Cargo.toml'))['package']['version']
assert version == '0.32.1', version
assert version == '0.32.2', version
spec = json.loads(read('src/admin/openapi.json'))
assert spec['info']['version'] == version
paths = spec['paths']
@@ -64,5 +64,5 @@ for language in ('en_US', 'pl_pl'):
for key in ('certificateApiView', 'certificateApiEdit', 'certificateApiSave', 'certificateApiRegenerate',
'certificateApiRegenerateConfirm', 'certificateApiTokenHidden', 'certificateApiCreatedAt'):
assert strings.get(key), (language, key)
assert '0.32.1' in read('docs/TEST_REPORT.md')
assert '0.32.2' in read('docs/TEST_REPORT.md')
print('PASS: 0.31.9 per-credential API routes, separated metadata editing/token regeneration, OpenAPI security, UI and locales (source-only).')
+10 -10
View File
@@ -24,7 +24,7 @@ def read_docker_agent_source() -> str:
preflight(root)
manifest = tomllib.loads((root / "Cargo.toml").read_text())
assert manifest["package"]["version"] == "0.32.1"
assert manifest["package"]["version"] == "0.32.2"
assert manifest["dependencies"]["quinn"]["version"] == "0.11.12"
h3dep = manifest["dependencies"]["h3"]
assert h3dep["package"] == "hpx-h3" and h3dep["version"] == "=2.5.20" and "quinn" in h3dep["features"]
@@ -32,14 +32,14 @@ assert "h3-quinn" not in manifest["dependencies"]
lock_path = root / "Cargo.lock"
if lock_path.exists():
lock = lock_path.read_text()
assert 'name = "rust-proxy"\nversion = "0.32.1"' in lock
assert 'name = "rust-proxy"\nversion = "0.32.2"' in lock
assert 'name = "systemstat"' in lock and 'name = "metrics-process"' in lock and 'name = "sysinfo"' in lock, "Cargo.lock must be resolver-generated after adding cross-platform statistics libraries"
for release_doc, marker in {
"README.md": "# RustProxy 0.32.1",
"CHANGELOG.md": "## 0.32.1",
"ROADMAP.md": "## 0.32.1",
"AGENTS.md": "Current source version: 0.32.1.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.32.1",
"README.md": "# RustProxy 0.32.2",
"CHANGELOG.md": "## 0.32.2",
"ROADMAP.md": "## 0.32.2",
"AGENTS.md": "Current source version: 0.32.2.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.32.2",
}.items():
assert marker in (root / release_doc).read_text(), f"stale release identity in {release_doc}"
assert tomllib.loads((root / "rust-toolchain.toml").read_text())["toolchain"]["channel"] == "stable"
@@ -285,7 +285,7 @@ assert 'Opt-in health decisions based on ordinary user traffic. Defaults to disa
assert '"force_ssl"' in openapi and '"forward_client_ip"' in openapi
assert '"backend_header"' in openapi and '"backend_timing_header"' in openapi
assert '"StreamMetrics"' in openapi and '"streams"' in openapi
for marker in ['"version": "0.32.1"', '"/api/traffic"', '"capture_user_agent"', '"capture_country"', '"hot_auto_trim_enabled"', '"hot_auto_trim_after_seconds"', '"hot_auto_trim_target_percent"', '"/api/config/{section}"', '"ConfigSectionUpdate"', '"cold_on_idle"', '"cold_on_age"', '"cold_max_hot_seconds"', '"cold_on_lru"', '"cold_on_shutdown"', '"cold_gc_interval_seconds"', '"cold_gc_max_delete"', '"collapse_enabled"', '"collapse_wait_timeout_ms"', '"collapse_timeout_fallback"', '"stale_if_error_timeout"', '"stale_if_error_connection"', '"stale_if_error_dns"', '"stale_if_error_tls"', '"stale_if_error_statuses"', '"proactive_refresh_enabled"', '"proactive_refresh_before_expiry_seconds"', '"proactive_refresh_percent"', '"proactive_refresh_max_inflight"', '"TransformSettings"', '"TransformOverrides"', '"/api/cache/cold/cleanup"', '"/api/cache/invalidate"', '"/api/cache/bans"', '"/api/cache/bans/{id}"', '"CacheInvalidationRequest"', '"CacheInvalidationResponse"', '"ColdCleanupJob"', '"match_type"', '"regex_insensitive"', '"rewrite"', '"response"', '"covered_hosts"', '"default": true']:
for marker in ['"version": "0.32.2"', '"/api/traffic"', '"capture_user_agent"', '"capture_country"', '"hot_auto_trim_enabled"', '"hot_auto_trim_after_seconds"', '"hot_auto_trim_target_percent"', '"/api/config/{section}"', '"ConfigSectionUpdate"', '"cold_on_idle"', '"cold_on_age"', '"cold_max_hot_seconds"', '"cold_on_lru"', '"cold_on_shutdown"', '"cold_gc_interval_seconds"', '"cold_gc_max_delete"', '"collapse_enabled"', '"collapse_wait_timeout_ms"', '"collapse_timeout_fallback"', '"stale_if_error_timeout"', '"stale_if_error_connection"', '"stale_if_error_dns"', '"stale_if_error_tls"', '"stale_if_error_statuses"', '"proactive_refresh_enabled"', '"proactive_refresh_before_expiry_seconds"', '"proactive_refresh_percent"', '"proactive_refresh_max_inflight"', '"TransformSettings"', '"TransformOverrides"', '"/api/cache/cold/cleanup"', '"/api/cache/invalidate"', '"/api/cache/bans"', '"/api/cache/bans/{id}"', '"CacheInvalidationRequest"', '"CacheInvalidationResponse"', '"ColdCleanupJob"', '"match_type"', '"regex_insensitive"', '"rewrite"', '"response"', '"covered_hosts"', '"default": true']:
assert marker in openapi, marker
for marker in ['"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
assert marker in openapi, marker
@@ -894,7 +894,7 @@ assert 'id="web-service-compression-options"' in html
for marker in [
'function syncCompression()',
'$("web-service-compression-options").hidden = disabled',
'$( `web-service-compression-${name}` ).disabled = disabled',
'$(`web-service-compression-${name}`).disabled = disabled',
'$("web-service-compression").addEventListener("change", syncCompression)',
]:
assert marker in web_service_ui_0270, marker
@@ -1396,7 +1396,7 @@ assert '"error_log" => {' not in admin_0241 and 'request("/api/config/error_log"
assert '"error_log"' in openapi and 'logging, error_log, forwarding' not in openapi
assert 'logging_section_persists_error_log_settings_atomically' in admin_0241 and 'error_log_is_not_a_standalone_configuration_section' in admin_0241
assert 'location.pathname === "/logging"' in settings_js_0241
assert '"RuntimeInformation"' in openapi and '"version": "0.32.1"' in openapi
assert '"RuntimeInformation"' in openapi and '"version": "0.32.2"' in openapi
# 0.24.1 compiler and hostname-cardinality regressions.
tls_0241 = rust_source(root, "src/tls.rs")
+1 -1
View File
@@ -14,7 +14,7 @@ parser = argparse.ArgumentParser()
parser.add_argument('--require-lock', action='store_true', help='Require the post-upgrade Cargo.lock produced by cargo update')
args = parser.parse_args()
manifest = tomllib.loads((ROOT / 'Cargo.toml').read_text())
assert manifest['package']['version'] == '0.32.1'
assert manifest['package']['version'] == '0.32.2'
assert manifest['package']['rust-version'] == '1.99'
deps = manifest['dependencies']
expected_requirements = {
+1 -1
View File
@@ -51,7 +51,7 @@ assert '"/docs/content.json"' in admin
readme = (ROOT / "README.md").read_text(encoding="utf-8")
roadmap = (ROOT / "ROADMAP.md").read_text(encoding="utf-8")
changelog = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8")
assert readme.startswith("# RustProxy 0.32.1")
assert readme.startswith("# RustProxy 0.32.2")
assert "docs/WEB_SERVER.md" in readme and "docs/README.md" in readme
assert readme.count("## What changed in ") == 1, "README duplicates historical release notes; use CHANGELOG/archive"
assert "## Release history" in readme and "docs/ROADMAP_HISTORY.md" in readme
+1 -1
View File
@@ -4,7 +4,7 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
html = (ROOT / 'src/admin/web/index.html').read_text()
assert '0.32.1' in (ROOT / "Cargo.toml").read_text()
assert '0.32.2' in (ROOT / "Cargo.toml").read_text()
assert '<a class="brand" href="/"' in html, 'logo must remain a true full-page navigation'
assert 'addEventListener("click"' not in html.split('<a class="brand" href="/"',1)[1].split('</a>',1)[0]
body = (ROOT / 'src/proxy/cache_helpers.rs').read_text()
+3 -3
View File
@@ -8,9 +8,9 @@ from html.parser import HTMLParser
ROOT = Path(__file__).resolve().parents[2]
def read(rel): return (ROOT / rel).read_text(encoding='utf-8')
assert tomllib.loads(read('Cargo.toml'))['package']['version'] == '0.32.1'
assert 'name = "rust-proxy"\nversion = "0.32.1"' in read('Cargo.lock')
assert json.loads(read('src/admin/openapi.json'))['info']['version'] == '0.32.1'
assert tomllib.loads(read('Cargo.toml'))['package']['version'] == '0.32.2'
assert 'name = "rust-proxy"\nversion = "0.32.2"' in read('Cargo.lock')
assert json.loads(read('src/admin/openapi.json'))['info']['version'] == '0.32.2'
pages = read('src/pages.rs')
for code in (400, 401, 403, 404, 408, 410, 413, 429, 500, 502, 503, 504):
+4 -4
View File
@@ -135,7 +135,7 @@ def upgrade_smoke(tmp: Path) -> None:
assert backup.is_dir()
assert (backup / "src/obsolete.rs").is_file()
assert not (target / "src/obsolete.rs").exists()
assert 'version = "0.32.1"' in (target / "Cargo.toml").read_text(encoding="utf-8")
assert 'version = "0.32.2"' in (target / "Cargo.toml").read_text(encoding="utf-8")
assert (target / "data/keep.txt").read_text(encoding="utf-8") == "preserve\n"
assert (target / ".env").read_text(encoding="utf-8") == "RUST_PROXY_DEMO=0\n"
assert (target / "langs/site.json").is_file()
@@ -146,7 +146,7 @@ def upgrade_smoke(tmp: Path) -> None:
assert (target / "docker-agent/certificate.go").is_file()
assert (target / "docker-agent/server.go").is_file()
manifest = json.loads((backup / "upgrade.json").read_text(encoding="utf-8"))
assert manifest["to_version"] == "0.32.1"
assert manifest["to_version"] == "0.32.2"
assert manifest["removed_obsolete_runtime_state"] is True
assert not legacy.exists()
assert not (backup / "legacy-data").exists()
@@ -617,7 +617,7 @@ def source_contract_smoke() -> None:
assert marker in admin, marker
anonymous_block = admin.split("fn anonymous_allowed", 1)[1].split("const CERTIFICATE_API_KEY_HEADER", 1)[0]
assert '"/api/status"' not in anonymous_block
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.32.1"' in openapi_0241
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.32.2"' in openapi_0241
assert 'id="panel-logging"' not in html
for marker in ['id="settings-tab-logging"', 'id="settings-page-logging"', 'id="logging-form"', 'id="error-log-form"']:
assert marker in html, marker
@@ -1102,7 +1102,7 @@ def source_contract_smoke() -> None:
assert '.managed-update' in main and "flush_hot_to_cold_for_update" in main
openapi = (ROOT / "src/admin/openapi.json").read_text(encoding="utf-8")
assert '"/api/config/history/restore"' in openapi
for marker in ['"version": "0.32.1"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
for marker in ['"version": "0.32.2"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
assert marker in openapi, marker
assert '"software_update"' in openapi and '"update_available"' in openapi
for marker in ['"/api/certificates/deploy"', '"CertificateDeploy"', '"CertificateDeployResult"', '"/api/certificates/reload"']:
+2 -2
View File
@@ -21,7 +21,7 @@ const openapi = json('src/admin/openapi.json');
const en = json('langs/en_US.json').translations;
const pl = json('langs/pl_pl.json').translations;
assert.match(manifest, /version = "0\.32\.1"/);
assert.match(manifest, /version = "0\.32\.2"/);
assert.match(lib, /pub mod user_agent;/);
assert.match(model, /user_agent_blocking: crate::user_agent::Policy/);
assert.match(vhost, /user_agent_blocking: crate::user_agent::Policy/);
@@ -52,7 +52,7 @@ assert.match(fileLogs, /localStorage\.setItem\(CONSOLIDATION_STORAGE_KEY/);
assert.match(fileLogs, /aggregate_min_count/);
assert.match(fileLogs, /file-log-aggregate-copy/);
assert.equal(openapi.info.version, '0.32.1');
assert.equal(openapi.info.version, '0.32.2');
assert.ok(openapi.components.schemas.Config.properties.user_agent_blocking);
assert.ok(openapi.components.schemas.Vhost.properties.user_agent_blocking);
assert.ok(openapi.components.schemas.FileLogSource.properties.group_id);
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python3
"""Rust-independent structural checks; the Rust tests require Cargo."""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
source = (ROOT / 'src/proxy/forward/web_source.rs').read_text()
helper = (ROOT / 'src/proxy/web_cache_revalidation.rs').read_text()
tests = (ROOT / 'src/proxy/web_cache_revalidation/tests.rs').read_text()
assert 'mod web_cache_revalidation;' in (ROOT / 'src/proxy/mod.rs').read_text()
assert 'state.cache.revalidation_tiered(ticket).await' in source
assert '!cached.reusable_for_request(&request_headers)' in source
assert 'web_origin_request_headers(' in source
assert 'revalidation_sent && response.status() == StatusCode::NOT_MODIFIED' in source
assert 'finish_web_revalidation(' in source
assert 'state.cache.discard(ticket)' in source
assert 'cache.publish_revalidated(' in helper
assert 'cache.ticket_current(ticket)' in helper
assert 'cache.discard(ticket)' in helper
assert 'origin_safe' in helper
assert 'cached.apply_validators(&mut headers)' in helper
assert 'headers.remove("if-none-match")' in helper
assert 'headers.remove("if-modified-since")' in helper
for name in (
'browser_refresh_304_preserves_web_server_hit_counter',
'browser_conditional_refresh_returns_304_without_resetting_hits',
'mismatched_browser_validator_cannot_validate_different_cached_object',
'range_and_head_requests_use_validated_cached_body',
'changed_local_file_is_not_falsely_revalidated',
'304_new_private_policy_discards_cached_representation',
):
assert f'async fn {name}()' in tests
print('PASS: RustProxy 0.32.2 Web Server revalidation wiring and Rust regression contracts (static only)')
+5 -2
View File
@@ -136,8 +136,11 @@ assert basic_identity_pos < web_source_pos
assert identity_pos < web_source_pos
assert web_source_pos < proxy.index("let (backend, backend_lease, affinity_cookie)")
web_source = rust_source(ROOT, "src/proxy/forward/web_source.rs")
assert web_source.index("apply_response_rules(") > web_source.index("let origin_headers =")
assert web_source.index("crate::transform::apply(") > web_source.index("apply_response_rules(")
# The 0.32.2 local-304 path also applies response rules, before the normal
# 200-origin processing. Enforce order using the latter's distinct call site.
normal_origin_rules = "apply_response_rules(response.headers_mut(),"
assert web_source.index(normal_origin_rules) > web_source.index("let origin_headers =")
assert web_source.index("crate::transform::apply(") > web_source.index(normal_origin_rules)
need(acl, "ambiguous encoded path character", '"/%2e%2e/secret"', '"/a%2fb"', '"/a%5cb"', '"/bad%"')
need(admin, '"/web-server"', '"web-services"', '"vhost_metrics"')
need((ROOT / "src/admin/panel_assets.rs").read_text(), '"/assets/web-server.js"')