test: framework integration tests + mock RPC server (#16) #31

Merged
charles merged 1 commit from feat/16-mock-server-tests into main 2026-04-11 18:43:45 +00:00
Owner

Closes #16. Stacked on #30.

Summary

Adds tests/framework_tests.rs: a separate integration test crate that exercises the framework's public API end-to-end against an in-process mock server. Complements the per-module unit tests (in src/*.rs) which already cover internal invariants.

Fixtures

  • spawn_health_server() — raw HTTP 200 responder on an ephemeral port
  • spawn_mock_rpc_server() — tokio_tungstenite accept loop speaking JSON-RPC 2.0 with a small vocabulary:
    • ping / <anything> → echoes method + params (default path)
    • fail → JSON-RPC error -32601
    • notify → emits N notifications, then responds {sent: N} (exercises the race-prone path from #7)
    • list_items → 3-element array for assert_array!
  • harness_for(health_port, ws_port)ProcessHarness pointed at the fixtures with a short health deadline

Integration tests

  • end_to_end_happy_path_with_runner_and_hooks — runner with before_all + after_all + three tests covering call, assert_ok!, assert_array!, and RPC error handling; verifies the lifecycle order
  • end_to_end_filter_limits_which_tests_run"gallery*" filter, one test counted as filtered
  • end_to_end_subscribe_and_wait_for_eventsubscribe + call("notify", ...) + wait_for with predicate
  • end_to_end_call_and_wait_is_race_free — uses the notify-then-respond handler
  • end_to_end_fail_fast_short_circuitsfail_fast = true, asserts skipped count

Checklist (from issue #16)

Mock server

  • Listens on an ephemeral port
  • Speaks JSON-RPC 2.0 over WebSocket
  • Supports per-method response mapping (fail, notify, list_items, default echo)
  • Emits notifications via notify method

Self-tests

  • RpcClient::call happy path + JSON-RPC error path — exercised via fail method
  • subscribe + wait_for with predicate
  • call_and_wait race-prone path (server emits event before response)
  • Runner hooks (before_all, after_all)
  • Filtering
  • Fail-fast
  • Assertion macros (assert_ok!, assert_field!, assert_field_exists!, assert_array!)

Performance

  • Full suite runs under 30 seconds: 89 unit + 5 integration tests complete in ≈ 1.1 s
  • All fixtures use ephemeral ports — no collisions in CI

Test plan

  • just qa green locally — 94/94 tests pass (89 unit + 5 integration)
  • cargo test --test framework_tests works in isolation
  • CI green on Forgejo

Notes for the reviewer

  • The mock server is intentionally simple — the exhaustive coverage of every RpcClient method / every assertion macro / every hook failure path lives in the per-module unit tests. The integration file verifies that the public seams fit together, nothing more. Adding more coverage here would just duplicate what unit tests already prove.
  • I deliberately didn't expose the mock server as a pub feature of the main crate (the issue allows either tests/mock_server.rs or src/testing/mock.rs behind a feature). A separate test fixture is lighter weight and doesn't grow the library's API surface.
  • No external test-fixture deps — everything uses crates already in the dep tree (futures-util, tokio, tokio-tungstenite, serde_json).
  • The integration test suite imports via use ws_rpc_test::{...}, which proves the prelude re-exports from #1 all resolve correctly for external consumers.
Closes #16. **Stacked on #30.** ## Summary Adds `tests/framework_tests.rs`: a separate integration test crate that exercises the framework's **public API** end-to-end against an in-process mock server. Complements the per-module unit tests (in `src/*.rs`) which already cover internal invariants. ### Fixtures - `spawn_health_server()` — raw HTTP 200 responder on an ephemeral port - `spawn_mock_rpc_server()` — tokio_tungstenite accept loop speaking JSON-RPC 2.0 with a small vocabulary: - `ping` / `<anything>` → echoes method + params (default path) - `fail` → JSON-RPC error `-32601` - `notify` → emits N notifications, then responds `{sent: N}` (exercises the race-prone path from #7) - `list_items` → 3-element array for `assert_array!` - `harness_for(health_port, ws_port)` — `ProcessHarness` pointed at the fixtures with a short health deadline ### Integration tests - `end_to_end_happy_path_with_runner_and_hooks` — runner with `before_all` + `after_all` + three tests covering `call`, `assert_ok!`, `assert_array!`, and RPC error handling; verifies the lifecycle order - `end_to_end_filter_limits_which_tests_run` — `"gallery*"` filter, one test counted as filtered - `end_to_end_subscribe_and_wait_for_event` — `subscribe` + `call("notify", ...)` + `wait_for` with predicate - `end_to_end_call_and_wait_is_race_free` — uses the notify-then-respond handler - `end_to_end_fail_fast_short_circuits` — `fail_fast = true`, asserts skipped count ## Checklist (from issue #16) ### Mock server - [x] Listens on an ephemeral port - [x] Speaks JSON-RPC 2.0 over WebSocket - [x] Supports per-method response mapping (`fail`, `notify`, `list_items`, default echo) - [x] Emits notifications via `notify` method ### Self-tests - [x] `RpcClient::call` happy path + JSON-RPC error path — exercised via `fail` method - [x] `subscribe` + `wait_for` with predicate - [x] `call_and_wait` race-prone path (server emits event before response) - [x] Runner hooks (`before_all`, `after_all`) - [x] Filtering - [x] Fail-fast - [x] Assertion macros (`assert_ok!`, `assert_field!`, `assert_field_exists!`, `assert_array!`) ### Performance - [x] Full suite runs under 30 seconds: **89 unit + 5 integration tests complete in ≈ 1.1 s** - [x] All fixtures use ephemeral ports — no collisions in CI ## Test plan - [x] `just qa` green locally — 94/94 tests pass (89 unit + 5 integration) - [x] `cargo test --test framework_tests` works in isolation - [ ] CI green on Forgejo ## Notes for the reviewer - The mock server is intentionally simple — the exhaustive coverage of every `RpcClient` method / every assertion macro / every hook failure path lives in the per-module unit tests. The integration file verifies that the **public seams** fit together, nothing more. Adding more coverage here would just duplicate what unit tests already prove. - I deliberately didn't expose the mock server as a `pub` feature of the main crate (the issue allows either `tests/mock_server.rs` or `src/testing/mock.rs behind a feature`). A separate test fixture is lighter weight and doesn't grow the library's API surface. - No external test-fixture deps — everything uses crates already in the dep tree (`futures-util`, `tokio`, `tokio-tungstenite`, `serde_json`). - The integration test suite imports via `use ws_rpc_test::{...}`, which proves the prelude re-exports from #1 all resolve correctly for external consumers.
Adds tests/framework_tests.rs: a separate integration test crate that
exercises the framework's public API end-to-end against an in-process
mock server. Complements the per-module unit tests in src/harness.rs /
client.rs / runner.rs / assert.rs which already cover internal
invariants.

Fixtures
- spawn_health_server: raw HTTP 200 responder on an ephemeral port
- spawn_mock_rpc_server: tokio_tungstenite::accept_async loop speaking
  JSON-RPC 2.0. Handles a small vocabulary:
    - ping / <anything>  → echoes method + params (default path)
    - fail               → JSON-RPC error code -32601
    - notify             → emits N notifications then responds {sent: N}
    - list_items         → returns a 3-element array for assert_array!
- harness_for(health_port, ws_port) builds a ProcessHarness pointed
  at the two fixtures with a short health deadline.

Integration tests (5 new in tests/framework_tests.rs)
- end_to_end_happy_path_with_runner_and_hooks: runner with before_all +
  after_all + three tests exercising call, assert_ok!, assert_array!,
  and server error handling. Verifies the full lifecycle order.
- end_to_end_filter_limits_which_tests_run: name filter "gallery*"
  counts one test as filtered.
- end_to_end_subscribe_and_wait_for_event: subscribe + call("notify")
  + wait_for with predicate.
- end_to_end_call_and_wait_is_race_free: uses the notify-then-respond
  handler to exercise the race-prone path from #7; asserts both call
  result and event params.
- end_to_end_fail_fast_short_circuits: fail_fast=true, second test
  fails, third recorded as Skipped.

These tests access ws_rpc_test's public API only — they live in tests/
so they compile as a separate crate, matching what end users would
see. Same result as a doctest would give, with more end-to-end reach.

just qa green: 89 unit + 5 integration = 94/94 tests, fmt + clippy
-D warnings clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
charles left a comment

Review — tests d'intégration framework + mock RPC server (#16)

Isolation publique correcte

Tests dans tests/ → crate séparée → uniquement la surface publique de ws_rpc_test. Exactement la bonne approche pour valider les seams end-to-end.

Mock server

spawn_mock_rpc_server() supporte ping, fail, notify, list_items et echo-passthrough. Bien aligné avec les besoins des tests. Code dupliqué avec les helpers unit-test — acceptable pour des tests d'intégration qui doivent être auto-contenus.

Couverture

  • end_to_end_happy_path_with_runner_and_hooks — vérifie les hooks + 3 tests dont un qui valide assert_array!.
  • end_to_end_filter_limits_which_tests_run — filtre par nom.
  • end_to_end_subscribe_and_wait_for_event — subscribe + wait_for en end-to-end.
  • end_to_end_call_and_wait_is_race_free — le cas le plus important : notification émise avant la réponse RPC.
  • end_to_end_fail_fast_short_circuits — fail-fast vérifié.

Minor

  • spawn_health_server() et spawn_mock_rpc_server() sont pub dans un fichier tests/. Si d'autres fichiers tests/*.rs arrivent, les déplacer dans un tests/helpers.rs avec mod helpers; serait plus propre. Pour l'instant c'est OK.

Aucun bloquant.

## Review — tests d'intégration framework + mock RPC server (#16) ### Isolation publique correcte Tests dans `tests/` → crate séparée → uniquement la surface publique de `ws_rpc_test`. Exactement la bonne approche pour valider les seams end-to-end. ### Mock server `spawn_mock_rpc_server()` supporte `ping`, `fail`, `notify`, `list_items` et echo-passthrough. Bien aligné avec les besoins des tests. Code dupliqué avec les helpers unit-test — acceptable pour des tests d'intégration qui doivent être auto-contenus. ### Couverture - `end_to_end_happy_path_with_runner_and_hooks` — vérifie les hooks + 3 tests dont un qui valide `assert_array!`. - `end_to_end_filter_limits_which_tests_run` — filtre par nom. - `end_to_end_subscribe_and_wait_for_event` — subscribe + wait_for en end-to-end. - `end_to_end_call_and_wait_is_race_free` — le cas le plus important : notification émise avant la réponse RPC. - `end_to_end_fail_fast_short_circuits` — fail-fast vérifié. ### Minor - `spawn_health_server()` et `spawn_mock_rpc_server()` sont `pub` dans un fichier `tests/`. Si d'autres fichiers `tests/*.rs` arrivent, les déplacer dans un `tests/helpers.rs` avec `mod helpers;` serait plus propre. Pour l'instant c'est OK. Aucun bloquant.
charles left a comment

Pas de bloquant — tests d'intégration bien isolés sur la surface publique, couverture des cas critiques (call_and_wait race-free, fail-fast).

✅ Pas de bloquant — tests d'intégration bien isolés sur la surface publique, couverture des cas critiques (call_and_wait race-free, fail-fast).
charles force-pushed feat/16-mock-server-tests from b1819a8810 to 469a515e9e 2026-04-11 18:41:56 +00:00 Compare
charles changed target branch from feat/12-parallel-mode to main 2026-04-11 18:43:37 +00:00
charles deleted branch feat/16-mock-server-tests 2026-04-11 18:43:46 +00:00
Sign in to join this conversation.
No description provided.