#!/usr/bin/env python3 """Offline release checks that do not require Rust/Cargo or network access. Exercises the shipped SQLite migration/backup and stopped-source upgrade path, then checks the 0.24.1 shared-statistics/runtime/settings contracts and retained legacy-WebSocket and retained control-plane/cache-UX/live-log contracts plus retained transport contracts. """ from __future__ import annotations import hashlib import json import os import re from pathlib import Path import sqlite3 import subprocess import tempfile import tomllib import zipfile import backup as backup_module import deploy import migrate_npm import upgrade as upgrade_module ROOT = Path(__file__).resolve().parents[1] def read_cache_source() -> str: return "\n".join(path.read_text(encoding="utf-8") for path in sorted((ROOT / "src/cache").glob("*.rs"))) def sql_and_backup_smoke(tmp: Path) -> None: data = tmp / "data" data.mkdir(mode=0o700, parents=True) db = data / "config.sqlite3" connection = sqlite3.connect(db) try: connection.executescript((ROOT / "migrations/0002_admin.sql").read_text(encoding="utf-8")) connection.executescript((ROOT / "migrations/0003_certificate_renewals.sql").read_text(encoding="utf-8")) document = json.dumps({"listeners": {"endpoints": []}, "vhosts": []}) connection.execute( "INSERT INTO configuration(id, revision, document) VALUES(1, 4, ?)", (document,), ) connection.execute( "INSERT INTO administrator(id,username,password_hash,generation,created_at) VALUES(1,'owner','test-hash',1,1)" ) connection.execute( "INSERT INTO certificate_renewals(certificate_id,last_checked,next_check,last_renewed,not_after,failures,status,message) VALUES(?,?,?,?,?,?,?,?)", ("site-cert", 100, 200, 50, 500, 1, "retry", "saved observation"), ) connection.execute("PRAGMA user_version = 3") connection.commit() # Simulate additive startup migrations through the current schema-v7. connection.executescript((ROOT / "migrations/0004_admin_preferences.sql").read_text(encoding="utf-8")) connection.executescript((ROOT / "migrations/0005_certificate_api.sql").read_text(encoding="utf-8")) connection.executescript((ROOT / "migrations/0006_certificate_api_credentials.sql").read_text(encoding="utf-8")) connection.executescript((ROOT / "migrations/0007_secret_store.sql").read_text(encoding="utf-8")) connection.execute( "INSERT INTO admin_preferences(administrator_id,document) VALUES(1,?)", (json.dumps({"live_log_columns": ["time", "status"]}),), ) connection.execute("PRAGMA user_version = 7") connection.commit() assert connection.execute("PRAGMA quick_check").fetchone()[0] == "ok" tables = { row[0] for row in connection.execute( "SELECT name FROM sqlite_master WHERE type='table'" ) } for table in ("configuration", "administrator", "admin_sessions", "admin_audit", "auth_throttle", "certificate_renewals", "admin_preferences", "certificate_api_credential", "certificate_api_credentials", "secret_store"): assert table in tables, table finally: connection.close() os.chmod(db, 0o600) destination = tmp / "backup" copied = backup_module.backup(data, destination) assert copied.is_file() metadata = json.loads((destination / "backup.json").read_text(encoding="utf-8")) assert metadata["project"] == "RustProxy" assert metadata["schema"] == 7 assert metadata["configuration_revision"] == 4 restored = sqlite3.connect(copied) try: assert restored.execute("PRAGMA quick_check").fetchone()[0] == "ok" assert restored.execute("SELECT revision FROM configuration WHERE id=1").fetchone()[0] == 4 assert restored.execute("SELECT status, message FROM certificate_renewals WHERE certificate_id='site-cert'").fetchone() == ("retry", "saved observation") assert json.loads(restored.execute("SELECT document FROM admin_preferences WHERE administrator_id=1").fetchone()[0]) == {"live_log_columns": ["time", "status"]} assert restored.execute("SELECT username FROM administrator WHERE id=1").fetchone()[0] == "owner" finally: restored.close() def upgrade_smoke(tmp: Path) -> None: target = tmp / "installed" target.mkdir(parents=True) (target / "Cargo.toml").write_text( '[package]\nname = "rust-proxy"\nversion = "0.8.1"\n', encoding="utf-8" ) (target / "src").mkdir() (target / "src/obsolete.rs").write_text("obsolete\n", encoding="utf-8") (target / "langs").mkdir() custom_pack = {"meta": {"code": "zz_ZZ", "name": "Site test"}, "strings": {}} (target / "langs/site.json").write_text(json.dumps(custom_pack), encoding="utf-8") (target / "data").mkdir(mode=0o700) (target / "data/keep.txt").write_text("preserve\n", encoding="utf-8") legacy = target / "data/certs/renewals.json" legacy.parent.mkdir(parents=True) legacy.write_text('{"legacy":true}\n', encoding="utf-8") (target / ".env").write_text("RUST_PROXY_DEMO=0\n", encoding="utf-8") backup = upgrade_module.upgrade(ROOT, target, stopped=True) assert backup.is_dir() assert (backup / "src/obsolete.rs").is_file() assert not (target / "src/obsolete.rs").exists() assert 'version = "0.24.9"' 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() assert (target / "docker-agent/main.go").is_file() manifest = json.loads((backup / "upgrade.json").read_text(encoding="utf-8")) assert manifest["to_version"] == "0.24.9" assert manifest["removed_obsolete_runtime_state"] is True assert not legacy.exists() assert not (backup / "legacy-data").exists() # Legacy cleanup must never follow a symlinked certs directory outside the data root. unsafe = tmp / "unsafe-installed" unsafe.mkdir() (unsafe / "Cargo.toml").write_text('[package]\nname = "rust-proxy"\nversion = "0.8.1"\n', encoding="utf-8") (unsafe / "src").mkdir(); (unsafe / "src/old.rs").write_text("old\n", encoding="utf-8") (unsafe / "data").mkdir() outside = tmp / "outside-certs"; outside.mkdir() outside_legacy = outside / "renewals.json"; outside_legacy.write_text('{"outside":true}\n', encoding="utf-8") (unsafe / "data/certs").symlink_to(outside, target_is_directory=True) try: upgrade_module.upgrade(ROOT, unsafe, stopped=True) except ValueError as error: assert "must not be a symlink" in str(error) else: raise AssertionError("symlinked certs directory was accepted during legacy cleanup") assert outside_legacy.read_text(encoding="utf-8") == '{"outside":true}\n' assert 'version = "0.8.1"' in (unsafe / "Cargo.toml").read_text(encoding="utf-8") assert (unsafe / "src/old.rs").is_file() def deployment_gate_smoke(tmp: Path) -> None: assert deploy.admin_address("127.0.0.1:9090") == ("127.0.0.1:9090", False) assert deploy.admin_address("0.0.0.0:9090", True) == ("0.0.0.0:9090", True) try: deploy.admin_address("0.0.0.0:9090", False) except ValueError: pass else: raise AssertionError("remote administration bind accepted without --allow-remote-admin") assert deploy.admin_address("192.0.2.10:9090", True) == ("192.0.2.10:9090", True) remote_unit = deploy.service_unit({"prefix":"/opt/rustproxy", "data_dir":"/var/lib/rustproxy", "user":"rustproxy", "group":"rustproxy", "admin":"0.0.0.0:9090", "remote_admin":True, "http":"0.0.0.0:80", "https":"0.0.0.0:443"}) assert "ExecStart=/opt/rustproxy/current/rust-proxy --data-dir /var/lib/rustproxy --admin-listen 0.0.0.0:9090 serve --proxy-listen 0.0.0.0:80 --tls-listen 0.0.0.0:443 --allow-remote-admin" in remote_unit assert "ReadWritePaths=/var/lib/rustproxy /var/log/rustproxy" in remote_unit assert "TimeoutStopSec=120" in remote_unit deploy_source = (ROOT / "scripts/deploy.py").read_text(encoding="utf-8") assert "def stop_for_managed_update" in deploy_source and ".managed-update" in deploy_source assert "systemctl', 'stop', SERVICE" in deploy_source assert "def ensure_service_data_access" in deploy_source assert "status=200/CHDIR" in deploy_source assert "runuser', '-u', account.pw_name, '--', 'test', '-x'" in deploy_source assert deploy_source.index("ensure_service_data_access(data, account)") < deploy_source.index("stop_for_managed_update(data)") assert deploy_source.index("stop_for_managed_update(data)") < deploy_source.index("backup = backup_stopped(record, prefix)") data = tmp / "admin-health" data.mkdir(parents=True) database = data / "config.sqlite3" connection = sqlite3.connect(database) connection.execute("CREATE TABLE configuration(id INTEGER PRIMARY KEY, revision INTEGER NOT NULL, document TEXT NOT NULL)") connection.execute( "INSERT INTO configuration VALUES(1, 7, ?)", (json.dumps({"administration": {"listen": "0.0.0.0:9443", "tls": True, "allowed_hosts": ["panel.example.test"]}}),), ) connection.commit(); connection.close() record = {"admin": "127.0.0.1:9090", "data_dir": str(data)} assert deploy.admin_health_endpoint(record) == ("127.0.0.1:9443", True, "panel.example.test:9443") def npm_migration_smoke(tmp: Path) -> None: database = tmp / "npm.sqlite" connection = sqlite3.connect(database) connection.execute( "CREATE TABLE proxy_host(" "id INTEGER, domain_names TEXT, forward_scheme TEXT, forward_host TEXT, forward_port INTEGER, " "allow_websocket_upgrade INTEGER, http2_support INTEGER, enabled INTEGER, certificate_id INTEGER, " "ssl_forced INTEGER, access_list_id INTEGER, advanced_config TEXT, locations TEXT, " "caching_enabled INTEGER, block_exploits INTEGER, meta TEXT)" ) connection.execute( "INSERT INTO proxy_host VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (12, json.dumps(["App.Example.test", "*.apps.example.test"]), "http", "10.20.30.40", 8080, 1, 1, 1, 9, 0, 4, "proxy_read_timeout 90s;", "[]", 1, 0, "{}"), ) connection.execute( "INSERT INTO proxy_host VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (13, json.dumps(["secure.example.test"]), "http", "10.20.30.41", 8081, 1, 1, 1, 10, 1, 0, "", "[]", 0, 0, "{}"), ) connection.commit(); connection.close() rows = migrate_npm.load_npm_hosts(database) config = { "listeners": {"endpoints": [ {"id": "public-http", "enabled": True, "binding": {"address": "0.0.0.0:80", "tls": False}}, {"id": "public-https", "enabled": True, "binding": {"address": "0.0.0.0:443", "tls": True}}, ]}, "vhosts": [], } merged, decisions = migrate_npm.build_migration(rows, config) assert len(decisions) == 2 and all(item.action == "import" for item in decisions) migrated = merged["vhosts"][0] assert migrated["frontend"]["hosts"] == ["app.example.test", "*.apps.example.test"] assert migrated["frontend"]["listener_ids"] == ["public-http", "public-https"] assert migrated["backends"][0]["url"] == "http://10.20.30.40:8080" assert migrated["backends"][0]["allow_private"] is True assert migrated["websockets"] is True assert migrated["force_ssl"] is False assert migrated["forward_client_ip"] is True assert any("access_list_id=4" in warning for warning in decisions[0].warnings or []) assert any("advanced_config" in warning for warning in decisions[0].warnings or []) secure = merged["vhosts"][1] assert secure["force_ssl"] is True assert secure["frontend"]["listener_ids"] == ["public-http", "public-https"] assert any("Force SSL" in warning for warning in decisions[1].warnings or []) def ui_contract_smoke() -> None: html = (ROOT / "src/admin/web/index.html").read_text(encoding="utf-8") app = (ROOT / "src/admin/web/app.js").read_text(encoding="utf-8") settings = (ROOT / "src/admin/web/settings.js").read_text(encoding="utf-8") vhosts = (ROOT / "src/admin/web/vhosts.js").read_text(encoding="utf-8") streams = (ROOT / "src/admin/web/streams.js").read_text(encoding="utf-8") redirects = (ROOT / "src/admin/web/redirects.js").read_text(encoding="utf-8") host404 = (ROOT / "src/admin/web/host404.js").read_text(encoding="utf-8") integrations = (ROOT / "src/admin/web/integrations.js").read_text(encoding="utf-8") acme_ui = (ROOT / "src/admin/web/acme.js").read_text(encoding="utf-8") cache = (ROOT / "src/admin/web/cache-panel.js").read_text(encoding="utf-8") transform_panel = (ROOT / "src/admin/web/transform-panel.js").read_text(encoding="utf-8") css = (ROOT / "src/admin/web/style.css").read_text(encoding="utf-8") required_html = [ 'id="settings-tab-logging"', 'id="settings-page-logging"', 'id="logging-form"', 'id="settings-tab-administration"', 'id="settings-tab-tls-policy"', 'id="admin-tls"', 'id="tls-policy-12"', 'id="tls-policy-13"', 'id="cache-resident-age-header"', 'id="cache-cold-enabled"', 'id="cache-transient-memory"', 'id="cache-cold-queue-bytes"', 'id="cache-eviction-limit"', 'data-cache-memory-preset="recommended"', 'id="cache-cold-path"', 'id="cache-cold-bytes"', 'id="cache-cold-entries"', 'id="cache-cold-after"', 'id="log-format-preset"', 'id="log-format-open"', 'id="logging-format-dialog"', 'id="logging-field-rows"', 'id="panel-acl-profiles"', 'id="acl-profile-form"', 'id="acl-profile-list"', 'id="acl-profile-rule-rows"', 'id="acl-profile-rule-add"', 'id="acl-profile-import-rules"', 'id="vhost-search"', 'id="panel-redirects"', 'id="redirect-search"', 'id="redirect-list"', 'id="redirect-new"', 'id="redirect-dialog"', 'id="redirect-form"', 'id="redirect-rules"', 'id="redirect-rule-add"', 'id="panel-notfound"', 'id="notfound-search"', 'id="notfound-list"', 'id="notfound-new"', 'id="notfound-dialog"', 'id="notfound-form"', 'id="notfound-hosts-editor"', 'id="notfound-listeners"', 'id="notfound-certificate"', 'id="notfound-mode"', 'id="notfound-status"', 'id="panel-streams"', 'id="stream-search"', 'id="stream-list"', 'id="stream-new"', 'id="stream-listener-add"', 'id="stream-dialog"', 'id="stream-form"', 'class="vhost-dialog logging-format-dialog"', ] for marker in required_html: assert marker in html, marker for marker in ['id="settings-tab-manual-config"', 'id="settings-page-manual-config"', 'id="config-editor"', 'id="save-json"', 'id="export-config"']: assert marker in html, marker assert 'data-panel="configuration"' not in html and 'id="panel-configuration"' not in html assert html.index('data-panel="vhosts"') < html.index('data-panel="redirects"') < html.index('data-panel="notfound"') < html.index('data-panel="streams"') < html.index('data-panel="acl-profiles"') assert "matching_resident_bytes" in cache and "matching_body_bytes" in cache for marker in ["cache-memory-breakdown", "cache-transient-breakdown", "cache-cold-storage", "cache-transient-sparkline", "cache-fill-sparkline", "cache-queue-sparkline", "cache-io-sparkline", "cache-cold-sparkline", "metric-info", "process-rss-total", "memory-cache-hot", "memory-cache-transient", "memory-transform", "memory-transform-inflight", "memory-non-cache-rss", "process-rss-anon", "process-rss-file", "process-data-swap", "memoryAccountingHint"]: assert marker in html, marker for marker in ["transient_bytes", "fill_bytes_active", "cold_queue_bytes", "cold_write_bytes_active", "cold_read_bytes_active", "process_rss_anon_bytes", "process_rss_file_bytes"]: assert marker in app or marker in cache, marker assert "matching_cold_bytes" in cache and "cold_promotions" in cache and "hot_demotions" in cache assert "next.logging" in app and "logstash:" in app and "loki:" in app and "file:" in app assert "LOGGING_PRESETS" in app and "selectedLoggingMapping" in app and "renderLoggingPreview" in app assert "administration" in settings and "tls-policy" in settings assert "additional_addresses" in integrations assert "addressTagEditor" in integrations and "listener-address-chip-editor" in integrations assert 'const h2 = field(grid, "listener-h2", "vhHttp2"' in integrations assert 'h2Configure' in integrations and 'selectHttpTab?.("http2")' in integrations assert 'renderHttp2Settings' in integrations and 'collectHttp2Listeners' in integrations assert 'const h3 = field(grid, "listener-h3", "h3Enable"' in integrations assert 'h3Configure' in integrations and 'selectHttpTab?.("http3")' in integrations h3_tuning = integrations.split('function http3SettingsControls', 1)[1].split('function addRow', 1)[0] assert 'h3Enable' not in h3_tuning assert 'data-i18n="processDiagnostics">Process<' in html for marker in ['function setOptionalMetric', 'setOptionalMetric("process-rss-anon"', 'setOptionalMetric("system-swap"', 'setOptionalMetric("system-network"']: assert marker in app, marker assert 'const http80 = prefer(false, 80);' in integrations assert 'if (http80) autoIds.add(http80.id);' in integrations and 'candidates.find(endpoint => !endpoint.binding?.tls)' in integrations assert 'https443' not in integrations assert 'window.rustProxyIntegrations.bindings(holder, selected, !editing)' in redirects assert 'window.rustProxyIntegrations.hostnames(hostHolder' in redirects assert 'function hostnames(parent, initial = [], options = {})' in integrations assert 'class="protocol-toggle-row"' in html assert "stream-host-rows" not in integrations and 'id="settings-page-streams"' not in html and 'id="streams-form"' not in html for marker in ["rustProxyStreams", "streamTrafficChart", "streamPendingRestart", "chartHistory", "sessions_total", "bytes_in", "bytes_out"]: assert marker in streams, marker assert "/assets/streams.js" in html assert "/assets/redirects.js" in html assert "/assets/host404.js" in html for marker in ["rustProxyRedirects", 'saveSection("redirects"', "redirectTargetType", "redirectPathMode", "redirectQueryMode", "redirectCacheSeconds"]: assert marker in redirects, marker for marker in ["rustProxyNotFound", 'saveSection("not_found_hosts"', "rustProxyIntegrations.hostnames", "rustProxyIntegrations.bindings", "notfound-mode", "notfound-status", "BUILTIN_STATUSES", "ERROR_STATUSES"]: assert marker in host404, marker assert 'for (let code = 200; code <= 599; code++)' not in host404 assert 'id="docker-search"' in html and 'id="docker-show-hidden"' in html assert 'preserve_host: true' in vhosts assert "tls_policy" in vhosts and "vhost-live-grid" in vhosts assert '"force-ssl", "vhForceSsl"' in vhosts and 'force_ssl: yes("force-ssl")' in vhosts assert '"forward-client-ip", "vhForwardClientIp"' in vhosts and 'forward_client_ip: yes("forward-client-ip")' in vhosts assert "vhForceSslNeedsTls" in vhosts and "vhForceSslNeedsHttp" in vhosts and "vhForceSslPortConflict" in vhosts assert "tlsPorts.has(443)" in vhosts assert '$("vhost-search").addEventListener("input", render)' in vhosts and "vhSearchEmpty" in vhosts assert "pool-vhost-disabled" in vhosts for marker in ["locationMatchRegex", "headersVisualBuilder", "headersAdvancedJson", "openLocationHelp", "cloudflare-client-ip", "timing-headers"]: assert marker in vhosts, marker for marker in ["location-help-dialog", "cache-memory-sparkline", "network-rx-sparkline", "latency-sparkline", "sourceIp"]: assert marker in html, marker for marker in ["lastApiLatencyMs", "cacheMemorySamples", "networkRxSamples", "responseLatencySamples"]: assert marker in app, marker assert '
' in html assert 'class="mini-live-chart-head"' in html and 'data-i18n="responseLatencyHistory"' in html assert 'viewBox: "0 0 300 60"' in app and 'M0 15H300 M0 30H300 M0 45H300' in app assert '.network-interface-row {' in css and 'background: transparent' in css assert "hostTagEditor" in vhosts and "vhost-chart-line" in vhosts and "vhLifetimeChart" in vhosts for marker in ["refreshCertificateInventory", "certificateDetails", "certificate-domain-pills", "certificateCoverageUnknown", "async function revalidateSession", 'Object.values(PANEL_PATHS), "/configuration"']: assert marker in app, marker for marker in ['data-certificate-tab="installed"', 'data-certificate-tab="renewal"', 'data-certificate-tab="generate"', 'id="certificate-page-installed"', 'id="certificate-page-renewal"', 'id="certificate-page-generate"']: assert marker in html, marker assert 'id="certificate-dialog"' not in html and 'id="certificate-close"' not in html for marker in ['function selectTab(name, focus = false, persist = true)', 'log_directories', 'certbotLogs', 'value?.kind?.startsWith("renewal")']: assert marker in acme_ui, marker for marker in ['.certificate-operation-card', '.renewal-meta', '#panel-tls .certificate-tabs']: assert marker in css, marker login = (ROOT / "src/admin/web/login.js").read_text(encoding="utf-8") settings_ui = (ROOT / "src/admin/web/settings.js").read_text(encoding="utf-8") assert "async function redirectExistingSession" in login and "/api/auth/session" in login assert '["/settings", "/configuration", "/logging"].includes(location.pathname)' in settings_ui assert 'new URLSearchParams(location.search).get("tab")' in settings_ui and 'function syncLocation()' in settings_ui and 'history.replaceState' in settings_ui assert '"/docker-vhosts"' in login and 'settingsTabs = new Set' in login and '`/settings?tab=${encodeURIComponent(tab)}`' in login login_html = (ROOT / 'src/admin/web/login.html').read_text(encoding='utf-8') assert 'class="auth-page auth-loading"' in login_html and 'document.body.classList.remove("auth-loading")' in login assert 'class="app-loading"' in html and 'document.documentElement.classList.remove("app-loading")' in app assert 'url.pathname = "/settings"' in settings_ui and 'url.searchParams.set("tab", active)' in settings_ui assert 'function withFormSubmitState(form, submitter, task)' in app and 'button.textContent = t("saving")' in app assert 'function fieldError(target, message)' in app and 'function showFieldError(error, form = null)' in app assert 'function authReturnDestination()' in app and 'pendingAuthDestination || authReturnDestination()' in app assert '$("login").hidden = navigating' in app and 'id="auth-message"' in html assert 'settings-tabs button.has-unsaved::after' in css and 'function syncSettingsDirtyIndicators()' in app assert 'setSettingsDirty("config-editor")' in app and '!settingsDirty.has("config-editor")' in app assert 'throw fieldError($("tls-policy-12")' in settings_ui assert 'input[type="checkbox"],\ninput[type="radio"] {' in css and 'width: 15px;' in css and '.log-column-option input[type="checkbox"]' not in css and 'overflow: visible' in css and 'max-height: min(38dvh, 220px)' not in css assert '.log-tools input:not([type="checkbox"]):not([type="radio"])' in css assert '.log-tools input {\n' not in css footer = app.split('function renderFooter(s) {', 1)[1].split('let lastLogs', 1)[0] assert 'SQLite / HTTP/1.1' not in footer and 'coldQ:' not in footer and 'streamFallback' not in footer assert '`${apiLatency} / ${t("logsDropped")}: ${s.logs_dropped}`' in footer assert 'update?.update_available && update.latest_version' in footer and 'footer-update' in footer assert '#docker-agents-form>.form-actions {' in css and 'justify-content: flex-start' in css for marker in ['prom-trusted-no-token', 'prom-streams', 'prom-logging']: assert marker in html and marker in settings_ui, marker for marker in [".certificate-details", ".certificate-domain-pills", ".certificate-card .pill"]: assert marker in css, marker integrations_0194 = (ROOT / "src/admin/web/integrations.js").read_text(encoding="utf-8") docker_0194 = (ROOT / "src/admin/web/docker.js").read_text(encoding="utf-8") assert 'readAddresses.input' in integrations_0194 assert 'form.reset(); window.rustProxyCertificates?.close();' in integrations_0194 assert '$("cert-import-pem").value = ""' not in integrations_0194 and '$("cert-import-key").value = ""' not in integrations_0194 assert 'throw fieldError(row.querySelector(".docker-agent-listener-choices")' in docker_0194 management = (ROOT / "src/admin/web/management.js").read_text(encoding="utf-8") assert "rustProxyAclProfiles" in management and "acl_profiles" in management assert "collectRuleRows" in management and "parseRules(rules.value, rules)" in management assert "normalizeAclRuleText" in app and "ruleSyntaxLine" in app assert ".settings-tabs" in css and "grid-template-columns" in css and "width: 100%" in css assert ".logging-grid fieldset" not in css assert ".pool-vhost-disabled" in css and ".acl-profile-rule" in css assert ".stream-live-grid" in css and ".stream-chart-rx" in css and ".listener-address-chip-editor" in css for marker in ['.vh-backend-card', '.auth-subcard', '.vh-cache-options[hidden]', '.protocol-toggle-row', '.listener-choices']: assert marker in css, marker assert html.count('

= 3 def source_contract_smoke() -> None: config = (ROOT / "src/config.rs").read_text(encoding="utf-8") tls = (ROOT / "src/tls.rs").read_text(encoding="utf-8") main = (ROOT / "src/main.rs").read_text(encoding="utf-8") server = (ROOT / "src/server.rs").read_text(encoding="utf-8") logging = (ROOT / "src/logging.rs").read_text(encoding="utf-8") secret_store = (ROOT / "src/secret_store.rs").read_text(encoding="utf-8") backup = (ROOT / "src/backup.rs").read_text(encoding="utf-8") acme_worker = (ROOT / "scripts/acme_provider.py").read_text(encoding="utf-8") for feature_marker in ["AES_256_GCM", "master.key", "O_NOFOLLOW", "migrate_acme_credentials"]: assert feature_marker in secret_store, feature_marker for feature_marker in ["Argon2", "RPROXYB1", "secret-store-key.enc", "validate_backup_version"]: assert feature_marker in backup, feature_marker for feature_marker in ["ErrorLogSettings", "ERROR_LOG_QUEUE", "RedactingStderr", "open_error_file", "ErrorLogLayer"]: assert feature_marker in logging, feature_marker assert "credential_ini_b64" in acme_worker and "credential_file.unlink" in acme_worker cache = read_cache_source() settings = (ROOT / "src/settings.rs").read_text(encoding="utf-8") websocket = (ROOT / "src/proxy/websocket.rs").read_text(encoding="utf-8") proxy = (ROOT / "src/proxy/mod.rs").read_text(encoding="utf-8") assert '.insert("x-rustproxy-backend-time", value)' in proxy and '.insert("x-rust-proxy-backend-time", value)' not in proxy pages = (ROOT / "src/pages.rs").read_text(encoding="utf-8") admin = (ROOT / "src/admin/mod.rs").read_text(encoding="utf-8") stream = (ROOT / "src/stream.rs").read_text(encoding="utf-8") runtime = (ROOT / "src/runtime.rs").read_text(encoding="utf-8") acl = (ROOT / "src/acl.rs").read_text(encoding="utf-8") policies = (ROOT / "tests/policies.rs").read_text(encoding="utf-8") http3 = (ROOT / "src/http3.rs").read_text(encoding="utf-8") redirect = (ROOT / "src/redirect.rs").read_text(encoding="utf-8") not_found = (ROOT / "src/not_found.rs").read_text(encoding="utf-8") prometheus = (ROOT / "src/prometheus.rs").read_text(encoding="utf-8") auth = (ROOT / "src/auth/mod.rs").read_text(encoding="utf-8") auth_store = (ROOT / "src/auth/store.rs").read_text(encoding="utf-8") upstream = (ROOT / "src/upstream.rs").read_text(encoding="utf-8") vhosts_js = (ROOT / "src/admin/web/vhosts.js").read_text(encoding="utf-8") docker_source = (ROOT / "src/docker.rs").read_text(encoding="utf-8") docker_js = (ROOT / "src/admin/web/docker.js").read_text(encoding="utf-8") docker_agent = (ROOT / "docker-agent/main.go").read_text(encoding="utf-8") html = (ROOT / "src/admin/web/index.html").read_text(encoding="utf-8") cache_panel_ui = (ROOT / "src/admin/web/cache-panel.js").read_text(encoding="utf-8") transform_panel = (ROOT / "src/admin/web/transform-panel.js").read_text(encoding="utf-8") app_js = (ROOT / "src/admin/web/app.js").read_text(encoding="utf-8") # 0.24.1: shared library statistics, authenticated runtime metadata and Logging in Settings. manifest = tomllib.loads((ROOT / "Cargo.toml").read_text(encoding="utf-8")) system_stats = (ROOT / "src/system_stats/mod.rs").read_text(encoding="utf-8") linux_stats = (ROOT / "src/system_stats/linux.rs").read_text(encoding="utf-8") settings_ui = (ROOT / "src/admin/web/settings.js").read_text(encoding="utf-8") openapi_0241 = (ROOT / "src/admin/openapi.json").read_text(encoding="utf-8") assert not (ROOT / "src/system_stats/bsd.rs").exists() metrics_process = manifest["dependencies"]["metrics-process"] assert metrics_process["version"] == "=2.4.3" assert metrics_process["default-features"] is False assert metrics_process["features"] == ["dummy"] sysinfo_network = manifest["dependencies"]["sysinfo"] assert sysinfo_network["version"] == "=0.38.4" assert sysinfo_network["default-features"] is False assert sysinfo_network["features"] == ["network"] target_key = 'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "macos", target_os = "windows"))' assert manifest["target"][target_key]["dependencies"]["systemstat"] == "=0.2.7" for marker in [ "systemstat::{Platform, System}", "metrics_process::collector::collect", "sysinfo::Networks::new_with_refreshed_list", "collect_network_systemstat", "merge_network_interfaces", "system_swap_total_bytes", "system_uptime_seconds", "process_virtual_memory_bytes", "process_start_time_seconds", "max_fds", ]: assert marker in system_stats, marker for forbidden in ["sysctl", "netstat", "Command::new"]: assert forbidden not in system_stats, forbidden for marker in ["/proc/stat", "/proc/meminfo", "RssAnon", "VmData", "VmSwap", "pub(crate) fn augment"]: assert marker in linux_stats, marker for marker in ["installed_data_dir_candidates", "resolve_installed_data_dir", '"/var/lib/rustproxy"', '"/var/db/rustproxy"']: assert marker in main, marker for marker in ['payload["runtime"]', '"statistics_source"', '"data_directory"', '"database_path"']: 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.24.9"' in openapi_0241 assert 'id="panel-logging"' not in html for marker in ['id="settings-tab-logging"', 'id="settings-page-logging"', 'id="logging-form"']: assert marker in html, marker assert 'location.pathname === "/logging"' in settings_ui # 0.22.3 h2c Upgrade and externally owned certificate deployment contracts. for marker in [ 'StatusCode::SWITCHING_PROTOCOLS', 'struct H2cUpgradeIo', 'HTTP2-Settings', 'http1_h2c_upgrade_bootstraps_request_as_http2_stream_one', 'malformed_h2c_upgrade_is_rejected_before_switching_protocols', ]: assert marker in server, marker for marker in [ '"/api/certificates/reload"', 'certificate_reload', '"/api/certificates/deploy"', 'certificate_deploy', 'certificate.deploy', 'certificate.reload', 'reload_certificates().await', '"operation": "unchanged"', 'config.tls.certificates.push(crate::tls::Certificate', ]: assert marker in admin, marker for marker in ['pub async fn reload_certificates', 'configured certificates reloaded from disk']: assert marker in runtime, marker for marker in ['TlsCommand::Deploy', '/api/certificates/deploy', '/api/certificates/reload', 'read_external_pem']: assert marker in main, marker for marker in ['pub fn stage_external_pair', 'pub fn reconcile_external_pair', 'ExternalPairDeployment::Unchanged', 'pub fn remove_external_pair', 'external_pair_stages_generation_and_rolls_back_cleanly', 'external_pair_reconcile_is_idempotent_and_repairs_missing_generation']: assert marker in tls, marker assert 'api("/api/certificates/reload"' in app_js transport_0223 = (ROOT / "docs/TRANSPORT.md").read_text(encoding="utf-8") assert 'Upgrade: h2c' in transport_0223 and 'stream 1' in transport_0223 assert 'legacy HTTP/1.1 Upgrade-to-h2c is not implemented' not in transport_0223 audit_0233 = (ROOT / "docs/V0.24.0_AUDIT.md").read_text(encoding="utf-8") audit_0232 = (ROOT / "docs/V0.23.2_AUDIT.md").read_text(encoding="utf-8") audit_0223 = (ROOT / "docs/V0.22.3_AUDIT.md").read_text(encoding="utf-8") for marker in ["legacy websocket", "origin", "http/1.1", "sec-websocket-accept", "ilo4"]: assert marker.lower() in audit_0233.lower(), marker for marker in ["600 samples", "308", "HTTPS", "/certs", "--livelogs"]: assert marker.lower() in audit_0232.lower(), marker assert 'external certificate ownership model' in audit_0223.lower() and 'release gate' in audit_0223.lower() update_check = (ROOT / "src/update_check.rs").read_text(encoding="utf-8") update_defaults = tomllib.loads((ROOT / "UPDATE_CHECK.toml").read_text(encoding="utf-8")) assert update_defaults["repository_url"] == "https://git.linuxiarz.pl/gru/RustProxy" assert update_defaults["version_url"].endswith("/raw/branch/master/Cargo.toml") assert update_defaults["interval_seconds"] == 3600 for marker in ['include_str!("../UPDATE_CHECK.toml")', 'pub async fn poll_loop', 'tokio::time::sleep(interval)', 'package_version(&document)', 'MAX_VERSION_DOCUMENT_BYTES']: assert marker in update_check, marker assert 'update version document is too large or incomplete: {error}' in update_check assert '.context("update version document is too large or incomplete")' not in update_check for marker in ['tracing::error!(', 'MAX_REDIRECTS: usize = 5', 'SSL_CERT_FILE', 'CURL_CA_BUNDLE', '/usr/local/share/certs/ca-root-nss.crt', 'response.status().is_redirection()', 'redirect_target(¤t, location)']: assert marker in update_check, marker assert 'pub last_error:' not in update_check for marker in ['RUST_PROXY_UPDATE_REPOSITORY_URL', 'RUST_PROXY_UPDATE_VERSION_URL', 'RUST_PROXY_UPDATE_CHECK_INTERVAL_SECS', 'state.start_update_check(update_check);']: assert marker in main, marker assert 'pub software_update: Arc' in runtime assert 'payload["software_update"] = json!(state.software_update.snapshot());' in admin for marker in [ "a persisted remote administration listener requires administration TLS", "validate_narrower_than", "tls_policy", "AclProfile", "normalize_host_pattern", "profile_country_access", "https_redirect_port", "forward_client_ip", "plain HTTP listener", "tls_ports.contains(&443)", "backend_tls_verification_defaults_off_and_can_be_enabled_explicitly", "pub accept_local_listener_risk: bool", "backend_diagnostic_flags_compile_to_frontend", "backend_header: host.backend_header", "backend_timing_header: host.backend_timing_header", ]: assert marker in config, marker for marker in ["compile_admin", "ProtocolPolicy", "policy_only_configuration_does_not_require_certificate_directory", "certificate_covered_hosts", "legacy_certificate_coverage_can_be_read_from_existing_pem"]: assert marker in tls, marker assert "compile_admin" in main and "serve_tls_with_plain_redirect" in main assert "TlsMode::Fixed" in server and "TlsMode::Frontend" in server and "plaintext_router" in server assert "additional_addresses" in settings and "multi-address listeners require explicit IPs" in settings for marker in ["pub session_days: u16", "session_days: 30", "(7..=30).contains(&self.session_days)", "administration_session_lifetime_is_7_to_30_days_and_hot_apply_only"]: assert marker in settings, marker for marker in ["30 * 24 * 60 * 60", "browser_session_lifetime_is_configurable_without_sliding_expiry", "session_cookie_uses_configured_remaining_lifetime"]: assert marker in auth, marker for marker in ["create_browser_session", "session_at_with_browser_lifetime", "browser session lifetime must be 7..30 days"]: assert marker in auth_store, marker for marker in ["accept_local_listener_risk", "!self_target || self.accept_local_listener_risk", "explicit_risk_acceptance_allows_local_listener_but_not_private_policy_bypass"]: assert marker in upstream, marker assert "member-local-risk" in vhosts_js and "vhLocalListenerRiskConfirm" in vhosts_js assert 'function administrationListenCandidates()' in vhosts_js and 'function adminListenerBackendCandidate(raw)' in vhosts_js for marker in ['latestStatus?.administration?.locked_listen', 'latestStatus?.administration?.active?.listen', 'latestStatus?.admin_listen', "value === 'localhost'", '/^127(?:\\.\\d{1,3}){3}$/.test(value)', 'riskGate.hidden = show !== true', 'row.syncLocalRisk = () => syncRisk(false)']: assert marker in vhosts_js, marker assert 'const saved = editing ? (envelope?.config.vhosts || []).find(item => item.id === editing) : null;' in vhosts_js assert 'load(saved ? structuredClone(saved) : defaults());' in vhosts_js assert 'backends: [{ id: "origin-1", url: ""' in vhosts_js assert 'preserve_host: true' in vhosts_js for marker in ['vhBackendDiagnostics', 'backend_header: yes("backend-header")', 'backend_timing_header: yes("backend-timing-header")', 'protocol-toggle-row', 'auth-subcard', 'vh-cache-options', 'syncCacheVisibility', 'cache: { enabled: false }']: assert marker in vhosts_js, marker assert 'values.length ? values : [{ id: "root"' not in vhosts_js for marker in ["PinnedCertificateVerifier", "tls_fingerprint_sha256", "health_check: crate::balance::HealthCheck", "certificate fingerprint mismatch", "generated_id"]: assert marker in docker_source, marker for marker in ["docker-agent-fingerprint", "docker-agent-certificate", "docker-health-mode", "docker-search", "docker-show-hidden", "pool_routes", "dockerBackendUp", "setInterval"]: assert marker in docker_js, marker assert 'function checkboxField(key, control)' in docker_js assert 'checkboxField("dockerPreserveHost", preserve)' in docker_js and 'checkboxField("dockerAclInline", inline)' in docker_js assert 'label = el("label", undefined, "check vh-check")' in docker_js ui_css = (ROOT / "src/admin/web/style.css").read_text(encoding="utf-8") assert '.docker-route-grid input:not([type="checkbox"])' in ui_css and '.docker-checkbox-field .vh-check {' in ui_css assert 'docker-switch-row' in docker_js and '.docker-switch-row {' in ui_css and 'repeat(5, minmax(110px, 160px))' in ui_css assert '.docker-passive-health-editor .docker-health-grid {' in ui_css assert '.docker-health-grid input:not([type="checkbox"])' in ui_css and '.docker-passive-health-editor .docker-checkbox-field input[type="checkbox"]' in ui_css assert 'id="sparkline-transform"' in html and 'id="rate-transform"' in html assert 'id="panel-transform"' in html and 'data-panel="transform"' in html and '/assets/transform-panel.js' in html assert '/api/transform' in transform_panel and 'tp-cache-hits' in transform_panel for marker in ['transform-memory-budget', 'transform-max-inflight', 'data-transform-preset="conservative"', 'data-transform-preset="recommended"', 'data-transform-preset="performance"', 'data-transform-preset="uber"']: assert marker in html, marker for marker in ['selectedTransformPreset', 'transformPresetValues', 'applyTransformPreset', 'updateTransformResourceSizing', 'memory_budget_bytes', 'max_inflight']: assert marker in app_js, marker assert '
' in html assert '
' in html for marker in ['cache-live-hit-line', 'cache-live-miss-line', 'cache-live-bypass-line']: assert marker in html and marker in cache_panel_ui, marker for marker in ['transform-live-applied-line', 'transform-live-cache-line', 'transform-live-skipped-line', 'transform-live-error-line']: assert marker in html and marker in transform_panel, marker assert 'sparklinePoints(liveChartSamples.hits, 900, 180, 10, peak)' in cache_panel_ui assert 'sparklinePoints(liveChartSamples.applied, 900, 180, 10, peak)' in transform_panel assert '.function-live-chart svg {' in ui_css and 'height: clamp(180px, 22vw, 270px)' in ui_css assert '--transform-chart:' in ui_css and 'stroke: var(--transform-chart)' in ui_css and 'background: var(--transform-chart)' in ui_css assert '.cache-transform-badge {' in ui_css and 'border: 1px solid var(--pill-line)' in ui_css and 'border-radius: 3px' in ui_css html = (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8') cache_panel_js = (ROOT / 'src/admin/web/cache-panel.js').read_text(encoding='utf-8') management_js = (ROOT / 'src/admin/web/management.js').read_text(encoding='utf-8') app_js = (ROOT / 'src/admin/web/app.js').read_text(encoding='utf-8') vhosts_js = (ROOT / 'src/admin/web/vhosts.js').read_text(encoding='utf-8') cache_rs = read_cache_source() admin_rs = (ROOT / 'src/admin/mod.rs').read_text(encoding='utf-8') logging_rs = (ROOT / 'src/logging.rs').read_text(encoding='utf-8') assert 'bindSettingsForm("pages-form", next => { next.pages = global.read(); }, "pages")' in management_js assert '"pages" => {' in admin_rs and 'default_pages_section_accepts_the_standard_value_payload' in admin_rs assert 'pub const LIVE_WINDOW: usize = 256;' in logging_rs and 'pub const HISTORY_CAPACITY: usize = 4096;' in logging_rs assert 'api("/api/logs?limit=4096")' in management_js assert 'const STATUS_NAMES = { 200: "OK"' in management_js and '404: "Not Found"' in management_js and 'statusLabel(code)' in management_js assert 'let paused = null, expanded = null' in management_js and 'function mergeExpanded(data)' in management_js and 'entries.length >= 4096' in management_js assert 'dialog.addEventListener("close", () => { if (pinning) return; expanded = null; paused = null;' in management_js assert 'hash_scheme.as_bytes()' in cache_rs and 'cache_key_separates_frontend_scheme' in cache_rs assert not (ROOT / 'src/cache.rs').exists() and (ROOT / 'src/cache/mod.rs').is_file() cache_mod = (ROOT / 'src/cache/mod.rs').read_text(encoding='utf-8') assert 'impl Cache {\n include!(' not in cache_mod for fragment in ['lifecycle.rs', 'cold.rs', 'dashboard.rs', 'tickets.rs', 'lookup.rs']: assert (ROOT / 'src/cache' / fragment).read_text(encoding='utf-8').lstrip().startswith('impl Cache {') assert 'payload["transform_metrics"] = json!(state.transform_metrics.total())' in admin_rs for marker in ['payload["transform_resources"]', 'payload["memory_accounting"]', 'non_cache_rss_includes_transform', 'cold_limit_bytes', 'cold_limit_entries']: assert marker in admin_rs or marker in cache, marker assert 'const transformed = s.transform_metrics || {}' in app_js and 'transformSamples.push(rates.transform)' in app_js for marker in ['status.memory_accounting || {}', 'memory.cache_hot_bytes', 'memory.transform_reserved_bytes', 'cache-cold-storage']: assert marker in app_js, marker for marker in ['data?.resources || {}', 'tp-memory', 'tp-inflight']: assert marker in transform_panel, marker assert 'function uptime(value)' in app_js and 'setOptionalMetric("process-uptime", status.uptime_seconds != null' in app_js assert 'connection-uptime-footer' not in html and 'id="process-uptime"' in html assert 'function rateBytes(value)' in app_js and 'if (value === 0) return "0.0 KiB";' in app_js and 'return "<0.1 KiB";' in app_js assert 'function backendTrafficRate(' in vhosts_js and 'member.bytes_in' in vhosts_js and 'member.bytes_out' in vhosts_js and 'rateBytes(memberTraffic)' in vhosts_js and 'rateBytes(d.traffic)' in vhosts_js assert 'id="cache-domain-filter"' in html and 'cacheDomainFilterEnabled' in html and 'cacheDomainFilterDisabled' in html assert 'const visibleDomains = (data, groups) =>' in cache_panel_js and 'scoped?.settings?.enabled ?? data.settings?.enabled ?? false' in cache_panel_js for marker in ['effectiveCacheMemory', 'cacheMemoryHeadroom', 'cachePresetValues', 'applyCacheMemoryPreset', 'selectedCacheMemoryPreset', 'selectedCacheColdPreset', 'applyRecommendedColdCachePreset', 'cacheMemoryPresetTooSmall', 'cacheObjectOverTransientWarning', 'cgroup_memory_max_bytes', 'transient_max_bytes', 'cold_queue_max_bytes', 'hot_eviction_max_objects']: assert marker in app_js, marker assert 'envelope?.config.cache?.enabled === true && cacheToggle.value !== "off"' in vhosts_js assert 'groups = d.domains || [], shownGroups = visibleDomains(data, groups)' in cache_panel_js and 'replaceChildren(...shownGroups.map(row =>' in cache_panel_js balance_rs = (ROOT / 'src/balance.rs').read_text(encoding='utf-8') assert 'traffic_rate_drops_to_zero_after_an_idle_sample_window' in balance_rs and 'if delta == 0 {' in balance_rs and 'sample.rate = 0.0;' in balance_rs all_admin_js = "\n".join(path.read_text(encoding='utf-8') for path in (ROOT / 'src/admin/web').glob('*.js')) assert not re.search(r'(?span {' in ui_css and 'transform: translateY(-1px)' in ui_css assert '.cache-toolbar-status .check' in ui_css and '.docker-toolbar .check' in ui_css and '.docker-alias-field>span:first-child {' in ui_css and 'margin-bottom: 0' in ui_css and '.docker-alias-editor {' in ui_css and 'min-height: 38px' in ui_css and 'height: 30px' in ui_css assert 'readOnly: !!editing' not in vhosts_js and 'vhIdRenameConfirm' in vhosts_js and 'v.id === value.id && v.id !== editing' in vhosts_js assert 'enforce_local_page_no_cache' in proxy and 'LocalGeneratedResponse' in proxy and 'locally_generated_pages_remove_cache_metadata' in proxy assert 'local_page_method_rejection_is_still_marked_non_cacheable' in pages and 'response.extensions_mut().insert(PageServed);' in pages screenshots = {path.name for path in (ROOT / 'docs/screenshots').iterdir() if path.is_file()} assert 'README.md' in screenshots assert screenshots <= {'README.md', 'login.png', 'location-headers.png'} assert 'generated_vhost_inherits_global_server_header_policy' in (ROOT / 'src/docker.rs').read_text(encoding='utf-8') assert '.docker-route-grid {' in ui_css and 'align-items: start' in ui_css assert '.docker-acl-grid {' in ui_css and 'height: 38px' in ui_css assert 'preserve.checked = assignment?.preserve_host !== false' in docker_js for marker in ["ensureSelfSignedCertificate", "ListenAndServeTLS", "CertificateSHA256", "RUSTPROXY_DOCKER_AGENT_TLS"]: assert marker in docker_agent, marker for marker in [ 'fallbacks: HashMap>', 'id: format!("{}/main", host.id)', 'custom_locations_override_but_main_vhost_remains_fallback', 'direct_admin_listener_backend_is_marked_for_guarded_control_plane_use', 'pub targets_admin_listener: bool', ]: assert marker in config, marker vhost_source = (ROOT / "src/vhost.rs").read_text(encoding="utf-8") assert 'is_legacy_main_fallback' in vhost_source assert "benign_tunnel_close" in websocket and "ConnectionReset" in websocket assert 'StatusCode::UNAUTHORIZED => Some(include_str!("admin/web/error_401.html"))' in pages assert 'StatusCode::NOT_FOUND => Some(include_str!("admin/web/error_404.html"))' in pages assert 'pub struct MinimalWelcome' in pages and 'builtin_welcome_is_minimal_and_never_cacheable' in pages and 'HeaderValue::from_static("no-store")' in pages for marker in [ "fn admin_request_authority", "Version::HTTP_2 | Version::HTTP_3", "request.uri().authority().ok_or(())?", "administration_https_health_accepts_http2_authority_without_host_header", ]: assert marker in admin, marker assert "fn panel_document_path" in admin and "panel_document_path(&path)" in admin and "panel_subpage_routes_survive_browser_refresh" in admin assert 'response.extensions().get::().is_none()' in proxy assert '"cdn-cache-control"' in proxy and '"x-cache-resident-age"' in proxy for marker in ["OriginBackend", "apply_backend_diagnostic_headers", "x-rustproxy-backend-time", "backend_diagnostics_are_opt_in_and_strip_spoofed_values"]: assert marker in proxy, marker for marker in ["force_ssl_redirect", "force_ssl_location", "StatusCode::PERMANENT_REDIRECT", "https_redirect_port", "apply_upstream"]: assert marker in proxy, marker forwarding = (ROOT / "src/forwarding.rs").read_text(encoding="utf-8") for marker in ["pub fn apply_upstream", 'headers.remove("x-forwarded-for")', 'headers.remove("x-real-ip")', "upstream_can_withhold_client_ip_without_losing_origin_metadata"]: assert marker in forwarding, marker for marker in ["SINK_QUEUE", "FileSink", "LogstashSink", "LokiSink", "Limited::new"]: assert marker in logging, marker for marker in [ "matching_body_bytes", "matching_resident_bytes", "matching_cold_bytes", "expose_resident_age", "x-cache-resident-age", "COLD_QUEUE", "ColdFile", "body_sha256", "get_tiered", "revalidation_tiered", "cold_queue_dropped_total", "cold_after_seconds", "cold_on_age", "cold_max_hot_seconds", "cold_policy_can_demote_by_total_hot_residency_age", "lru_demotion_persists_and_survives_restart", "managed_update_flush_ignores_shutdown_toggle_when_cold_tier_is_enabled", "flush_hot_to_cold_for_update", "disabled_cache_strips_diagnostics_without_exposing_bypass_headers", "vary_selects_variants_without_fragmenting_the_primary_hash", "vary_schema_change_removes_incompatible_fallbacks", "varnish_primary_hash_uses_host_and_url_not_unrelated_request_headers", "resident_age_survives_successful_revalidation", "COLD_FORMAT_VERSION: u8 = 3", "cold_cache_uses_two_hex_character_shards", "legacy_flat_cold_cache_is_not_migrated", "manual_cold_cleanup_reports_removed_objects_and_freed_bytes", "rfc5861_windows_and_revalidation_flags_are_separate_metadata", "request_max_stale_is_bounded_and_respects_revalidation_flags", "max_stale_can_reuse_an_object_after_normal_ttl_expiry", "request_max_age_only_revalidates_when_cached_age_exceeds_limit", "transformed_plan_uses_transformed_content_length", "transformed_representation_is_stored_and_reused", "every_enabled_transform_kind_can_fill_and_reuse_the_cached_representation", "query_strings_remain_distinct_and_bypass_query_is_only_a_request_policy", "transformed_client_conditionals_use_the_representation_etag_not_the_origin_etag", "no_transform_does_not_disable_shared_storage", "duplicate_and_argument_immutable_do_not_invalidate_cache_control", "fill_incomplete", "transform_length_mismatch", "object_too_large", "cold_cleanup_skips_an_entry_while_a_read_is_pinned", "stale_if_error_is_only_available_inside_its_window", "matching_hot_key", "matching_cold_key", ]: assert marker in cache, marker transform = (ROOT / "src/transform/mod.rs").read_text(encoding="utf-8") for marker in [ "pub struct Settings", "pub struct Overrides", "resolved.enabled = global.enabled && self.enabled", "pub fn no_transform", "OriginValidators", "fn decode<'a>", "Cow::Borrowed(body)", "trim_allocator_after_large_transform", "pub memory_budget_bytes: usize", "pub max_inflight: usize", "Preset::Uber", "try_reserve_transform_memory(required_memory, settings.memory_budget_bytes)", "try_reserve_transform_slot(settings.max_inflight)", "drop(decoded);", "drop(raw);", "rustproxy-", "unchanged_compressed_body_is_canonicalized_to_identity", "origin_no_transform_blocks_body_changes_and_keeps_validator", "final_no_transform_blocks_body_changes", "compressed_no_transform_preserves_origin_encoding_and_bytes", "empty_head_body_with_origin_length_keeps_origin_representation_headers", "CanonicalIdentity", ]: assert marker in transform, marker assert 'collect bounded static response for transformation: {error}' in transform assert '.context("collect bounded static response for transformation")' not in transform lookup = (ROOT / 'src/cache/lookup.rs').read_text(encoding='utf-8') model = (ROOT / 'src/cache/model.rs').read_text(encoding='utf-8') css_minifier = (ROOT / 'src/transform/css.rs').read_text(encoding='utf-8') assert 'let Some(_read_lease) = self.try_reserve_transient' in lookup assert 'struct TransientLease {\n _inner: Arc,' in model assert 'struct TransientLease(Arc);' not in model assert 'last_char = out.chars().last();' not in css_minifier assert css_minifier.count("last_char = Some(' ');") == 1 assert "pub mod transform;" in (ROOT / "src/lib.rs").read_text(encoding="utf-8") for name in ["js.rs", "css.rs"]: minifier = (ROOT / "src/transform" / name).read_text(encoding="utf-8") assert "Vec" not in minifier and ".chars().collect" not in minifier assert "into_bytes()" in minifier balance = (ROOT / "src/balance.rs").read_text(encoding="utf-8") for marker in [ "pub struct PassiveHealth", "enabled: false", "pub passive_health: PassiveHealth", "passive_health_is_opt_in_and_uses_separate_thresholds", "legacy_settings_without_passive_health_default_to_off", "slow_5xx_counts_both_observability_classes_when_passive_is_off", "unhealthy_source_hands_off_when_the_original_health_domain_recovers", "active_health_failures_total", "passive_health_failures_total", "http_5xx_total", "timeouts_total", "connect_errors_total", "io_errors_total", "slow_responses_total", ]: assert marker in balance, marker for marker in [ 'fill_bytes_active', 'cold_queue_items', 'cold_queue_bytes', 'cold_write_bytes_active', 'cold_read_bytes_active', 'transient_bytes', 'transient_max_bytes', 'cold_queue_max_bytes', 'hot_eviction_max_objects', 'hot_logical_bytes', 'hot_allocated_bytes', 'memory_pressure_rejections', 'struct FillGauge', 'struct TransientLease', 'reserve_fill_allocation', 'release_allocator_memory()', 'fn hot_entry_cost(', ]: assert marker in cache, marker assert 'impl Drop for Candidate' not in cache diagnostics = (ROOT / "src/diagnostics.rs").read_text(encoding="utf-8") + (ROOT / "src/system_stats/mod.rs").read_text(encoding="utf-8") + (ROOT / "src/system_stats/linux.rs").read_text(encoding="utf-8") for marker in ['process_rss_anon_bytes', 'process_rss_file_bytes', 'process_rss_shmem_bytes', 'process_data_bytes', 'process_swap_bytes', 'RssAnon', 'RssFile', 'RssShmem', 'VmData', 'VmSwap']: assert marker in diagnostics, marker for marker in ['admin_session_passthrough', 'admin_control_plane', 'restore_admin_cookie', 'BEFORE returning a successful RFC 9220 response', 'websocket_phase("tunnel_ready")']: assert marker in proxy, marker headers_source = (ROOT / "src/proxy/headers.rs").read_text(encoding="utf-8") assert 'pub fn restore_admin_cookie' in headers_source and 'admin_cookie_can_be_restored_only_from_captured_fragment' in headers_source assert "state.cache.purge(None)" not in proxy assert "request.method() == Method::HEAD && cache_ticket.is_some()" in proxy for marker in ["cache_effective_enabled", "transform_effective_enabled", "Some(route.id.clone())"]: assert marker in proxy, marker for marker in ["pub struct Metrics", "pub struct MetricsView", "struct ActiveSession", "sessions_total", "sessions_completed", "bytes_in", "bytes_out", "packets_in", "packets_out", "metrics_view_reports_l4_counters"]: assert marker in stream, marker for marker in ["pub enum TargetType", "pub enum PathMode", "pub enum QueryMode", "StatusCode::from_u16", "cache_seconds", "unconditional self-redirect"]: assert marker in redirect, marker assert "pub redirects: Vec" in config and "redirect_on" in config assert "pub not_found_hosts: Vec" in config and "not_found_host_on" in config for marker in ["pub struct Host", "pub struct CompiledHost", "crate::pages::Page"]: assert marker in not_found, marker assert "RedirectResponse" in proxy and "snapshot.redirect_on" in proxy assert "#[serde(default)]\n pub tls_verify: bool" in config upstream = (ROOT / "src/upstream.rs").read_text(encoding="utf-8") assert "BackendCertificateVerificationDisabled { supported_schemes }" in upstream disabled_verifier = upstream.split("impl ServerCertVerifier for BackendCertificateVerificationDisabled", 1)[1].split("pub fn client(", 1)[0] assert disabled_verifier.count("Ok(HandshakeSignatureValid::assertion())") == 2 assert "WebPkiServerVerifier" not in disabled_verifier assert "stream_metrics" in runtime and "stream_metrics" in main for marker in ["PrefixPriority", "RegexInsensitive", "LocationResponse", "pub rewrite: PathRewrite"]: assert marker in (ROOT / "src/vhost.rs").read_text(encoding="utf-8"), marker for marker in ["rewritten_path", "nginx_style_location_precedence_exact_priority_regex_prefix", "location_rewrites_are_normalized_and_regex_captures_expand", "custom_location_response_is_validated"]: assert marker in config, marker for marker in ["local_location_response", "route.rewritten_path(&path)", "encoded_origin_path", "client_ip.to_string()"]: assert marker in proxy, marker assert proxy.index("RustProxy: denied by access policy") < proxy.index("if let Some(spec) = &route.response") < proxy.index("let (backend, backend_lease, affinity_cookie)") assert "pub client_ip: Option" in logging and "client_ip" in logging assert '"metrics": stream_metrics' in admin and '"/assets/streams.js"' in admin and 'include_str!("web/streams.js")' in admin assert '.route("/404-hosts", get(public::index))' in admin and '"/assets/host404.js"' in admin and 'include_str!("web/host404.js")' in admin and '"not_found_hosts"' in admin assert "session_at_with_browser_lifetime" in admin and "session_lifetime_seconds" in admin assert '/api/preferences' in admin and 'live_log_columns' in admin assert 'default_admin_theme' in admin and '"dark" | "light"' in admin html = (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8') assert 'id="ui-theme"' in html and 'html[data-theme="light"] {' in ui_css assert 'body[data-density="compact"] input:not([type="checkbox"]):not([type="radio"])' in ui_css assert '
INDEPENDENT BY DESIGN ' not in html assert '
INDEPENDENT BY DESIGN
' in html assert "inspected_metadata" in admin and "certificate_metadata" in admin balance = (ROOT / "src/balance.rs").read_text(encoding="utf-8") assert "max_concurrent_requests" in balance and "queue_timeout_ms" in balance and "select_retry_wait" in balance for marker in ["dns_errors_total", "tls_errors_total", "response_header_timeouts_total", "last_failure_detail", "last_recovered_ms"]: assert marker in balance, marker tls_source = (ROOT / "src/tls.rs").read_text(encoding="utf-8") for marker in ["CertificateMetadata", "not_before_unix", "not_after_unix", "days_remaining", "expiry_status"]: assert marker in tls_source, marker assert '"/assets/redirects.js"' in admin and 'include_str!("web/redirects.js")' in admin and '.route("/redirects", get(public::index))' in admin assert '/Ubuntu%2014.04%20LTS/' in acl and 'repeated_slashes_are_normalized_for_routing' in acl and 'percent_encoded_spaces_are_valid_request_paths' in policies and 'repeated_slashes_route_normally_but_reach_upstream_unchanged' in policies and 'local_location_response_uses_response_rules_without_opening_backend' in policies and 'local_location_response_still_respects_route_acl' in policies db_source = (ROOT / "src/db.rs").read_text(encoding="utf-8") acme_source = (ROOT / "src/acme.rs").read_text(encoding="utf-8") assert "SCHEMA_VERSION: i64 = 7" in db_source and "certificate_renewals" in db_source and "0004_admin_preferences.sql" in db_source and "0005_certificate_api.sql" in db_source and "0006_certificate_api_credentials.sql" in db_source and "0007_secret_store.sql" in db_source assert "load_renewal_observations" not in acme_source and "renewals.json" not in acme_source for marker in ["pub kind: &'static str", "pub log_directory: String", "pub(crate) fn certbot_log_directory", 'kind: "issue"', '"renewal-dry-run"']: assert marker in acme_source, marker assert '"log_directories":log_directories' in admin and 'crate::acme::certbot_log_directory' in admin assert "store.load_certificate_renewals().await?" in main and "state.acme.restore_renewals(renewal_observations)" in main assert 'accept-remote-admin-risk' in main and 'hide = true' in main settings_rust = (ROOT / 'src/settings.rs').read_text(encoding='utf-8') assert 'pub struct StartupLocks' in settings_rust and 'fixed by systemd/startup' in settings_rust logging_rust = (ROOT / 'src/logging.rs').read_text(encoding='utf-8') assert 'pub per_vhost: bool' in logging_rust and 'default_vhost_log_directory' in logging_rust pages_rust = (ROOT / 'src/pages.rs').read_text(encoding='utf-8') assert 'validate_local_response' in pages_rust and '(200..=599).contains(&status)' in pages_rust assert 'Kind::Builtin' in pages_rust and 'builtin_application_page' in pages_rust docker_rust = (ROOT / 'src/docker.rs').read_text(encoding='utf-8') for marker in ['pub poll_interval_seconds: u64', 'pub acl_profile_id: Option', 'pub proxy_timeouts: crate::vhost::ProxyTimeouts', 'refresh(&self, settings: &Settings, force: bool)']: assert marker in docker_rust, marker docker_ui = (ROOT / 'src/admin/web/docker.js').read_text(encoding='utf-8') for marker in ['docker-live-refresh', 'saveAssignment(agentId, row, publish)', 'docker-acl-profile', 'docker-proxy-read', 'dockerOpenApp', 'proxiedUrl(agent, hostname)']: assert marker in docker_ui, marker compression_source = (ROOT / 'src/compression.rs').read_text(encoding='utf-8') metrics_source = (ROOT / 'src/metrics.rs').read_text(encoding='utf-8') style_source = (ROOT / 'src/admin/web/style.css').read_text(encoding='utf-8') app_source = (ROOT / 'src/admin/web/app.js').read_text(encoding='utf-8') vhosts_ui = (ROOT / 'src/admin/web/vhosts.js').read_text(encoding='utf-8') for marker in ['pub enum AppliedEncoding', 'AppliedEncoding::Gzip', 'AppliedEncoding::Brotli', 'AppliedEncoding::Zstd', 'AppliedEncoding::Deflate']: assert marker in compression_source, marker assert 'quality_weights_can_select_each_enabled_codec' in compression_source diagnostics_source = (ROOT / 'src/diagnostics.rs').read_text(encoding='utf-8') + (ROOT / 'src/system_stats/mod.rs').read_text(encoding='utf-8') + (ROOT / 'src/system_stats/linux.rs').read_text(encoding='utf-8') for marker in ['pub struct NetworkInterfaceView', 'pub network_interfaces: Vec', 'sysinfo::Networks::new_with_refreshed_list', 'system.network_stats', 'collect_network_systemstat', 'normalize_network_interfaces', 'merge_network_interfaces']: assert marker in diagnostics_source, marker assert 'interfaces.truncate(64)' not in diagnostics_source, 'network inventory must not silently truncate interfaces' for marker in ['pub http1_requests: AtomicU64', 'pub fn record_compression', 'each_codec_has_an_independent_counter', 'http1_requests_total']: assert marker in metrics_source, marker assert 'state.metrics.record_compression(encoding)' in proxy assert 'protocolSamples' in app_source and 'm.http1_requests' in app_source and '$("open-proxy")' not in app_source assert 'renderNetworkInterfaceCharts' in app_source and 'networkInterfaceSamples' in app_source assert 'rp-hidden-network-interfaces' in app_source and 'network-interface-hide' in style_source ui_html = (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8') for marker in ['metric-bytes-down', 'metric-bytes-up', 'connection-chart', 'certificate-api-credentials']: assert marker in ui_html, marker assert 'id="network-interface-charts"' in ui_html assert 'compressionCountersHelp' not in ui_html assert 'sparkline-h1' in (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8') assert 'body[data-density="compact"]' in style_source and '--ui-control: 22px' in style_source assert 'status.backend_pool_domains' in vhosts_ui and 'payload["backend_pool_domains"]' in admin docker_source = (ROOT / 'src/docker.rs').read_text(encoding='utf-8') management_ui = (ROOT / 'src/admin/web/management.js').read_text(encoding='utf-8') for marker in ['function aliasEditor(', 'host-chip-editor docker-alias-editor', '.docker-force-ssl', '.docker-websockets', '.docker-cache-enabled', '.docker-passive-enabled', '.docker-passive-5xx', '.docker-agent-acl-profile']: assert marker in docker_ui, marker for marker in ['"force_ssl": assignment.force_ssl.unwrap_or(false)', '"websockets": assignment.websockets', '"cache": {"enabled": assignment.cache_enabled}', '"passive_health": assignment.passive_health.clone()', 'assignment.acl_profile_id.clone().or_else(|| agent.acl_profile_id.clone())']: assert marker in docker_source, marker assert 'agent.acl_profile_id === profile.id' in management_ui vhost_source = (ROOT / 'src/vhost.rs').read_text(encoding='utf-8') for marker in ['DEFAULT_PROXY_CONNECT_TIMEOUT_SECONDS: u64 = 10', 'DEFAULT_PROXY_RESPONSE_HEADER_TIMEOUT_SECONDS: u64 = 120', 'DEFAULT_PROXY_READ_TIMEOUT_SECONDS: u64 = 60', 'DEFAULT_PROXY_SEND_TIMEOUT_SECONDS: u64 = 60', 'proxy_timeouts_have_explicit_safe_defaults_but_keep_null_compatibility']: assert marker in vhost_source, marker assert 'defaultProxyTimeouts = () => ({ connect_timeout_seconds: 10, response_header_timeout_seconds: 120, read_timeout_seconds: 60, send_timeout_seconds: 60 })' in vhosts_ui assert 'actions.append(enabledLabel, stateBadge, remove)' in docker_ui assert 'row._readListeners = listenerControl.read' in docker_ui and '.docker-agent-listeners' not in docker_ui assert 'docker-agent-status-badge' in style_source and 'justify-content: center' in style_source assert '.docker-agent-grid>label' in style_source and '#docker-agent-add' in style_source and 'margin-top: 14px' in style_source assert 'Bearer token:' in (ROOT / 'docker-agent/install.sh').read_text(encoding='utf-8') tls_rust = (ROOT / 'src/tls.rs').read_text(encoding='utf-8') assert 'pub fn validate_certificate_removals' in tls_rust and 'pub fn effective_cipher_suites' in tls_rust assert "renewals.json" not in main upgrade_source = (ROOT / "scripts/upgrade.py").read_text(encoding="utf-8") assert "removed_obsolete_runtime_state" in upgrade_source and "legacy-data" not in upgrade_source and "certs_dir.is_symlink()" in upgrade_source 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") for marker in ['"version": "0.24.9"', '"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"']: assert marker in openapi, marker for marker in ['"/api/certificate-api"', '"/api/certificate-api/{key_id}"', '"/api/certificates/{certificate_id}/reload"', '"CertificateReloadOneResult"', '"CertificateApiBearer"', '"CertificateApiKey"', '"/api/live-logs"']: assert marker in openapi, marker websocket_source = (ROOT / 'src/proxy/websocket.rs').read_text(encoding='utf-8') proxy_source = (ROOT / 'src/proxy/mod.rs').read_text(encoding='utf-8') for marker in ['websocket_legacy_support', 'vhWebsocketLegacySupport', 'vhWebsocketLegacySupportHelp']: assert marker in vhosts_ui or marker in openapi, marker for marker in ['fn legacy_websocket_origin(', 'request.headers_mut().insert("origin", origin)', 'websocket_legacy_support && !websocket_upstream_h2']: assert marker in proxy_source, marker for marker in ['legacy_support: bool', 'legacy 101 response declared a non-zero body', 'legacy_upstream_response_accepts_old_101_syntax_but_keeps_accept_proof_strict']: assert marker in websocket_source, marker for marker in ['certificate_api_credentials', 'upsert_certificate_api_credential', 'revoke_certificate_api_credential', 'verify_certificate_api_credential']: assert marker in db_source, marker logging_source = (ROOT / 'src/logging.rs').read_text(encoding='utf-8') for marker in ['LIVE_STREAM_CAPACITY', 'broadcast::Sender', 'pub fn subscribe']: assert marker in logging_source, marker for marker in ['livelogs: bool', 'LiveLogFilters', 'live_logs_command', 'LocalAdminCertificateVerifier', 'detect_local_admin_scheme', 'admin.uri("/api/live-logs")']: assert marker in main, marker assert '.uri(format!("http://{addr}/api/live-logs"))' not in main for marker in [ "allow_trusted_network_without_token", "include_docker_vhosts", "include_streams", "include_logging", "rust_proxy_docker_vhost_{name}", "rust_proxy_docker_backend_{name}", "rust_proxy_geoip_loaded", "not_after_timestamp_seconds", "ewma_latency_seconds", "passive_failures", "active_health_failures_total", "passive_health_failures_total", "http_5xx_total", "timeouts_total", "connect_errors_total", "io_errors_total", "slow_responses_total", "rust_proxy_log_sink_queue_dropped_total", "rust_proxy_not_found_hosts_configured", ]: assert marker in prometheus, marker assert "if docker_targets.contains_key(&pool.id)" in prometheus assert "if docker_targets.contains_key(id)" in prometheus assert "rust_proxy_docker_vhost_exchange_duration_seconds" in prometheus assert "PeerAddr" in prometheus and "Forwarded headers never participate in this decision" in prometheus transport_doc = (ROOT / "docs/TRANSPORT.md").read_text(encoding="utf-8") assert '127.0.0.1:9090' in transport_doc and 'accept_local_listener_risk: true' in transport_doc drop_impl = http3.split('impl Drop for ReceiveBody', 1)[1].split('#[cfg(test)]', 1)[0] assert 'self.stream.stop_sending(' not in drop_impl websocket_source = (ROOT / 'src/proxy/websocket.rs').read_text(encoding='utf-8') for marker in ['h3::quinn::RecvStream', 'websocket_rfc9220_on(local)', 'run_h3', 'H3_REQUEST_CANCELLED']: assert marker in http3 or marker in websocket_source, marker assert 'upstream: Upgraded' in websocket_source assert 'timeout(Duration::from_secs(10), upstream)' not in websocket_source for marker in ['websocket_phase("tunnel_started")', 'websocket_phase("tunnel_closed")', 'h3_reset("H3_REQUEST_CANCELLED")']: assert marker in websocket_source, marker logging_source = (ROOT / 'src/logging.rs').read_text(encoding='utf-8') for marker in ['http_version', 'transport', 'websocket_bootstrap', 'websocket_phase', 'websocket_error', 'h3_reset_code', 'upstream_status']: assert marker in logging_source, marker assert 'h3_quinn' not in http3 and 'h3_quinn' not in (ROOT / 'tests/http3.rs').read_text(encoding='utf-8') h3_test = (ROOT / 'tests/http3.rs').read_text(encoding='utf-8') assert 'RFC 9220: Extended CONNECT negotiates WebSocket on the same H3 stream.' in h3_test assert 'Method::CONNECT' in h3_test and 'h3::ext::Protocol' in h3_test and '.send_data(' in h3_test for marker in ['external_admin_authority', 'external_admin_scheme', 'trusted_admin_forwarded_proto', 'reverse_proxy_dns_allowlist_uses_external_port_and_keeps_direct_port_available']: assert marker in admin, marker def container_contract_smoke() -> None: dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8") unit = (ROOT / "deploy/rustproxy.service").read_text(encoding="utf-8") assert "LogsDirectory=rustproxy" in unit and "LogsDirectoryMode=0770" in unit assert "Environment=MALLOC_ARENA_MAX=2" in unit assert "TimeoutStopSec=120" in unit assert "ReadWritePaths=/var/lib/rustproxy /var/log/rustproxy" in unit generated_unit = deploy.service_unit({"prefix":"/opt/rustproxy", "data_dir":"/var/lib/rustproxy", "user":"rustproxy", "group":"rustproxy", "admin":"127.0.0.1:9090", "remote_admin":False, "http":"0.0.0.0:80", "https":"0.0.0.0:443"}) assert "LogsDirectory=rustproxy" in generated_unit and "/var/log/rustproxy" in generated_unit assert "Environment=MALLOC_ARENA_MAX=2" in generated_unit for marker in [ "FROM rust:1.88-trixie AS build", "cargo generate-lockfile", "cargo build --locked --release", "FROM debian:trixie-slim AS runtime", "USER rustproxy:rustproxy", 'VOLUME ["/var/lib/rustproxy"]', ]: assert marker in dockerfile, marker ignore = set((ROOT / ".dockerignore").read_text(encoding="utf-8").splitlines()) for marker in {"data", "target", ".env", "*.pem", "*.key"}: assert marker in ignore, marker assert (ROOT / "docs/CONTAINER.md").is_file() def debian_grafana_contract_smoke() -> None: control = (ROOT / "debian/control").read_text(encoding="utf-8") rules = (ROOT / "debian/rules").read_text(encoding="utf-8") service = (ROOT / "debian/rustproxy.service").read_text(encoding="utf-8") environment = (ROOT / "debian/rustproxy.environment").read_text(encoding="utf-8") logrotate = (ROOT / "debian/rustproxy.logrotate").read_text(encoding="utf-8") postinst = (ROOT / "debian/rustproxy.postinst").read_text(encoding="utf-8") repo = (ROOT / "debian/build-repository.sh").read_text(encoding="utf-8") dashboard = json.loads((ROOT / "grafana/rustproxy-overview.json").read_text(encoding="utf-8")) assert "debhelper-compat (= 13)" in control and "cargo" in control and "rustc" in control assert "cargo build --release --locked" in rules and "cargo test --locked" in rules for marker in ["User=rustproxy", "Group=rustproxy", "Environment=MALLOC_ARENA_MAX=2", "AmbientCapabilities=CAP_NET_BIND_SERVICE", "StateDirectory=rustproxy", "LogsDirectory=rustproxy", "ProtectSystem=strict", "ReadWritePaths=/var/lib/rustproxy /var/log/rustproxy"]: assert marker in service, marker assert "RUST_PROXY_ADMIN_LISTEN=127.0.0.1:9090" in environment for marker in ["daily", "rotate 3", "maxage 3", "compresscmd /usr/bin/gzip", "compressoptions -9", "copytruncate", "su rustproxy rustproxy"]: assert marker in logrotate, marker assert "/var/log/rustproxy" in postinst and "-o rustproxy -g rustproxy" in postinst assert "rustproxy.linuxiarz.pl" in repo and "dpkg-scanpackages" in repo and "gzip -9 -n" in repo assert dashboard.get("uid") == "rustproxy-0170" queries = json.dumps(dashboard) for marker in ["rust_proxy_process_rss_bytes", "rust_proxy_cache_transient_bytes", "rust_proxy_backend_ewma_latency_seconds", "rust_proxy_certificate_renewal_not_after_timestamp_seconds", "rust_proxy_not_found_hosts_configured", "rust_proxy_http1_requests_total"]: assert marker in queries, marker def reproducible_archive_smoke(tmp: Path) -> None: first = tmp / "first.zip" second = tmp / "second.zip" env = dict(os.environ, SOURCE_DATE_EPOCH="1700000000") subprocess.run(["python3", str(ROOT / "make_zip.py"), str(first)], check=True, env=env, stdout=subprocess.DEVNULL) subprocess.run(["python3", str(ROOT / "make_zip.py"), str(second)], check=True, env=env, stdout=subprocess.DEVNULL) first_bytes, second_bytes = first.read_bytes(), second.read_bytes() assert hashlib.sha256(first_bytes).digest() == hashlib.sha256(second_bytes).digest() with zipfile.ZipFile(first) as archive: names = archive.namelist() assert "RustProxy/SHA256SUMS" in names assert "RustProxy/src/admin/web/error_401.html" in names assert "RustProxy/src/admin/web/error_404.html" in names assert "RustProxy/docs/CUSTOM_LOCATIONS.md" in names assert "RustProxy/nagios/check_rustproxy.py" in names assert "RustProxy/nagios/check_rustproxy_api" in names assert "RustProxy/src/admin/web/host404.js" in names assert "RustProxy/docs/NOT_FOUND_HOSTS.md" in names assert "RustProxy/debian/control" in names assert "RustProxy/debian/rustproxy.service" in names assert "RustProxy/debian/rustproxy.logrotate" in names assert "RustProxy/grafana/rustproxy-overview.json" in names assert "RustProxy/UPDATE_CHECK.toml" in names assert "RustProxy/src/update_check.rs" in names assert "RustProxy/docker-agent/docker-agent" not in names assert all("/data/" not in name and "/target/" not in name for name in names) timestamps = {item.date_time for item in archive.infolist()} assert len(timestamps) == 1 def main() -> None: with tempfile.TemporaryDirectory(prefix="rustproxy-release-smoke-") as directory: tmp = Path(directory) sql_and_backup_smoke(tmp / "sql") upgrade_smoke(tmp / "upgrade") reproducible_archive_smoke(tmp / "archives") deployment_gate_smoke(tmp) npm_migration_smoke(tmp) ui_contract_smoke() source_contract_smoke() container_contract_smoke() debian_grafana_contract_smoke() print("PASS: 0.24.9 SQLite/upgrade, local-response 404 hosts, Prometheus/Grafana diagnostics, Debian packaging, cache/dashboard/logging/Nagios, retained admin/H3/stream/TLS contracts and reproducible source archives.") if __name__ == "__main__": main()