No description
  • Rust 97.9%
  • Just 2.1%
Find a file
Claude Desktop 13130710b0
All checks were successful
qa / qa (push) Successful in 1m5s
refactor(ci): use shared ci-base:v1 image, simplify ci-setup
2026-04-13 18:45:56 +00:00
.forgejo/workflows refactor(ci): use shared ci-base:v1 image, simplify ci-setup 2026-04-13 18:45:56 +00:00
examples docs: add README and basic + loom_e2e examples (closes #17) 2026-04-11 20:40:18 +02:00
src test(e2e): real binary e2e suite with test-server process 2026-04-11 20:56:01 +02:00
tests style: apply rustfmt to e2e test file 2026-04-11 22:30:34 +02:00
.gitignore chore: bootstrap project scaffolding, CI workflows, and just recipes 2026-04-11 13:33:10 +02:00
Cargo.lock feat(harness): builder, lifecycle, stdout/stderr ring buffers (closes #2) 2026-04-11 14:04:16 +02:00
Cargo.toml test(e2e): real binary e2e suite with test-server process 2026-04-11 20:56:01 +02:00
justfile ci: make workflows project-agnostic via just recipes 2026-04-11 13:45:04 +02:00
README.md docs: add README and basic + loom_e2e examples (closes #17) 2026-04-11 20:40:18 +02:00
rust-toolchain.toml chore: bootstrap project scaffolding, CI workflows, and just recipes 2026-04-11 13:33:10 +02:00
ws-rpc-test.md chore: specs 2026-04-11 11:47:43 +02:00

ws-rpc-test

End-to-end testing framework for applications that expose a JSON-RPC 2.0 API over WebSocket.

Think Puppeteer, but for backend APIs: start a process, connect via WebSocket, send commands, wait for events, assert results.

Motivation

Modern desktop and server applications increasingly expose control APIs over WebSocket for remote management, AI agent integration, and automation. Testing these APIs end-to-end requires:

  • starting the application process
  • waiting for it to become healthy
  • connecting a WebSocket client
  • sending JSON-RPC requests and validating responses
  • subscribing to events and waiting for specific notifications
  • handling timeouts, retries, and cleanup

This is repetitive plumbing. ws-rpc-test provides it as a reusable framework so application developers write only test logic.

Quickstart

use std::time::Duration;
use ws_rpc_test::{assert_field_exists, assert_ok, prelude::*};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let harness = ProcessHarness::builder()
        .command("target/debug/my-app")
        .health_url("http://127.0.0.1:8080/health")
        .ws_url("ws://127.0.0.1:8080/ws")
        .build();

    let mut runner = TestRunner::new(harness);
    runner.parse_cli_args(); // reads --filter, --fail-fast, etc.

    runner.test("health check", |c| async move {
        let r = c.call("health_check", json!({})).await?;
        assert_ok!(r);
        Ok(())
    });

    runner.test("create item", |c| async move {
        let r = c.call("create_item", json!({"name": "test"})).await?;
        assert_field_exists!(r, "id");
        Ok(())
    });

    let report = runner.run().await?;
    std::process::exit(if report.failed > 0 { 1 } else { 0 });
}

Features

  • Process harness — spawn, health-poll, SIGTERM-then-SIGKILL shutdown, stdout/stderr capture, auto-forward DISPLAY / XDG_RUNTIME_DIR for GUI apps
  • WebSocket JSON-RPC clientcall, call_timeout, subscribe, wait_for, wait_for_any, collect_events, call_and_wait (race-free)
  • Assertion macrosassert_ok!, assert_field!, assert_field_exists!, assert_array!, assert_error!, and more
  • Test runnerbefore_all / before_each / after_each / after_all hooks, name / tag filtering, declarative skip_if, runtime Skip, fail-fast, optional parallel mode with isolated connections
  • Reporters — console (colour, real-time, TTY-aware), JSON, JUnit XML
  • CLI — clap-based parse_cli_args() one-liner, gated behind the default-on cli feature

Examples

  • examples/basic.rs — minimal 5-test suite mirroring spec §9
  • examples/loom_e2e.rs — illustrative consumer example mirroring spec §10 (GUI, generation flow, gallery / workflow / preset)

Build them with:

cargo build --examples
cargo run --example basic -- --binary target/debug/my-app

Status

v0.1.0 milestone in progress. See the milestone on forge.jacquin.app for the remaining stories.

License

MIT OR Apache-2.0