85 lines
4.5 KiB
Python
Executable File
85 lines
4.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Static contracts for 0.33.0 WWW observability (not Rust compilation)."""
|
|
from pathlib import Path
|
|
from panel_html import render_admin_index
|
|
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 = render_admin_index(ROOT)
|
|
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)')
|