Testing Guide
Quick Reference
# Server tests
cargo test # All workspace tests
cargo test -p xylolabs-server # Server unit + non-ignored tests
./scripts/run-tests.sh # Full docker-backed server integration suite
# Protocol crate tests (encoder + decoder roundtrips)
cargo test -p xylolabs-protocol --all-features
# SDK core tests (client, session, ring buffer, config, health)
cargo test -p xylolabs-sdk --all-features
# Audio pipeline tests (decimation, I2S config, full encode pipeline -- runs on host)
cd tests/audio-pipeline && cargo test
# Frontend tests
cd frontend && npx tsc -b --noEmit && npx vite build && pnpm test
cd frontend-app && npx tsc -b --noEmit && npx vite build && pnpm test
cargo test -p xylolabs-serverandcargo test -p xylolabs-server --tests --libdo not run the ignored docker-backed integration tests by themselves. Use./scripts/run-tests.sh(orcargo test -p xylolabs-server --tests -- --ignored) when you need the full server integration lane.
Cross-Process Locking For DB-Gated Suites
Every crates/xylolabs-server/tests/api_*.rs suite goes through TestApp::setup() (crates/xylolabs-server/tests/common/mod.rs), which cleans and reseeds the shared TEST_DATABASE_URL database before each test. An in-process tokio::sync::Mutex already serializes TestApps within one test binary, but that offers no protection when a second process — another suite binary, another CI lane, another agent — targets the same database at the same time; one process's cleanup can interleave with another's fixture setup and produce spurious foreign-key violations that look like real regressions. setup() now also takes a Postgres session-level pg_advisory_lock (a fixed key, held on a dedicated non-pooled connection for the whole test) before cleaning the database, so a second process attempting the same thing simply queues on the lock instead of corrupting shared state. Running one DB-gated suite is unaffected; running two or more concurrently against the same database now serializes safely rather than racing. The acquisition is time-bounded rather than unbounded: if the lock cannot be taken within a generous budget, setup() fails loudly with a diagnostic naming the Postgres backend holding it (looked up from pg_locks) instead of hanging forever, so a stuck or crashed peer surfaces as an actionable error rather than a mysterious stall.
Rust SDK Tests
Protocol Crate
cargo test -p xylolabs-protocol --all-features
Tests XMBP encoder/decoder roundtrips for all 11 value types, boundary conditions, truncation handling, and performance benchmarks (encode/decode must complete in < 100 µs per 2 kHz batch).
SDK Core
cargo test -p xylolabs-sdk --all-features
Tests client initialization, session lifecycle, ring buffer SPSC correctness, config validation, health statistics, codec encode/decode, and cross-language parity with the C SDK.
Audio Pipeline
cd tests/audio-pipeline && cargo test
Runs on host (not cross-compiled). Tests: - FIR decimation (96kHz to 16kHz): coefficient verification, DC passthrough, stereo interleaving, continuity, near-Nyquist attenuation, max-amplitude overflow safety - Full encode pipeline (I2S to decimate to XAP): compression ratio, header format, silence/sine
54 host tests + 7 QEMU tests. CI-gated since cycle 3 (C3-TE-7): the host suite runs in .github/workflows/sdk-tests.yml (rust-tests job) on every push/PR.
Burn-In Tests
# Start test infrastructure + API server
docker compose -f docker-compose.test.yml up -d
cargo run --bin xylolabs-server --release # with test env vars
# Run burn-in scenarios (requires running API server)
./scripts/run-burnin.sh standard # 60s quick validation
./scripts/run-burnin.sh stress # 120s high throughput (4ch @96kHz)
./scripts/run-burnin.sh endurance # 120s+ sustained load
./scripts/run-burnin.sh multi-device # 10 concurrent devices
# QEMU ARM emulation (simulates ~150MHz RP2350)
./scripts/run-burnin.sh standard --qemu
# ESP32-S3 Xtensa QEMU emulation
./scripts/run-qemu-esp32.sh
# Cleanup
docker compose -f docker-compose.test.yml down -v
Pass/fail criteria: < 5% batch failure rate, < 3 reconnects. Performance metrics reported per run.
Legacy C SDK Tests
# Run all unit tests natively (requires gcc + libm)
gcc -Wall -Wextra -Werror -O2 -o /tmp/test_xmbp sdk/c/test/test_xmbp.c sdk/c/common/src/xmbp_encoder.c -Isdk/c/common/include -lm && /tmp/test_xmbp
gcc -Wall -Wextra -Werror -O2 -o /tmp/test_xap sdk/c/test/test_xap.c sdk/c/common/src/xap_encoder.c -Isdk/c/common/include -lm && /tmp/test_xap
gcc -Wall -Wextra -Werror -O2 -o /tmp/test_ring_buffer sdk/c/test/test_ring_buffer.c sdk/c/common/src/ring_buffer.c -Isdk/c/common/include && /tmp/test_ring_buffer
gcc -Wall -Wextra -O2 -o /tmp/test_client sdk/c/test/test_client.c sdk/c/common/src/xylolabs_client.c sdk/c/common/src/xmbp_encoder.c sdk/c/common/src/adpcm_encoder.c sdk/c/common/src/xap_encoder.c sdk/c/common/src/ring_buffer.c sdk/c/common/src/http_transport.c sdk/c/common/src/session_manager.c -Isdk/c/common/include -lm && /tmp/test_client
# Run all C tests via Docker (includes ARM cross-compilation syntax check + QEMU)
docker build -t xylolabs-sdk-test -f sdk/c/test/Dockerfile . && docker run --rm xylolabs-sdk-test
# Expected results: XMBP 34/34, XAP 45/45, Ring Buffer 358/358, Client 11/11
# Run E2E test suite (native + ARM cross-compile + QEMU emulation)
./scripts/run-sdk-e2e.sh
# Or manually: build and run E2E Docker image
docker build -f sdk/c/test/Dockerfile.e2e -t xylolabs-sdk-e2e . && docker run --rm xylolabs-sdk-e2e
# E2E covers: 8 native suites, 4 cross-compile targets (M4F/M33/RV64/Xtensa-compat), 16 QEMU-user + 3 QEMU-sys suites
# Run Xtensa (ESP32-S3) tests using Espressif IDF Docker image (~2GB)
./scripts/run-sdk-xtensa.sh
# Or include Xtensa tests in the main E2E suite
RUN_XTENSA=true ./scripts/run-sdk-e2e.sh
E2E Ingest Test
Located in tests/e2e-ingest/. Generates 4-channel sine wave audio (1/2/3/4 kHz), encodes with XAP, packages with XMBP, and uploads to the live API.
cd tests/e2e-ingest
API_KEY=xk_... cargo run --release
Tests the full pipeline: PCM → XAP encode → XMBP frame → HTTP POST with X-Api-Key → server ingest.
Remote E2E Pipeline Test
Located in tests/remote-e2e/. Tests against a live API (e.g. api.xylolabs.com) covering the full XMBP/XAP pipeline with metadata verification.
# Create .env in project root with:
# E2E_API_KEY=xk_...
# E2E_HOST=api.xylolabs.com
# E2E_EMAIL=admin@xylolabs.com
# E2E_PASSWORD=...
cd tests/remote-e2e
cargo run
Test rules (constants): - 4-channel audio: 1 kHz / 2 kHz / 3 kHz / 4 kHz sine waves - 16 kHz sample rate, 10 ms frames, 64 kbps, 500 ms duration - 3 metadata streams: temperature (f64), humidity (f64), pressure (f32) at 10 Hz
Steps: Login → Create ingest session (4 streams) → Send XMBP audio batch (50 XAP frames) → Send XMBP metadata batch (15 samples) → Close session → Query all 4 streams and verify sample counts → Upload XAP file → Verify transcode job created.
XAP Upload & Transcode Tests
Located in crates/xylolabs-server/tests/api_upload_transcode.rs. Tests XAP file upload and transcode job creation (requires Docker test infrastructure).
cargo test --test api_upload_transcode -- --ignored
XMBP Pipeline Tests
Located in crates/xylolabs-server/tests/api_xmbp_pipeline.rs. Full 4-channel XAP + metadata e2e test with ingest session lifecycle.
cargo test --test api_xmbp_pipeline -- --ignored
Atomicity Tripwire Tests
Located in crates/xylolabs-server/tests/api_atomicity_tripwires.rs. Compile-time static-assertion tests that verify critical atomicity invariants are preserved in the source code (e.g. that high-value DB writes are not fire-and-forget). These tests require no Docker and no running database — they pass or fail at compile time by inspecting source text at test startup.
cargo test -p xylolabs-server api_atomicity
XAP Decoder Tests
Unit tests for the server-side XAP decoder (inverse MDCT + dequantization).
cargo test -p xylolabs-transcode --lib xap_decode
23 tests grouped by topic: LC3 transport-header parsing + SDK tag validation + oversized-claim allocation guard (3); decode roundtrips — standard, truncated-tail, embedded-header resync, mid-stream misframe, single-channel extraction, cross-codec liblc3↔lc3-codec parity (6); RMS/dBFS conversion + legacy/garbage none-returns (6); legacy sample-rate inference (2); legacy-frame decode — short header + silence (2); and stream-decoder framing/resync — whole-blob, mid-packet split, drop-before-header, resync-after-corruption (4).
Browser Validation
Headless Playwright script that validates all admin dashboard pages load correctly.
cd tests && npm install playwright && npx playwright install chromium
node browser-validate.mjs
Checks: Login → Dashboard → Uploads → Transcode → Metadata Sessions → Devices → Alerts → Users → API Keys → Settings → Firmware → Facilities, with explicit browser console and pageerror validation.
Frontend Tests
cd frontend && npx tsc -b --noEmit && npx vite build && pnpm test
cd frontend-app && npx tsc -b --noEmit && npx vite build && pnpm test
Frontend unit/component tests (frontend/src: 50 *.test.ts(x) files; frontend-app/src: 32 — enumerate with find frontend/src frontend-app/src -name '*.test.ts*'). Representative suites:
- authStore.test.ts -- Auth store state management
- client.test.ts -- API client layer
- ProtectedRoute.test.tsx -- Route protection component
- RoleGate.test.tsx -- Role-based UI gating
- Sidebar.test.tsx -- Sidebar navigation component
Test Coverage
Audio pipeline (54 host tests + 7 QEMU tests): - FIR decimation (96kHz to 16kHz): coefficient verification, DC passthrough, stereo interleaving, continuity, near-Nyquist attenuation, max-amplitude overflow safety - Full encode pipeline (I2S to decimate to XAP): compression ratio, header format, silence/sine - QEMU ESP32 firmware: decimation + full pipeline verification
Known Test Coverage Gaps
Server (Priority 1) (list re-audited cycle 3, C3-TE-6 — the previous
bullets had gone stale):
- ~~IngestManager: zero unit tests~~ — now covered (ingest/manager.rs carries 20+ unit tests: sequence gap wrapping, backpressure, flush guard, stale eviction)
- ~~WebSocket ingest: zero integration tests~~ — now covered (tests/api_ingest.rs: binary batches, gap detection, wraparound, watchdog stale-session close, dongle-ownership rejection, manager-cache-loss recovery)
- ~~JWT middleware tamper/expiry coverage~~ — now covered (C3-TE-1, cycle 3: tests/api_auth.rs test_me_endpoint_rejects_tampered_signature + test_me_endpoint_rejects_expired_token); RBAC middleware unit tests unchanged (middleware/rbac.rs)
SDK (Priority 2 -- legacy C only):
- xylolabs_client.c: audio_feed, meta_feed, tick paths untested (only init covered)
- session_send_audio: entirely untested
- session_ensure_active reconnect: exponential backoff untested
- ADPCM codec path: not validated in QEMU CI matrix
Security:
- No brute-force rate limiting test for API keys
- ~~No JWT signature verification test~~ — covered by the C3-TE-1 tamper test (tests/api_auth.rs)