Skip to content

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-server and cargo test -p xylolabs-server --tests --lib do not run the ignored docker-backed integration tests by themselves. Use ./scripts/run-tests.sh (or cargo 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)