v0.32.6_fix1

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-14 08:19:09 +02:00
1 parent a3d183bd4b
commit e3de084149
34 files changed
+559 -85

No files matched your search

@@ -0,0 +1,54 @@
// Verifies real-status-to-badge mapping without guessing a configured Auto result.
import assert from 'node:assert/strict';
import fs from 'node:fs';
import vm from 'node:vm';
const source = fs.readFileSync('src/admin/web/vhosts/render.js', 'utf8');
const block = source.slice(source.indexOf('const VHOST_PROTOCOL_FRESH_MS = '), source.indexOf('function render() {'));
assert(block.includes('function renderUpstreamProtocolBadges(status)'));
const rustClient = fs.readFileSync('src/upstream/client.rs', 'utf8');
const rustPool = fs.readFileSync('src/balance/pool_view.rs', 'utf8');
assert(rustClient.includes('self.inner.observe_protocol(value.version())'));
assert(rustClient.includes('let response = client.request(request).await;'));
for (const name of ['upstream_protocol_observed','upstream_protocol_last_seen_ms','upstream_protocol_http1_total','upstream_protocol_http2_total']) {
assert(rustPool.includes(`"${name}"`), name);
}
const en = JSON.parse(fs.readFileSync('langs/en_US.json', 'utf8')).translations;
const pl = JSON.parse(fs.readFileSync('langs/pl_pl.json', 'utf8')).translations;
for(const key of ['vhBackendProtocolRecent','vhBackendProtocolUnobserved']) assert(en[key] && pl[key]);
const badge = (vhost, backend) => ({dataset:{upstreamProtocolVhost:vhost,upstreamProtocolBackend:backend},textContent:'',title:''});
const visible = [badge('api','auto-backend'),badge('api','h1-backend'),badge('other','auto-backend')];
const envelope = {config:{vhosts:[
{id:'api',backends:[{id:'auto-backend',protocol:'auto'},{id:'h1-backend',protocol:'http1'}]},
{id:'other',backends:[{id:'auto-backend',protocol:'auto'}]},
]}};
const ctx = {Date, Number, String, Map, envelope, document:{querySelectorAll:()=>visible},t:key=>en[key]};
vm.createContext(ctx);
vm.runInContext(block, ctx);
const recent = Date.now() - 2000;
let status = {backend_pools:[{id:'api',members:[
{id:'auto-backend',upstream_protocol_observed:'h2',upstream_protocol_last_seen_ms:recent,upstream_protocol_http1_total:5,upstream_protocol_http2_total:9},
{id:'h1-backend',upstream_protocol_observed:'h1',upstream_protocol_last_seen_ms:recent,upstream_protocol_http1_total:12},
]}]};
ctx.renderUpstreamProtocolBadges(status);
assert.equal(visible[0].textContent,'auto-backend · AUTO → H2');
assert.equal(visible[0].dataset.observed,'true');
assert(visible[0].title.includes('H1: 5; H2: 9'));
assert.equal(visible[1].textContent,'h1-backend · H1 → H1');
assert.equal(visible[2].textContent,'auto-backend · AUTO → ?');
assert.equal(visible[2].dataset.observed,'false');
// Old/stale response must not masquerade as a recent ALPN observation.
status.backend_pools[0].members[0].upstream_protocol_last_seen_ms = Date.now() - 3600000;
ctx.renderUpstreamProtocolBadges(status);
assert.equal(visible[0].textContent,'auto-backend · AUTO → ?');
assert.equal(visible[0].dataset.observed,'false');
assert(visible[0].title.startsWith(en.vhBackendProtocolUnobserved));
// Fresh data for a similarly named backend from another vhost must not leak.
ctx.renderUpstreamProtocolBadges({backend_pools:[{id:'other',members:[
{id:'auto-backend',upstream_protocol_observed:'h1',upstream_protocol_last_seen_ms:recent}
]}]});
assert.equal(visible[0].textContent,'auto-backend · AUTO → ?');
assert.equal(visible[2].textContent,'auto-backend · AUTO → H1');
ctx.renderUpstreamProtocolBadges({backend_pools:[]});
assert.equal(visible[2].textContent,'auto-backend · AUTO → ?');
console.log('PASS: observed Auto/H1/H2 badges, freshness, cross-vhost isolation and unknown data');
+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.5', version
assert version == '0.32.6', 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.5' in read('docs/TEST_REPORT.md')
assert '0.32.6' 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).')
+9 -9
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.5"
assert manifest["package"]["version"] == "0.32.6"
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.5"' in lock
assert 'name = "rust-proxy"\nversion = "0.32.6"' 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.5",
"CHANGELOG.md": "## 0.32.5",
"ROADMAP.md": "## 0.32.5",
"AGENTS.md": "Current source version: 0.32.5.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.32.5",
"README.md": "# RustProxy 0.32.6",
"CHANGELOG.md": "## 0.32.6",
"ROADMAP.md": "## 0.32.6",
"AGENTS.md": "Current source version: 0.32.6.",
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.32.6",
}.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.5"', '"/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.6"', '"/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
@@ -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.5"' in openapi
assert '"RuntimeInformation"' in openapi and '"version": "0.32.6"' 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.5'
assert manifest['package']['version'] == '0.32.6'
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.5")
assert readme.startswith("# RustProxy 0.32.6")
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
+3 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Static source contract for DDNS ACL 0.32.5; NOT a Rust test."""
"""Static source contract for DDNS ACL 0.32.6; NOT a Rust test."""
from pathlib import Path
import json
import tomllib
@@ -25,8 +25,8 @@ checks = {
'visual selector and disabled DNS deny': '.acl-profile-rule-type' in js and 'aclDnsAllowOnly' in js,
'old network field persisted': 'network: normalizeNetwork(' in js,
'PL/EN translations': all(k in json.loads(read(f'langs/{loc}.json'))['translations'] for loc in ('en_US','pl_pl') for k in ('aclRuleType','aclRuleDns','aclDnsHelp','aclDnsAllowOnly')),
'package version': tomllib.loads(read('Cargo.toml'))['package']['version'] == '0.32.5',
'package version': tomllib.loads(read('Cargo.toml'))['package']['version'] == '0.32.6',
}
for name, ok in checks.items():
if not ok: raise AssertionError(name)
print(f'PASS: {len(checks)} DNS ACL 0.32.5 static source checks (not compiled or network-tested)')
print(f'PASS: {len(checks)} DNS ACL 0.32.6 static source checks (not compiled or network-tested)')
+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.5' in (ROOT / "Cargo.toml").read_text()
assert '0.32.6' 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()
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""Source-only mobile/observed-backend UI invariants, no browser required."""
from pathlib import Path
root=Path(__file__).resolve().parents[2]
css=(root/'src/admin/web/style.css').read_text()
preferences=(root/'src/admin/web/app/preferences.js').read_text()
masonry=(root/'src/admin/web/app/overview-masonry.js').read_text()
html=(root/'src/admin/web/index.html').read_text()
render=(root/'src/admin/web/vhosts/render.js').read_text()
client=(root/'src/upstream/client.rs').read_text()
pool=(root/'src/balance/pool_view.rs').read_text()
assert 'touch-action: pan-y pinch-zoom' in css
assert 'overflow-anchor: none' in css
assert 'grid-template-columns: repeat(2, minmax(0, 1fr))' in css
assert '@container overview-canvas (max-width: 700px)' in css
assert 'overview-canvas (max-width: 440px)' not in css
assert '.vhost-backend-protocol-badge' in css
assert '.vhost-card' in css[css.index('/* Global shape preference.'):]
assert '.draft-dock,' not in css[css.index('/* Global shape preference.'):]
assert 'grid.append(...orderedNodes)' in preferences
assert 'orderedNodes.some((node, index) => node !== currentNodes[index])' in preferences
assert 'if (ordered.some((node, i) => node !== previous[i])) grid.append(...ordered);' in masonry
assert html.count('data-dashboard-widget=') >= 62
assert 'function renderUpstreamProtocolBadges(status)' in render
assert 'const VHOST_PROTOCOL_FRESH_MS = 5 * 60 * 1000' in render
assert 'self.inner.observe_protocol(value.version())' in client
assert '"upstream_protocol_observed"' in pool
print('PASS: mobile grid, shape, stable reorder and observed backend protocol source contracts')
+123
View File
@@ -0,0 +1,123 @@
#!/usr/bin/env python3
"""Mobile Dashboard scroll/layout and appearance browser regression.
Uses the shipped static web assets in a Chromium touch-device emulation, not
physical Safari, the authenticated server or a running Rust backend.
"""
from pathlib import Path
import json
import shutil
from bs4 import BeautifulSoup
from playwright.sync_api import sync_playwright
ROOT = Path(__file__).resolve().parents[2]
WEB = ROOT / 'src/admin/web'
soup = BeautifulSoup((WEB / 'index.html').read_text(), 'html.parser')
widgets = soup.select('#dashboard-overview-grid > [data-dashboard-widget]')
assert len(widgets) == 62
widget_ids = [widget['data-dashboard-widget'] for widget in widgets]
css = (WEB / 'style.css').read_text()
source = (WEB / 'app/preferences.js').read_text()
layout_code = source.split('function applyDashboardLayout(order, hidden) {')[1].split('function dashboardWidgetLabel(')[0]
html = ('<!doctype html><html data-theme="ultra_dark" data-dashboard-density="compact" '
'data-corner-style="default"><head><meta name="viewport" '
'content="width=device-width,initial-scale=1"><style>' + css +
'</style></head><body style="margin:0">'
'<div style="height:80px">Mobile navigation</div>'
'<section id="panel-overview" style="width:auto;padding:0 10px;box-sizing:border-box">'
'<div id="dashboard-overview-grid" class="stats-grid dashboard-overview-grid">' +
''.join(map(str, widgets)) + '</div></section>'
'<div id="radii" style="padding:10px"><article class="vhost-card" id="vhost-test">Vhost</article>'
'<div class="entity" id="entity-test">Entity</div>'
'<span class="vhost-backend-protocol-badge" id="badge-test">AUTO → H2</span></div>'
'</body></html>')
with sync_playwright() as browser_tool:
chromium = browser_tool.chromium.launch(headless=True,
executable_path=shutil.which('chromium') or shutil.which('chromium-browser'),
args=['--no-sandbox'])
context = chromium.new_context(viewport={'width': 393, 'height': 844},
device_scale_factor=3, is_mobile=True, has_touch=True)
page = context.new_page()
page.set_content(html, wait_until='domcontentloaded')
page.add_script_tag(content=(WEB / 'app/overview-masonry.js').read_text())
preference_fixture = (
'const ADMIN_DASHBOARD_WIDGETS=' + json.dumps(widget_ids) + ';' +
'const ADMIN_DEFAULT_HIDDEN_WIDGETS=[];' +
'const latestStatus=null; const currentPanel="overview";' +
'const normalizeDashboardWidgets=(ids)=>ids;' +
'const normalizeHiddenDashboardWidgets=(ids)=>ids;' +
'function applyDashboardLayout(order, hidden) {' + layout_code
)
page.add_script_tag(content=preference_fixture)
for width in (320, 375, 393, 402, 430, 550):
page.set_viewport_size({'width': width, 'height': 844})
snapshot = page.evaluate('''() => {
const grid=document.getElementById('dashboard-overview-grid');
const cards=[...grid.querySelectorAll('[data-dashboard-widget].stat')]
.filter(node => !node.hidden).slice(0,6).map(node => {
const r=node.getBoundingClientRect();
return {left:r.left,top:r.top,right:r.right,bottom:r.bottom,width:r.width};
});
return {columns:getComputedStyle(grid).gridTemplateColumns.split(' ').length,
cards,gridWidth:grid.clientWidth,gridScroll:grid.scrollWidth,
screenScroll:document.documentElement.scrollWidth,
screen:innerWidth,
touch:getComputedStyle(grid.querySelector('.stat')).touchAction};
}''')
a,b,c,d,e,f = snapshot['cards']
assert snapshot['columns'] == 2, (width, snapshot)
assert abs(a['top']-b['top']) < 1 and abs(c['top']-d['top']) < 1
assert abs(e['top']-f['top']) < 1 and c['top'] > a['top'] and e['top'] > c['top']
assert b['left'] >= a['right'] and d['left'] >= c['right']
assert a['width'] > 50 and b['width'] > 50
assert snapshot['gridScroll'] <= snapshot['gridWidth']+2, (width,snapshot)
assert snapshot['screenScroll'] <= snapshot['screen']+2, (width,snapshot)
assert 'pan-y' in snapshot['touch']
action = page.evaluate('''() => {
const tile=document.querySelector('.stat-with-action');
const label=tile.querySelector(':scope > span').getBoundingClientRect();
const button=tile.querySelector('.stat-corner-action').getBoundingClientRect();
const value=tile.querySelector('strong').getBoundingClientRect();
const card=tile.getBoundingClientRect();
return {labelBottom:label.bottom,buttonTop:button.top,
buttonBottom:button.bottom,valueTop:value.top,
buttonRight:button.right,cardRight:card.right};
}''')
assert action['buttonTop'] >= action['labelBottom'] - 2, (width,action)
assert action['valueTop'] >= action['buttonBottom'] - 2, (width,action)
assert action['buttonRight'] <= action['cardRight'] + 1, (width,action)
page.evaluate('window.scrollTo(0,350)')
before = page.evaluate('window.scrollY')
assert before > 0, width
page.evaluate('''() => {
const grid=document.getElementById('dashboard-overview-grid');
window.rustProxyMasonry.refresh();
for(const [index,node] of [...grid.querySelectorAll('.stat strong')].entries()) {
node.textContent=index%2 ? '26216' : '49,750';
}
}''')
after = page.evaluate('window.scrollY')
assert abs(after - before) <= 2, (width, before, after)
# Reapplying an unchanged preference must NOT reparent the grid children.
moves = page.evaluate('''() => {
const grid=document.getElementById('dashboard-overview-grid');
const order=[...grid.children].map(n=>n.dataset.dashboardWidget);
let moves=0;
const observer=new MutationObserver(items => { moves+=items.filter(i=>i.type==='childList').length; });
observer.observe(grid,{childList:true});
applyDashboardLayout(order,[]);
observer.takeRecords().forEach(i=>{if(i.type==='childList')moves++});
observer.disconnect();
return moves;
}''')
assert moves == 0, (width, moves)
assert abs(page.evaluate('window.scrollY')-before) <= 2
print(f'PASS: {width}px touch emulation, two metric columns, no overflow or scroll jumps')
for style,surface in (('square','0px'), ('subtle','5px'), ('rounded','12px'), ('extra','18px')):
page.evaluate('(s) => document.documentElement.dataset.cornerStyle=s', style)
for selector in ('#vhost-test','#entity-test','#badge-test','.stat'):
radius = page.locator(selector).first.evaluate('node=>getComputedStyle(node).borderTopLeftRadius')
assert radius == surface, (style,selector,radius)
print('PASS: vhost, entity, backend badge and metric corner presets')
chromium.close()
@@ -147,7 +147,7 @@ with sync_playwright() as playwright:
cases=[(1720,0,1,'normal',12),(1350,180,1,'compact',12),
(1200,260,1,'normal',6),(900,0,1,'compact',6),
(900,190,1,'normal',6),(720,190,1,'ultra',2),
(620,0,1,'compact',2),(430,0,1,'ultra',1),
(620,0,1,'compact',2),(430,0,1,'ultra',2),
(1140,200,.9,'compact',6),(1140,200,1.1,'normal',6),
(1140,200,1.3,'normal',6)]
for width,sidebar,zoom,density,max_columns in cases:
+3 -3
View File
@@ -51,6 +51,6 @@ for marker in ['margin: 0;', '--overview-gap: max(3px, calc(var(--ui-gap) - 1px)
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.5'
assert 'version = "0.32.5"' in read('Cargo.toml')
print('PASS: 0.32.5 origin pinning, authority, retry, CSS gutter source contracts (not Rust runtime)')
assert json.loads(read('src/admin/openapi.json'))['info']['version']=='0.32.6'
assert 'version = "0.32.6"' in read('Cargo.toml')
print('PASS: 0.32.6 origin pinning, authority, retry, CSS gutter source contracts (not Rust runtime)')
+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.5'
assert 'name = "rust-proxy"\nversion = "0.32.5"' in read('Cargo.lock')
assert json.loads(read('src/admin/openapi.json'))['info']['version'] == '0.32.5'
assert tomllib.loads(read('Cargo.toml'))['package']['version'] == '0.32.6'
assert 'name = "rust-proxy"\nversion = "0.32.6"' in read('Cargo.lock')
assert json.loads(read('src/admin/openapi.json'))['info']['version'] == '0.32.6'
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.5"' in (target / "Cargo.toml").read_text(encoding="utf-8")
assert 'version = "0.32.6"' 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.5"
assert manifest["to_version"] == "0.32.6"
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.5"' in openapi_0241
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.32.6"' 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.5"', '"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.6"', '"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\.5"/);
assert.match(manifest, /version = "0\.32\.6"/);
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.5');
assert.equal(openapi.info.version, '0.32.6');
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);
+5 -5
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Offline source contracts for RustProxy 0.32.5 upstream protocols and card registry.
"""Offline source contracts for RustProxy 0.32.6 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.
@@ -141,7 +141,7 @@ for field in ('dashboard_widgets', 'dashboard_hidden_widgets'):
assert '"version": "0.32.5"' in read('src/admin/openapi.json')
assert 'version = "0.32.5"' in read('Cargo.toml')
assert 'version = "0.32.5"' in read('Cargo.lock')
print(f'PASS: 0.32.5 upstream H1/Auto/compatible-HTTP2/strict-H2 source contracts and {len(ids)} registered Overview Cards')
assert '"version": "0.32.6"' in read('src/admin/openapi.json')
assert 'version = "0.32.6"' in read('Cargo.toml')
assert 'version = "0.32.6"' in read('Cargo.lock')
print(f'PASS: 0.32.6 upstream H1/Auto/compatible-HTTP2/strict-H2 source contracts and {len(ids)} registered Overview Cards')