mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-10-16 23:29:57 +00:00
Fixes #661 (the narrowed scope from the 2026-10-14 comment). ## What changes - `GET /api/observers/metrics/summary` adds `tx_airtime_pct_1h` and `rx_airtime_pct_1h` per observer: the airtime share over the hour before now, whatever `window` is, from the cumulative `tx_air_secs` / `rx_air_secs` counters in `observer_metrics`. - A counter that goes down marks a reboot; only the stretch after the last reset counts. - Under 15 minutes of samples (one sample, or a reboot late in the hour) returns `null`. - Clamped to 0..100, rounded to 2 decimals. - The response also carries `duty_cycle_warn_pct`, from the new config key `dutyCycleWarnPct` (default 10, the EU 868 MHz g3 sub-band limit). Added to `config.example.json`. - RF Health tiles show `TX 1h: x%`; the observer list gets a sortable numeric `TX 1h` column. Both use the shared `public/observer-airtime.js` helper and flag values at or over the threshold with `.airtime-over` (`var(--status-red-text)`). - The handler now returns a named `MetricsSummaryResponse` struct instead of `map[string]interface{}` (one occurrence fewer). ## Why the warning matters Repeater and room-server firmware default to `airtime_factor = 1.0` (`examples/simple_repeater/MyMesh.cpp:890`), which `Dispatcher.cpp:26` turns into a 50% TX budget per 1 h window. The device therefore does not keep itself under the EU 10% limit; this view is where an operator can see it. ## Cost - Server: one extra query per summary request, `SELECT observer_id, timestamp, tx_air_secs, rx_air_secs FROM observer_metrics WHERE timestamp >= now-1h`, on the existing `idx_observer_metrics_timestamp`, then one O(rows) pass. On our instance that is 35 observers x about 12 samples (5 min interval). - Observer list: one extra bulk call `summary?window=1h` per load (client TTL 30 s, failure ignored), not a call per observer. ## Tests - Go: `TestRollingAirtimePct` (steady, reboot mid-hour, single sample, span under 15 min, reboot leaving under 15 min, nil counters, clamp, rounding), `TestGetMetricsSummaryAirtime1h` (samples older than 1 h ignored inside a 24 h window, single-sample observer gets null), `TestMetricsSummaryDutyCycleWarnPct` (default 10, config override, `observers` is `[]` not `null`). Full `cmd/server` suite passes locally (skipping `TestSaveGeoFilterPreservesFileMode`, which fails on Windows on clean master too). - Frontend unit: `tests/unit/test-issue-661-observer-airtime.js` (8 assertions, real file in `vm`), added to `test-all.sh`. `test-observers-headings.js` updated for the new column. Full `test-all.sh` passes locally. - E2E: `tests/e2e/test-issue-661-airtime-e2e.js`, registered in `deploy.yml` and `scripts/non-unit-tests.json`. The fixture DB has no `observer_metrics` table, so the summary is served through `page.route` with known values for the fixture's own observer ids. It checks the column header, over/under/unknown cells, sort order and the RF Health tile. 4/4 locally. It caught a real bug during development: `loadRFHealthData` declares a local `const window`, so the tile calls the helper as the global `ObserverAirtime`. ## Validation Deployed to a staging instance with live MQTT data (35 observers with metrics) and checked in the browser: - The observer list shows the column for 42 rows (33 with a value, 9 without enough samples). Sorting puts the highest share (5.3%) first. - RF Health shows `TX 1h` on all 35 tiles. With a fourth stat the line wrapped inside items on narrow tiles; the stats row now wraps between items only. - No observer was over 10% at the time, so the red state was checked by rendering one cell client-side at 12.5% (computed colour `rgb(185, 28, 28)` in light mode), and by the E2E test. ## Not done - The threshold is config-only; exposing it in the customizer is a follow-up. - No airtime comparison across observers (that is #671) and no per-packet airtime (Relay Airtime Share covers it). - Dark mode was not checked in the browser; `--status-red-text` is defined for both themes. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
211 lines
9.5 KiB
Bash
Executable File
211 lines
9.5 KiB
Bash
Executable File
#!/bin/sh
|
|
# Authoritative standalone frontend test list for local runs and CI.
|
|
# Every suite in tests/unit is listed here; every suite in tests/e2e is classified
|
|
# in scripts/non-unit-tests.json.
|
|
set -e
|
|
cd "$(dirname "$0")"
|
|
|
|
node tests/unit/test-test-inventory.js
|
|
node tests/unit/test-1108-region-hide-nodes.js
|
|
node tests/unit/test-1659-analytics-warmup.js
|
|
node tests/unit/test-a11y-1705-subpath-hop-prefix-e2e.js
|
|
node tests/unit/test-a11y-1715-dark-role-swatches.js
|
|
node tests/unit/test-a11y-1716-rf-range-btn-active.js
|
|
node tests/unit/test-a11y-1719-contrast-root-causes-e2e.js
|
|
node tests/unit/test-a11y-1996-scope-audit-chips.js
|
|
node tests/unit/test-a11y-axe-1668-selftest.js
|
|
node tests/unit/test-a11y-axe-routes-coverage.js
|
|
node tests/unit/test-admin-dashboard-ui.js
|
|
node tests/unit/test-aging.js
|
|
node tests/unit/test-analytics-channels-integration.js
|
|
node tests/unit/test-anl1-tooltip-render.js
|
|
node tests/unit/test-area-filter.js
|
|
node tests/unit/test-channel-color-picker.js
|
|
node tests/unit/test-channel-colors.js
|
|
node tests/unit/test-channel-decrypt-ecb.js
|
|
node tests/unit/test-channel-decrypt-insecure-context.js
|
|
node tests/unit/test-channel-decrypt-m345.js
|
|
node tests/unit/test-channel-fluid-layout.js
|
|
node tests/unit/test-channel-issue-1087.js
|
|
node tests/unit/test-channel-issue-1101.js
|
|
node tests/unit/test-channel-live-decrypt-userprefix.js
|
|
node tests/unit/test-channel-live-decrypt.js
|
|
node tests/unit/test-channel-modal-ux.js
|
|
node tests/unit/test-channel-proposals-ui.js
|
|
node tests/unit/test-channel-psk-ux.js
|
|
node tests/unit/test-channel-qr-wiring.js
|
|
node tests/unit/test-channel-qr.js
|
|
node tests/unit/test-channel-sidebar-layout.js
|
|
node tests/unit/test-channel-ux-followup.js
|
|
node tests/unit/test-channel-ux-round2.js
|
|
node tests/unit/test-channels-approved-ui.js
|
|
node tests/unit/test-channels-merge-1498-unit.js
|
|
node tests/unit/test-clear-filters.js
|
|
node tests/unit/test-color-picker-ux.js
|
|
node tests/unit/test-compare-flood-filter.js
|
|
node tests/unit/test-compare-overlap.js
|
|
node tests/unit/test-confidence-indicator.js
|
|
node tests/unit/test-coverage-gate.js
|
|
node tests/unit/test-customizer-v2.js
|
|
node tests/unit/test-drag-manager.js
|
|
node tests/unit/test-e2e-coverage.js
|
|
node tests/unit/test-direct-rf-heard-by.js
|
|
node tests/unit/test-embed-mode-1369.js
|
|
node tests/unit/test-fetch-all-nodes-pagination.js
|
|
node tests/unit/test-fluid-scaffolding.js
|
|
node tests/unit/test-frontend-helpers.js
|
|
node tests/unit/test-geofilter-draft.js
|
|
node tests/unit/test-hash-color.js
|
|
node tests/unit/test-hop-resolver-affinity.js
|
|
node tests/unit/test-issue-1065-gesture-hints-gates.js
|
|
node tests/unit/test-issue-1107-live-layout.js
|
|
node tests/unit/test-issue-1136-observer-iata-map.js
|
|
node tests/unit/test-issue-1166-first-seen-column.js
|
|
node tests/unit/test-issue-1189-composed-cell.js
|
|
node tests/unit/test-issue-1189-live-iata-badge.js
|
|
node tests/unit/test-issue-1279-p2-code-filter.js
|
|
node tests/unit/test-issue-1293-marker-shapes.js
|
|
node tests/unit/test-issue-1356-map-a11y.js
|
|
node tests/unit/test-issue-1360-pill-letter-count.js
|
|
node tests/unit/test-issue-1361-cb-presets.js
|
|
node tests/unit/test-issue-1364-pill-no-clamp.js
|
|
node tests/unit/test-issue-1375-scope-stats-fetch.js
|
|
node tests/unit/test-issue-1380-cb-reset-button.js
|
|
node tests/unit/test-issue-1380-cb-sim-overlay.js
|
|
node tests/unit/test-issue-1407-cb-preset-propagation.js
|
|
node tests/unit/test-issue-1409-no-encrypted-flood.js
|
|
node tests/unit/test-issue-1412-customizer-no-override.js
|
|
node tests/unit/test-issue-1415-packets-layout.js
|
|
node tests/unit/test-issue-1418-cb-preset-ramp.js
|
|
node tests/unit/test-issue-1418-deeplink-hops-channels.js
|
|
node tests/unit/test-issue-1418-edge-weights.js
|
|
node tests/unit/test-issue-1418-polish-review.js
|
|
node tests/unit/test-issue-1418-raw-hex-extraction.js
|
|
node tests/unit/test-issue-1418-spider-fan.js
|
|
node tests/unit/test-issue-1420-tile-providers.js
|
|
node tests/unit/test-issue-1438-customizer-mcrole.js
|
|
node tests/unit/test-issue-1438-marker-css-vars.js
|
|
node tests/unit/test-issue-1446-cb-preset-cascade.js
|
|
node tests/unit/test-issue-1450-logo-aspect.js
|
|
node tests/unit/test-issue-1454-channels-toggle.js
|
|
node tests/unit/test-issue-1456-score-labels.js
|
|
node tests/unit/test-issue-1461-mobile-page-actions.js
|
|
node tests/unit/test-issue-1470-card-bg-contrast.js
|
|
node tests/unit/test-issue-1470-node-tile-helper.js
|
|
node tests/unit/test-issue-1473-prefix-generator.js
|
|
node tests/unit/test-issue-1473-reserved-prefixes.js
|
|
node tests/unit/test-issue-1485-live-anim-z.js
|
|
node tests/unit/test-issue-1488-marker-stroke-vars.js
|
|
node tests/unit/test-issue-1496-reset-all-complete.js
|
|
node tests/unit/test-issue-1509-detect-preset.js
|
|
node tests/unit/test-issue-1509-nav-active-bg.js
|
|
node tests/unit/test-issue-1518-home-url.js
|
|
node tests/unit/test-issue-1532-live-fullscreen.js
|
|
node tests/unit/test-issue-1562-observers-summary.js
|
|
node tests/unit/test-issue-661-observer-airtime.js
|
|
node tests/unit/test-issue-1563-aggregate-and-inflight.js
|
|
node tests/unit/test-issue-1574-live-map-max-nodes.js
|
|
node tests/unit/test-issue-1606-pagination.js
|
|
node tests/unit/test-issue-1614-tile-url-function.js
|
|
node tests/unit/test-issue-1619-feed-detail-card-draggable.js
|
|
node tests/unit/test-issue-1633-hide-1byte-hops.js
|
|
node tests/unit/test-issue-1644-redesign.js
|
|
node tests/unit/test-issue-1646-compare-polish.js
|
|
node tests/unit/test-issue-1648-followup-phosphor-leaks.js
|
|
node tests/unit/test-issue-1648-m1-emoji-scan.js
|
|
node tests/unit/test-issue-1648-m2-emoji-scan.js
|
|
node tests/unit/test-issue-1648-m3-emoji-scan.js
|
|
node tests/unit/test-issue-1648-m4-emoji-scan.js
|
|
node tests/unit/test-issue-1648-m5-emoji-scan.js
|
|
node tests/unit/test-issue-1648-m6-final-sweep.js
|
|
node tests/unit/test-issue-1648-m6-lint-self.js
|
|
node tests/unit/test-issue-1668-m2-contrast.js
|
|
node tests/unit/test-issue-1668-m3-typography.js
|
|
node tests/unit/test-issue-1668-m4-per-route.js
|
|
node tests/unit/test-issue-1697-mqtt-mobile-e2e.js
|
|
node tests/unit/test-issue-1699-retransmission-chart.js
|
|
node tests/unit/test-issue-1705-subpath-contrast.js
|
|
node tests/unit/test-issue-1753-copy-url-slash.js
|
|
node tests/unit/test-issue-1770-mobile-row-clamp.js
|
|
node tests/unit/test-issue-1789-observer-firmware-cols.js
|
|
node tests/unit/test-issue-1825-observer-node-cross-links.js
|
|
node tests/unit/test-issue-1833-legend-toggle-vcr-offset.js
|
|
node tests/unit/test-issue-1836-crossnav-case-normalization.js
|
|
node tests/unit/test-issue-1843-node-qr-quiet-zone.js
|
|
node tests/unit/test-issue-1846-observers-width.js
|
|
node tests/unit/test-issue-1849-trace-hashbytes.js
|
|
node tests/unit/test-issue-1851-channel-message-scope.js
|
|
node tests/unit/test-issue-1862-map-region-filter.js
|
|
node tests/unit/test-issue-1868-control-decode.js
|
|
node tests/unit/test-packet-detail-channel-escape.js
|
|
node tests/unit/test-packet-detail-hash-size.js
|
|
node tests/unit/test-issue-1890-og-url.js
|
|
node tests/unit/test-issue-1956-release-routing.js
|
|
node tests/unit/test-issue-1979-scope-adverts-by-role.js
|
|
node tests/unit/test-issue-1997-distance-building.js
|
|
node tests/unit/test-issue-2042-recent-adverts-label.js
|
|
node tests/unit/test-issue-2001-map-scope-state.js
|
|
node tests/unit/test-issue-2012-clear-filters-selection.js
|
|
node tests/unit/test-issue-2095-channels-client-state.js
|
|
node tests/unit/test-issue-2097-hop-ambiguity-badge.js
|
|
node tests/unit/test-issue-2098-last-hop-region.js
|
|
node tests/unit/test-live-anims.js
|
|
node tests/unit/test-live-dt-cap-1524.js
|
|
node tests/unit/test-live-legend-helper.js
|
|
node tests/unit/test-live-multibyte-filter.js
|
|
node tests/unit/test-live-region-filter.js
|
|
node tests/unit/test-live.js
|
|
node tests/unit/test-map-clustering.js
|
|
node tests/unit/test-mqtt-status-panel.js
|
|
node tests/unit/test-my-repeaters-dashboard.js
|
|
node tests/unit/test-naive-banner-tone.js
|
|
node tests/unit/test-nav-stats-throttle.js
|
|
node tests/unit/test-node-hop-analytics.js
|
|
node tests/unit/test-node-reach-coverage-debounce.js
|
|
node tests/unit/test-node-reach-coverage.js
|
|
node tests/unit/test-nodes-export-wiring.js
|
|
node tests/unit/test-nav-drawer-version-footer.js
|
|
node tests/unit/test-nodes-export.js
|
|
node tests/unit/test-notifications-ui.js
|
|
node tests/unit/test-observer-iata-1188.js
|
|
node tests/unit/test-observer-menu-interactions.js
|
|
node tests/unit/test-observer-naive-clock-1478.js
|
|
node tests/unit/test-observer-search-filter.js
|
|
node tests/unit/test-observers-headings.js
|
|
node tests/unit/test-packet-filter-time.js
|
|
node tests/unit/test-packet-filter-ux.js
|
|
node tests/unit/test-packet-filter.js
|
|
node tests/unit/test-packets-local-channels.js
|
|
node tests/unit/test-packets-live-channel-filter.js
|
|
node tests/unit/test-packets.js
|
|
node tests/unit/test-panel-corner.js
|
|
node tests/unit/test-path-inspector.js
|
|
node tests/unit/test-payload-labels-content.js
|
|
node tests/unit/test-payload-labels-namespace.js
|
|
node tests/unit/test-perf-anomaly.js
|
|
node tests/unit/test-perf-disk-io-1120.js
|
|
node tests/unit/test-perf-go-runtime.js
|
|
node tests/unit/test-perf-render-1258.js
|
|
node tests/unit/test-preflight-xss-gate.js
|
|
node tests/unit/test-pull-to-reconnect-1091.js
|
|
node tests/unit/test-pull-to-reconnect.js
|
|
node tests/unit/test-region-filter-setselected.js
|
|
node tests/unit/test-repeater-metric-scatter.js
|
|
node tests/unit/test-rx-coverage-config-race.js
|
|
node tests/unit/test-rx-coverage-mine.js
|
|
node tests/unit/test-rx-coverage-escape.js
|
|
node tests/unit/test-rx-coverage-noise.js
|
|
node tests/unit/test-rx-coverage-viewport.js
|
|
node tests/unit/test-scope-audit-styles-linked.js
|
|
node tests/unit/test-settings-sync.js
|
|
node tests/unit/test-slideover-1056-rowsel-strict.js
|
|
node tests/unit/test-top-routes-overlay.js
|
|
node tests/unit/test-traces.js
|
|
node tests/unit/test-url-state.js
|
|
node tests/unit/test-user-management-ui.js
|
|
node tests/unit/test-warmup-banner.js
|
|
node tests/unit/test-ws-stale-watchdog-1074.js
|
|
node tests/unit/test-xss-escape-sinks.js
|
|
|
|
echo "All standalone frontend suites passed"
|