#!/usr/bin/env python3 """Static contracts for 0.33.0 WWW observability (not Rust compilation).""" from pathlib import Path import json import re import tomllib ROOT = Path(__file__).resolve().parents[2] read = lambda file: (ROOT / file).read_text() manifest = tomllib.loads(read('Cargo.toml')) lock = tomllib.loads(read('Cargo.lock')) version = manifest['package']['version'] assert tuple(map(int, version.split('.'))) >= (0, 33, 0) assert [p['version'] for p in lock['package'] if p['name'] == 'rust-proxy'] == [version] registry = json.loads(read('src/admin/web/dashboard-widgets.json')) widgets = [w for w in registry if w['id'].startswith('www_')] assert len(widgets) == 12 assert all(w['default_hidden'] is True for w in widgets) assert len({w['id'] for w in registry}) == len(registry) html = read('src/admin/web/index.html') english = json.loads(read('langs/en_US.json'))['translations'] polish = json.loads(read('langs/pl_pl.json'))['translations'] for widget in widgets: assert f'data-dashboard-widget="{widget["id"]}" hidden' in html, widget['id'] assert widget['label_key'] in english, widget['label_key'] assert widget['label_key'] in polish, widget['label_key'] if widget['description_key']: assert widget['description_key'] in english assert widget['description_key'] in polish # Safely assemble the admin app before core.js: the earlier release crashed on # registering onChange callbacks before rustProxyOverviewSamples was defined. router = read('src/admin/router.rs') assert router.index('include_str!("web/app/overview-samples.js")') < router.index('include_str!("web/app/core.js")') assert '"web/app/web-overview.js"' in router assert '"web_server_metrics"' in read('src/admin/status_live.rs') assert 'web_server_metrics_payload(&snapshot)' in read('src/admin/status.rs') # record_web is only consumed by web_server::serve; expose no wider than the # ResponseKind enum argument. This is a static visibility contract, not rustc. metrics = read('src/web_server/metrics.rs') assert 'pub(super) enum ResponseKind' in metrics assert 'pub(super) fn record_web(' in metrics assert 'pub(crate) fn record_web(' not in metrics assert 'service.web_metrics.record_web(' in read('src/web_server/serve.rs') families = read('src/prometheus/web_php.rs') required = { 'rust_proxy_web_server_requests_total', 'rust_proxy_web_server_responses_total', 'rust_proxy_web_server_request_kind_total', 'rust_proxy_web_server_methods_total', 'rust_proxy_web_server_status_code_total', 'rust_proxy_web_server_response_header_duration_microseconds_total', 'rust_proxy_web_server_response_body_bytes_total', 'rust_proxy_web_server_requests_active', 'rust_proxy_web_server_compression_total', 'rust_proxy_php_requests_total', 'rust_proxy_php_responses_total', 'rust_proxy_php_response_header_duration_microseconds_total', 'rust_proxy_php_response_body_bytes_total', 'rust_proxy_php_concurrency_available', 'rust_proxy_php_concurrency_limit', 'rust_proxy_php_concurrency_in_use', 'rust_proxy_php_failures_total' } assert all(f'"{name}"' in families for name in required), required assert 'PHP_FAILURE_NAMES' in families assert re.search(r'\["GET", "POST", "PUT", "DELETE", "other"\]', families) assert 'let wl = labels(&[("web_service", service.id.as_str())]);' in families j = json.loads(read('grafana/rustproxy-overview.json')) assert j['title'].startswith('RustProxy 0.33.') ids = [p['id'] for p in j['panels']] assert len(ids) == len(set(ids)) assert any(v.get('name') == 'web_service' and v.get('multi') and v.get('includeAll') for v in j['templating']['list']) for title in ('Web Server 0.33.0', 'PHP-FPM / FastCGI', 'Per Web Service', 'WWW / PHP correlation'): assert any(p['title'] == title and p['type'] == 'row' for p in j['panels']) extra = [p for p in j['panels'] if p['id'] >= 89] for i, p in enumerate(extra): a = p['gridPos'] assert 0 <= a['x'] and a['w'] > 0 and a['x'] + a['w'] <= 24 for q in extra[i + 1:]: b = q['gridPos'] overlap = a['x'] < b['x'] + b['w'] and b['x'] < a['x'] + a['w'] and a['y'] < b['y'] + b['h'] and b['y'] < a['y'] + a['h'] assert not overlap, (p['id'], q['id']) production = [p for p in (ROOT / 'src').rglob('*.rs') if p.name != 'tests.rs' and 'tests' not in p.parts] large = [str(p.relative_to(ROOT)) for p in production if len(p.read_text().splitlines()) > 500] assert not large, large print('PASS RustProxy 0.33.0 source version, opt-in widgets, localizations, status API,') print(' exporter families, Grafana geometry/filtering and Rust module size (STATIC ONLY)')