This commit is contained in:
Mateusz Gruszczyński committed 2026-10-13 22:54:52 +02:00
1 parent b0f211555a
commit 8b9ca4db44
39 files changed
+1481 -303

No files matched your search

+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.3', version
assert version == '0.32.4', 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.3' in read('docs/TEST_REPORT.md')
assert '0.32.4' 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.3"
assert manifest["package"]["version"] == "0.32.4"
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.3"' in lock
assert 'name = "rust-proxy"\nversion = "0.32.4"' 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.3",
"CHANGELOG.md": "## 0.32.3",
"ROADMAP.md": "## 0.32.3",
"AGENTS.md": "Current source version: 0.32.3.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.32.3",
"README.md": "# RustProxy 0.32.4",
"CHANGELOG.md": "## 0.32.4",
"ROADMAP.md": "## 0.32.4",
"AGENTS.md": "Current source version: 0.32.4.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.32.4",
}.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.3"', '"/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.4"', '"/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
@@ -1020,7 +1020,7 @@ assert 'html[data-theme="light"] {' in style_source and '--field-bg:' in style_s
assert 'overflow: visible;' in style_source and 'max-height: min(38dvh, 220px)' not in style_source
assert 'body[data-density="compact"] input:not([type="checkbox"]):not([type="radio"])' in style_source
assert 'input:not([type="checkbox"]):not([type="radio"]),\nselect,\ntextarea {' in style_source
assert '#[serde(default = "default_admin_theme")]' in admin_source and 'matches!(preferences.theme.as_str(), "dark" | "ultra_dark" | "light" | "cream")' in admin_source
assert '#[serde(default = "default_admin_theme")]' in admin_source and re.search(r'matches!\(\s*preferences\.theme\.as_str\(\),\s*"dark" \| "ultra_dark" \| "light" \| "cream"\s*\)', admin_source)
# 0.22.3 dashboard/config/cache/log-history regressions.
assert 'bindSettingsForm("pages-form", next => { next.pages = global.read(); }, "pages")' in management_ui
assert '"pages" => {' in admin_source and 'config.pages =' in admin_source
@@ -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.3"' in openapi
assert '"RuntimeInformation"' in openapi and '"version": "0.32.4"' 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.3'
assert manifest['package']['version'] == '0.32.4'
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.3")
assert readme.startswith("# RustProxy 0.32.4")
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.3' in (ROOT / "Cargo.toml").read_text()
assert '0.32.4' 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()
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Browser regression: compact, matching Overview gutters in both directions.
Fixture uses live CSS and the real grid grouping script, but mocked content.
"""
from pathlib import Path
from bs4 import BeautifulSoup
from playwright.sync_api import sync_playwright
root = Path(__file__).resolve().parents[2] / "src/admin/web"
soup = BeautifulSoup((root / "index.html").read_text(), "html.parser")
widgets = soup.select("#dashboard-overview-grid > [data-dashboard-widget]")
assert len(widgets) == 62, len(widgets)
css = (root / "style.css").read_text()
html = ("<!DOCTYPE html><html data-theme='dark' data-dashboard-density='normal'>"
"<head><meta charset='utf-8'><style>" + css + "</style></head>"
"<body><section id='panel-overview' style='width:1680px'>"
"<div id='dashboard-overview-grid' class='stats-grid dashboard-overview-grid'>"
+ "".join(map(str, widgets)) + "</div></section></body></html>")
snapshot = """() => {
const grid = document.getElementById('dashboard-overview-grid');
const style = getComputedStyle(grid);
const parentGap = parseFloat(getComputedStyle(document.body).getPropertyValue('--ui-gap'));
const rows = [];
for (const node of grid.children) {
if (!node.dataset.dashboardWidget || node.hidden) continue;
const rect = node.getBoundingClientRect();
const group = window.rustProxyMasonry.groupFor(node.dataset.dashboardWidget);
let row = rows.find(r => Math.abs(r.top - rect.top) < 0.75);
if (!row) {
row = {top: rect.top, bottom: rect.bottom, group, cards: []};
rows.push(row);
}
row.bottom = Math.max(row.bottom, rect.bottom);
row.cards.push({left: rect.left, right: rect.right, bottom: rect.bottom});
}
return {
rowGap: parseFloat(style.rowGap), columnGap: parseFloat(style.columnGap),
parentGap, margin: style.marginBottom,
rows: rows.sort((a, b) => a.top - b.top)
};
}"""
with sync_playwright() as playwright:
browser = playwright.chromium.launch(
headless=True, executable_path="/usr/bin/chromium", args=["--no-sandbox"])
page = browser.new_page(viewport={"width": 1800, "height": 1100})
page.set_content(html)
page.add_script_tag(content=(root / "app/overview-masonry.js").read_text())
checks = 0
for width in (900, 1220, 1680):
page.evaluate("w => document.getElementById('panel-overview').style.width = w + 'px'", width)
for ui_density, old_gap in (("normal", 5), ("dense", 4), ("comfortable", 13)):
page.evaluate("d => {document.body.dataset.density = d === 'normal' ? '' : d;}", ui_density)
for card_density in ("normal", "compact", "ultra"):
page.evaluate("d => document.documentElement.dataset.dashboardDensity = d", card_density)
data = page.evaluate(snapshot)
expected = max(3, old_gap - 1)
assert abs(data["parentGap"] - old_gap) < 0.01, data
assert abs(data["rowGap"] - expected) < 0.01, data
assert abs(data["columnGap"] - expected) < 0.01, data
assert data["margin"] in ("0px", "0"), data
rows = data["rows"]
assert len(rows) >= 4, (width, ui_density, card_density)
for before, after in zip(rows[:10], rows[1:11]):
distance = after["top"] - before["bottom"]
assert abs(distance - expected) < 1.05, (width, ui_density, card_density, distance, expected)
for row in rows[:10]:
cards = sorted(row["cards"], key=lambda card: card["left"])
assert max(c["bottom"] for c in cards) - min(c["bottom"] for c in cards) < 1.1
for a, b in zip(cards, cards[1:]):
assert abs(b["left"] - a["right"] - expected) < 1.1, (
width, ui_density, card_density, a, b, expected)
first_large = next((i for i, row in enumerate(rows) if row["group"] != 0), None)
assert first_large is not None and first_large > 0
transition = rows[first_large]["top"] - rows[first_large-1]["bottom"]
assert abs(transition - expected) < 1.05, (width, ui_density, card_density, transition)
checks += 1
print(f"PASS: {width}px/{ui_density}: uniform {expected}px gutters, all Dashboard densities")
browser.close()
print(f"PASS: {checks} Chromium layout checks including small-to-large transitions")
@@ -61,7 +61,9 @@ const css=fs.readFileSync('src/admin/web/style.css','utf8');
const aligned = fs.readFileSync('src/admin/web/app/overview-masonry.js', 'utf8');
assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*grid-auto-rows:\s*auto;/);
assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*grid-auto-flow:\s*row;/);
assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*gap:\s*var\(--ui-gap\);/);
assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*--overview-gap:\s*max\(3px, calc\(var\(--ui-gap\) - 1px\)\);/);
assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*gap:\s*var\(--overview-gap\);/);
assert.match(css, /\.dashboard-overview-grid\.is-aligned\.stats-grid\s*\{[^}]*gap:\s*var\(--overview-gap\);/);
assert.match(css, /\.dashboard-overview-grid\.is-aligned\s*\{[^}]*align-items:\s*stretch;/);
assert.match(css, /#panel-overview\s*\{\s*container:\s*overview-canvas \/ inline-size;/);
assert.match(css, /@container overview-canvas \(max-width:\s*700px\)/);
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Verify the security-sensitive transport split without claiming a Rust execution."""
from pathlib import Path
import json
import re
root=Path(__file__).resolve().parents[2]
read=lambda file:(root/file).read_text(encoding='utf-8')
backend=read('src/config/backend.rs')
forward=read('src/proxy/forward/upstream.rs')
proxy=read('src/proxy/types.rs')
client=read('src/upstream/client.rs')
conn=read('src/upstream.rs')
assert not re.search(r'ensure!\([^;]*!backend\.preserve_host',backend)
assert 'preserve_host: backend.preserve_host' in backend
assert 'fn upstream_request_target(' in proxy
assert 'parts.authority = Some(authority)' in proxy
assert 'if strict_h2_request' in proxy and 'headers.remove(HOST)' in proxy
assert 'let Some(upstream_target) = upstream_request_target(' in forward
assert 'retry_headers = request.headers().clone()' in forward
assert 'upstream_request_target(&uri, request.headers_mut(), backend, false, false)' in proxy
assert 'let destination: http::Uri' in client
assert 'let uri = self.destination.clone()' in conn
assert 'fn call(&mut self, _request_uri: Uri)' in conn
assert 'connect_host' in client and 'connect_host' in conn
assert 'policy.check(ip)' in conn
assert '.retry_canceled_requests(false)' in client
assert 'preserve_host_is_valid_with_every_upstream_protocol' in read('src/config/tests.rs')
for test in (
'preserve_host_sets_http2_authority_without_changing_network_origin',
'retry_prepares_authority_for_the_selected_backend_not_the_previous_one',
'invalid_outgoing_host_is_rejected_instead_of_used_for_dialing',
):
assert test in read('src/proxy/tests/authority_tests.rs'),test
for test in (
'strict_h2_preserves_public_authority_while_tcp_and_sni_use_private_origin',
'auto_h1_fallback_preserves_public_host_without_resolving_it',
'auto_h2_alpn_retains_same_public_host_and_pinned_tls_origin',
'cleartext_h2c_preserves_public_authority_and_pins_socket',
):
assert test in read('src/upstream/protocol_tests.rs'),test
css=read('src/admin/web/style.css')
match=re.search(r'#panel-overview \.dashboard-overview-grid\.is-aligned\s*\{([^}]*)\}',css)
assert match
rules=match[1]
for marker in ['margin: 0;', '--overview-gap: max(3px, calc(var(--ui-gap) - 1px));',
'row-gap: var(--overview-gap);', 'column-gap: var(--overview-gap);',
'gap: var(--overview-gap);']:
assert marker in rules, marker
# Regression guard for E0382: the retry builder must not consume the URI
# before upstream_request_target() borrows it to prepare HTTP/2 authority.
retry = re.search(r'fn retry_request\([\s\S]*?\n}\n', proxy)
assert retry and '.uri(uri.clone())' in retry[0]
assert 'upstream_request_target(&uri, request.headers_mut()' in retry[0]
assert json.loads(read('src/admin/openapi.json'))['info']['version']=='0.32.4'
assert 'version = "0.32.4"' in read('Cargo.toml')
print('PASS: 0.32.4 origin pinning, authority, retry, CSS gutter source contracts (not Rust runtime)')
@@ -0,0 +1,42 @@
import fs from 'node:fs';
import vm from 'node:vm';
import assert from 'node:assert/strict';
const ui = fs.readFileSync('src/admin/web/vhosts/core.js', 'utf8');
const head = 'function debugOriginIssues(members) {';
const tail = '\nfunction aclEditor(';
const start = ui.indexOf(head);
const end = ui.indexOf(tail, start);
assert(start >= 0 && end > start, 'debug origin verification helper is present');
const warn = vm.runInNewContext('(' + ui.slice(start, end).trim() + ')', { URL });
function member(protocol, more={}) { return {
url:'https://10.87.20.2:443',protocol,preserve_host:true,
pool_eligible:true,allow_private:true,tls_verify:false,...more,
}; }
for (const protocol of ['http1', 'auto', 'http2', 'h2']) {
assert.equal(warn([member(protocol)]).length, 0,
`${protocol} is accepted without forcing H1 or disabling preserved Host`);
assert.equal(warn([member(protocol, {tls_server_name:'unitraklub.pl',tls_verify:true})]).length, 0,
`${protocol} supports explicitly verified certificate DNS names`);
}
assert.equal(warn([member('h2',{pool_eligible:false,allow_private:false})]).length,0,
'pool and private-origin flags must not be mistaken for protocol requirements');
assert.equal(warn([member('auto',{tls_verify:true})]).length,1,
'HTTPS-by-IP without TLS identity receives a nonblocking warning');
assert.equal(warn([{...member('h2',{tls_verify:true}),url:'https://site.example'}]).length,0,
'DNS HTTPS origin needs no IP-certificate warning');
assert.equal(warn([{...member('h2',{tls_verify:true}),url:'http://10.87.20.2:80'}]).length,0);
assert(!/preserve\.disabled\s*=|preserve\.checked\s*=\s*false/.test(ui),
'Protocol selector may not disable or clear Preserve Host');
assert(ui.includes('value.preserve_host === true'), 'Existing backend preference remains visible');
assert(ui.includes('protocol.value = "http1"; preserve.checked = true'),
'Optional explicit H1 troubleshooting preset is retained');
assert(!ui.includes('syncProtocol()'), 'Do not call removed protocol-only Host limiter');
assert(ui.includes('protocol.dispatchEvent(new Event("change"))'), 'Preset refreshes protocol help text');
for (const lang of ['en_US','pl_pl']) {
const t=JSON.parse(fs.readFileSync(`langs/${lang}.json`,'utf8')).translations;
for (const key of ['vhPreserveHost','vhDebugOriginPreset','vhOriginPresetHelp','vhDebugOriginMismatch'])
assert(t[key] && t[key].length>30,`${lang}.${key}`);
}
console.log('PASS: Preserve Host is available for four modes; IP/TLS hint is conditional and optional');
+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.3'
assert 'name = "rust-proxy"\nversion = "0.32.3"' in read('Cargo.lock')
assert json.loads(read('src/admin/openapi.json'))['info']['version'] == '0.32.3'
assert tomllib.loads(read('Cargo.toml'))['package']['version'] == '0.32.4'
assert 'name = "rust-proxy"\nversion = "0.32.4"' in read('Cargo.lock')
assert json.loads(read('src/admin/openapi.json'))['info']['version'] == '0.32.4'
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.3"' in (target / "Cargo.toml").read_text(encoding="utf-8")
assert 'version = "0.32.4"' 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.3"
assert manifest["to_version"] == "0.32.4"
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.3"' in openapi_0241
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.32.4"' 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.3"', '"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.4"', '"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\.3"/);
assert.match(manifest, /version = "0\.32\.4"/);
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.3');
assert.equal(openapi.info.version, '0.32.4');
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);
+7 -7
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Offline source contracts for RustProxy 0.32.3 upstream protocols and card registry.
"""Offline source contracts for RustProxy 0.32.4 upstream protocols and card registry.
These assertions supplement, rather than replace, Rust ALPN, H2C and websocket
runtime tests. No code execution against a RustProxy server is implied.
@@ -64,9 +64,9 @@ assert 'is_https && backend.protocol == UpstreamProtocol::Http2' in client
assert 'let protocol = if websocket {' in client and 'UpstreamProtocol::Http1' in client
assert 'if !is_https && protocol == UpstreamProtocol::Auto' in client
assert 'UpstreamProtocol::Http1 => builder.enable_http1().wrap_connector(http)' in client
assert 'UpstreamProtocol::Http2 | UpstreamProtocol::H2 => builder.enable_http2().wrap_connector(http)' in client
assert re.search(r'UpstreamProtocol::Http2 \| UpstreamProtocol::H2 => \{?\s*builder\.enable_http2\(\)\.wrap_connector\(http\)',client)
assert 'UpstreamProtocol::Auto => builder.enable_http1().enable_http2().wrap_connector(http)' in client
assert '.http2_only(matches!(protocol, UpstreamProtocol::Http2 | UpstreamProtocol::H2))' in client
assert re.search(r'\.http2_only\(matches!\(\s*protocol,\s*UpstreamProtocol::Http2 \| UpstreamProtocol::H2\s*\)\s*\)',client)
assert 'backend.origin.starts_with("http://")' in read('src/proxy/types.rs')
assert 'UpstreamProtocol::Http2 | crate::config::UpstreamProtocol::H2' in read('src/proxy/forward/upstream.rs')
assert '.retry_canceled_requests(false)' in client
@@ -141,7 +141,7 @@ for field in ('dashboard_widgets', 'dashboard_hidden_widgets'):
assert '"version": "0.32.3"' in read('src/admin/openapi.json')
assert 'version = "0.32.3"' in read('Cargo.toml')
assert 'version = "0.32.3"' in read('Cargo.lock')
print(f'PASS: 0.32.3 upstream H1/Auto/compatible-HTTP2/strict-H2 source contracts and {len(ids)} registered Overview Cards')
assert '"version": "0.32.4"' in read('src/admin/openapi.json')
assert 'version = "0.32.4"' in read('Cargo.toml')
assert 'version = "0.32.4"' in read('Cargo.lock')
print(f'PASS: 0.32.4 upstream H1/Auto/compatible-HTTP2/strict-H2 source contracts and {len(ids)} registered Overview Cards')