mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-06-07 00:31:38 +00:00
cde62166cb
- Release notes for 95 commits since v3.4.1 - OpenAPI/Swagger docs: /api/spec and /api/docs called out everywhere - Deployment guide: new API Documentation section - README: API docs link added - FAQ: 'Where is the API documentation?' entry - Test plans for v3.4.2 validation
310 lines
14 KiB
Markdown
310 lines
14 KiB
Markdown
# v3.4.2 Release Test Plan
|
|
|
|
**Scope:** 90 commits since v3.4.1 (84 files, +14,931 / -1,005)
|
|
**Categories:** 19 perf, 19 feat, 18 fix, 15 docs, 3 chore, 1 test, 1 refactor, 1 ci
|
|
**Date:** 2026-04-08
|
|
|
|
---
|
|
|
|
## A. Automated Tests — Verify All Pass
|
|
|
|
### Go Backend
|
|
```bash
|
|
cd cmd/server && go test -race -count=1 ./...
|
|
cd cmd/ingestor && go test -race -count=1 ./...
|
|
```
|
|
|
|
**Test files (27 total):**
|
|
|
|
| File | Tests For |
|
|
|------|-----------|
|
|
| `cmd/server/decoder_test.go` | Hash size zero-hop, TRACE hopsCompleted, transport direct |
|
|
| `cmd/server/backfill_async_test.go` | **NEW** — Async chunked backfill |
|
|
| `cmd/server/eviction_test.go` | Memory eviction with runtime heap stats |
|
|
| `cmd/server/apikey_security_test.go` | **NEW** — Weak/default API key rejection |
|
|
| `cmd/server/openapi_test.go` | **NEW** — OpenAPI spec generation |
|
|
| `cmd/server/routes_test.go` | Batch observations endpoint, subpaths-bulk, expand=observations |
|
|
| `cmd/server/cache_invalidation_test.go` | cacheTTL config wiring |
|
|
| `cmd/server/config_knobs_test.go` | cacheTTLSec helper |
|
|
| `cmd/server/helpers_test.go` | constantTimeEqual, IsWeakAPIKey |
|
|
| `cmd/server/obs_dedup_test.go` | UniqueObserverCount tracking |
|
|
| `cmd/server/neighbor_*.go` (4 files) | Neighbor graph, affinity, persistence |
|
|
| `cmd/server/perfstats_race_test.go` | Perf stats concurrency |
|
|
| `cmd/server/resolve_context_test.go` | Resolved path filtering |
|
|
| `cmd/server/advert_pubkey_test.go` | Advert pubkey tracking |
|
|
| `cmd/server/db_test.go` | SQLite operations |
|
|
| `cmd/server/config_test.go` | Config loading |
|
|
| `cmd/server/coverage_test.go` | Coverage helpers |
|
|
| `cmd/server/parity_test.go` | Go/JS decoder parity |
|
|
| `cmd/server/websocket_test.go` | WebSocket broadcast |
|
|
| `cmd/ingestor/decoder_test.go` | Ingestor decoder (hash size zero-hop) |
|
|
| `cmd/ingestor/db_test.go` | Ingestor DB writes |
|
|
| `cmd/ingestor/config_test.go` | Ingestor config |
|
|
| `cmd/ingestor/main_test.go` | Ingestor entry |
|
|
| `cmd/ingestor/coverage_boost_test.go` | Coverage helpers |
|
|
|
|
### Frontend Unit Tests
|
|
```bash
|
|
node test-packet-filter.js
|
|
node test-aging.js
|
|
node test-frontend-helpers.js
|
|
node test-table-sort.js # NEW — shared table sort utility
|
|
node test-channel-colors.js # NEW — channel color model
|
|
node test-panel-corner.js # NEW — panel corner toggle
|
|
node test-packets.js # NEW — packets page logic
|
|
node test-hop-resolver-affinity.js
|
|
node test-customizer-v2.js
|
|
node test-live.js
|
|
node test-live-dedup.js
|
|
```
|
|
|
|
### E2E / Playwright
|
|
```bash
|
|
BASE_URL=http://localhost:13581 node test-e2e-playwright.js
|
|
```
|
|
|
|
**Expected:** All existing tests pass + new tests added for sortable tables, deep linking, collapsible panels.
|
|
|
|
---
|
|
|
|
## B. Manual Browser Verification
|
|
|
|
### B1. HIGH RISK — Data Correctness
|
|
|
|
| # | Feature | Page | What to Check |
|
|
|---|---------|------|---------------|
|
|
| 1 | Hash size zero-hop | Packets detail | Find a direct (route_type=0) packet → hash_size should show 0, not a bogus computed value |
|
|
| 2 | TRACE hopsCompleted | Packets detail / Live map | Find a TRACE packet → verify `hopsCompleted` shows in decoded JSON, live map shows real path length vs intended |
|
|
| 3 | Transport direct hash size | Packets detail | Find route_type=RouteTransportDirect packet → hash_size=0 |
|
|
| 4 | resolved_path filtering | Node detail → Paths tab | Verify path-hop candidates use resolved_path, no prefix collision false positives |
|
|
| 5 | Hash stats repeater filter | Analytics → Hash Issues | "By Repeaters" should only show nodes with repeater role, not companions/sensors |
|
|
| 6 | Async chunked backfill | Server startup | Start server with large DB → verify HTTP serves within 2 minutes, `X-CoreScope-Status: backfilling` header present, then transitions to `ready` |
|
|
| 7 | Memory eviction (heap stats) | Admin/stats | Verify `/api/stats` shows realistic memory numbers from runtime heap, not the old estimation |
|
|
| 8 | Distance/subpath/path-hop indexes | Analytics → Distances, Subpaths | Verify analytics data matches v3.4.1 output (no missing or extra entries) |
|
|
| 9 | cacheTTL config wiring | Config | Set `cacheTTL.analyticsHashSizes: 300` in config → verify collision cache respects it |
|
|
|
|
### B2. MEDIUM RISK — User-Facing Features
|
|
|
|
| # | Feature | Page | What to Check |
|
|
|---|---------|------|---------------|
|
|
| 10 | Distance unit preference | Nodes detail, Map | Toggle km/mi/auto in settings → distances update throughout UI |
|
|
| 11 | Panel corner toggle | Live page | Click corner toggle → panel moves to opposite corner, persists on reload |
|
|
| 12 | Noise floor column chart | Analytics → RF | Verify column chart renders with color-coded thresholds, hover shows values |
|
|
| 13 | Deep linking UI states | All pages | Navigate to `#/nodes?tab=neighbors`, `#/packets?observer=X`, `#/channels?node=Y` → correct state loads. Copy URL, open in new tab → same state |
|
|
| 14 | Sortable tables | Nodes list, Neighbors, Observers | Click column headers → sort asc/desc, indicator arrow shows, persists correctly |
|
|
| 15 | Channel color highlighting | Channels, Live feed | Assign color to channel → feed rows show that color, persists on reload |
|
|
| 16 | Mobile accessibility | All pages (phone viewport) | Touch targets ≥44px, ARIA labels present, small viewport doesn't overflow |
|
|
| 17 | Collapsible panels | Live map | Collapse/expand panels, medium breakpoint auto-collapses, state persists |
|
|
| 18 | Byte-size map filter | Map page | Filter by byte size → markers update correctly |
|
|
| 19 | OpenAPI/Swagger | `/api/spec`, `/api/docs` | Spec loads valid JSON, Swagger UI renders and all endpoints are documented |
|
|
| 20 | API key rejection | Protected endpoints | Send weak key (e.g. "changeme", "test123") → 403 forbidden |
|
|
| 21 | Channel color picker mobile | Channels (phone viewport) | Color picker usable on touch, doesn't overflow |
|
|
| 22 | RF Health dashboard | Analytics → RF Health | Observer metrics grid, airtime charts, battery charts, error rate, region filter |
|
|
| 23 | Prefix Tool tab | Analytics → Prefix Tool | Renders correctly, collision data consistent with Hash Issues |
|
|
| 24 | View Route on Map | Packet detail page | Button works and shows route on map |
|
|
|
|
### B3. LOWER RISK — Performance (Verify No Regressions)
|
|
|
|
| # | Feature | Page | What to Check |
|
|
|---|---------|------|---------------|
|
|
| 25 | Incremental DOM diff | Packets (30K+) | Virtual scroll renders smoothly, no visible flicker |
|
|
| 26 | Coalesced WS renders | Live page | Rapid packets don't cause frame drops (rAF coalescing) |
|
|
| 27 | Marker reposition on zoom | Map | Zoom/resize → markers move smoothly, no full rebuild flash |
|
|
| 28 | Parallel replay fetches | Live → VCR | Replay loads quickly (parallel observation fetches) |
|
|
| 29 | Batch observations API | Packets page (sort change) | Changing sort fetches observations in batch (network tab: 1 POST not N GETs) |
|
|
| 30 | Client-side network status | Analytics | No separate API call for network status |
|
|
| 31 | og-image compression | `/og-image.png` | Verify loads, ~235KB not ~1.1MB |
|
|
|
|
---
|
|
|
|
## C. API Regression Tests
|
|
|
|
Run against a local server with test-fixture DB:
|
|
|
|
```bash
|
|
BASE=http://localhost:13581
|
|
|
|
# Core endpoints — verify response shape
|
|
curl -s "$BASE/api/stats" | jq '.totalPackets, .backfilling, .backfillProgress'
|
|
curl -s "$BASE/api/packets?limit=5" | jq '.packets[0] | keys'
|
|
curl -s "$BASE/api/packets?limit=5&expand=observations" | jq '.packets[0].observations | length'
|
|
curl -s "$BASE/api/nodes?limit=5" | jq '.[0] | keys'
|
|
|
|
# New endpoints
|
|
curl -s -X POST "$BASE/api/packets/observations" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"hashes":["test123"]}' | jq '.results | keys'
|
|
|
|
curl -s "$BASE/api/analytics/subpaths-bulk?hops=A,B&hops=B,C" | jq 'keys'
|
|
|
|
curl -s "$BASE/api/observers/metrics/summary" | jq 'type'
|
|
curl -s "$BASE/api/spec" | jq '.openapi'
|
|
curl -s "$BASE/api/docs" | head -5 # Should return HTML
|
|
|
|
# Backfill status header
|
|
curl -sI "$BASE/api/stats" | grep X-CoreScope-Status
|
|
|
|
# API key rejection
|
|
curl -s -H 'X-API-Key: changeme' "$BASE/api/debug/vars" | jq '.error'
|
|
curl -s -H 'X-API-Key: test' "$BASE/api/debug/vars" | jq '.error'
|
|
|
|
# Existing endpoints — verify not broken
|
|
curl -s "$BASE/api/analytics/rf?timeRange=24h" | jq 'keys'
|
|
curl -s "$BASE/api/analytics/hash-sizes" | jq 'type'
|
|
curl -s "$BASE/api/analytics/distances" | jq 'type'
|
|
curl -s "$BASE/api/analytics/subpaths" | jq 'type'
|
|
curl -s "$BASE/api/channels" | jq 'type'
|
|
curl -s "$BASE/api/config/client" | jq 'keys'
|
|
```
|
|
|
|
### Expected response shape changes from v3.4.1:
|
|
- `/api/stats` now includes `backfilling` (bool) and `backfillProgress` (float 0-1)
|
|
- `/api/packets` no longer strips observations by default (lazy via `ExpandObservations` flag) — verify `observations` key absent without `expand=observations`
|
|
- Decoded packets with route_type=direct now have `hashSize: 0`
|
|
- TRACE packets now have `path.hopsCompleted` field
|
|
|
|
---
|
|
|
|
## D. Performance Regression Tests
|
|
|
|
### D1. Server Startup Time
|
|
```bash
|
|
# Start server with production-size DB (~30K packets)
|
|
# Measure time from process start to first successful HTTP response
|
|
time curl -s http://localhost:13581/api/stats > /dev/null
|
|
# Target: < 2 minutes (async backfill requirement)
|
|
```
|
|
|
|
### D2. Go Benchmarks
|
|
```bash
|
|
cd cmd/server && go test -bench=. -benchmem -count=3
|
|
```
|
|
Key benchmarks to compare with v3.4.1 baseline:
|
|
- `BenchmarkQueryPackets` — should not regress with new indexes
|
|
- `BenchmarkEvictStale` — batch removal from secondary indexes
|
|
- `BenchmarkGetStoreStats` — 2 concurrent queries vs 5 sequential
|
|
- `BenchmarkIngestNew` — additional index maintenance overhead
|
|
|
|
### D3. Frontend Performance
|
|
- Open Packets page with 30K+ packets → measure initial render time (DevTools Performance tab)
|
|
- Scroll rapidly through virtual scroll → should maintain 60fps
|
|
- Switch sort column on packets → single batch POST, not N+1 GETs
|
|
- Open Analytics page → no redundant API calls in network tab
|
|
|
|
### D4. Memory Usage
|
|
- After loading 30K packets, check `/api/stats` memory figure
|
|
- Compare with v3.4.1 baseline (prefix map cap at 8 chars should reduce ~10x)
|
|
- Verify eviction triggers at correct memory threshold using runtime heap stats
|
|
|
|
---
|
|
|
|
## E. Infrastructure / Deployment Tests
|
|
|
|
### E1. Docker Build
|
|
```bash
|
|
docker build -t corescope:test .
|
|
docker run --rm -p 13581:13581 corescope:test
|
|
# Verify: container starts, HTTP responds, WebSocket connects
|
|
```
|
|
|
|
### E2. GHCR Publish (CI)
|
|
- Verify CI publishes to `ghcr.io/kpa-clawbot/corescope`
|
|
- Verify tags: `edge` (master), `vX.Y.Z` (release)
|
|
|
|
### E3. Staging Deploy
|
|
```bash
|
|
# Verify staging compose works with standard ports
|
|
docker compose -f docker-compose.staging.yml up -d
|
|
# Check: no 3GB memory limit, standard port binding
|
|
```
|
|
|
|
### E4. DISABLE_CADDY
|
|
```bash
|
|
docker run --rm -e DISABLE_CADDY=true corescope:test
|
|
# Verify: Caddy not started, Go server serves directly
|
|
```
|
|
|
|
### E5. CI Pipeline
|
|
- Verify consolidated pipeline: build → publish GHCR → deploy staging
|
|
- Verify runs on `meshcore-runner-2`
|
|
|
|
---
|
|
|
|
## F. Edge Cases & Integration Tests
|
|
|
|
### F1. Cross-Feature Interactions
|
|
| Scenario | Risk |
|
|
|----------|------|
|
|
| Deep link to sorted table → sort state matches URL params | Medium |
|
|
| Channel color + deep link → color persists in linked URL | Medium |
|
|
| Panel corner toggle + collapsible panels → both states persist independently | Low |
|
|
| Distance unit pref + neighbor table sort by distance → sort uses correct unit | Medium |
|
|
| Noise floor chart + region filter → chart respects filter | Medium |
|
|
| Byte-size map filter + channel color highlighting → both active simultaneously | Low |
|
|
|
|
### F2. Data Correctness Edge Cases
|
|
| Scenario | Risk |
|
|
|----------|------|
|
|
| Zero-hop TRACE packet (should NOT reset hashSize — TRACE exemption) | **High** |
|
|
| Packet with all hops having same 2-char prefix → resolved_path filtering prevents false match | **High** |
|
|
| Node that switches role (repeater → companion) → hash stats updates | Medium |
|
|
| Backfill interrupted mid-chunk (server restart) → resumes or completes on next start | Medium |
|
|
| Empty DB startup → no errors, backfill completes instantly | Low |
|
|
| DB with 100K+ packets → async backfill doesn't OOM, progress reported | **High** |
|
|
|
|
### F3. Concurrency / Race Conditions
|
|
| Scenario | Risk |
|
|
|----------|------|
|
|
| Concurrent API requests during backfill → no deadlock (lock ordering documented) | **High** |
|
|
| Eviction running while analytics query in progress → no stale pointer panic | **High** |
|
|
| Multiple WebSocket clients during high ingest rate → coalesced broadcasts don't drop | Medium |
|
|
| `time.NewTicker` cleanup on graceful shutdown (replaced `time.Tick`) | Low |
|
|
|
|
### F4. API Key Security
|
|
| Scenario | Expected |
|
|
|----------|----------|
|
|
| No API key configured → write endpoints disabled | 403 "write endpoints disabled" |
|
|
| Weak key "changeme" → rejected even if configured | 403 "forbidden" |
|
|
| Timing-safe comparison → no timing oracle | Constant-time via `crypto/subtle` |
|
|
| Empty string key → rejected | 401 "unauthorized" |
|
|
|
|
### F5. Browser Compatibility
|
|
- Test on Chrome, Firefox, Safari (latest)
|
|
- Test on iOS Safari, Android Chrome
|
|
- Verify touch targets on mobile (44px minimum)
|
|
- Verify ARIA labels with screen reader
|
|
|
|
---
|
|
|
|
## G. Test Coverage Gaps — Action Items
|
|
|
|
| Gap | Priority | Action |
|
|
|-----|----------|--------|
|
|
| No automated test for distance unit preference rendering | Medium | Add Playwright test |
|
|
| No automated test for noise floor column chart | Medium | Add Playwright test |
|
|
| No automated test for deep link state restoration | **High** | Add Playwright tests for each deep-linkable state |
|
|
| No automated test for channel color persistence | Medium | `test-channel-colors.js` covers model; need Playwright for UI |
|
|
| No automated test for mobile viewport behavior | Medium | Add Playwright test with mobile viewport |
|
|
| No automated test for backfill progress header | Low | Add to `routes_test.go` |
|
|
| No automated test for `time.NewTicker` cleanup | Low | Add to graceful shutdown test |
|
|
| Observer metrics endpoints not covered in route tests | Medium | Add to `routes_test.go` |
|
|
| Subpaths-bulk endpoint needs test | Medium | Add to `routes_test.go` |
|
|
| No load test for batch observations endpoint (200 hash limit) | Low | Add boundary test |
|
|
|
|
---
|
|
|
|
## H. Release Checklist
|
|
|
|
- [ ] All Go tests pass with `-race` flag
|
|
- [ ] All frontend unit tests pass
|
|
- [ ] Playwright E2E tests pass
|
|
- [ ] Manual browser verification (Section B) complete
|
|
- [ ] API regression tests (Section C) pass
|
|
- [ ] Docker build succeeds
|
|
- [ ] Staging deploy verified
|
|
- [ ] No console errors on any page
|
|
- [ ] Performance spot-checks (Section D) — no regressions
|
|
- [ ] Coverage badges updated (backend ≥85%, frontend ≥42%)
|
|
- [ ] CHANGELOG updated
|
|
- [ ] Tag `v3.4.2` created
|