v0.32.4
This commit is contained in:
1 parent
b0f211555a
commit
8b9ca4db44
39 files changed
+1481
-303
No files matched your search
@@ -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).')
|
||||
@@ -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")
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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\)/);
|
||||
|
||||
@@ -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');
|
||||
@@ -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):
|
||||
|
||||
@@ -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"']:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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')
|
||||
Reference in new issue
Block a user