blockwatcher-stellar-testkit
blockwatcher-stellar-testkit is a scripted JSON-RPC mock node
blockwatcher-stellar’s own tests share, so that no test in that crate has to
run against a live stellar-rpc endpoint. Its crate doc states why the mock
is a crate rather than a #[cfg(test)] module: “Consumed exclusively via
[dev-dependencies]: nothing here ever reaches a production binary. Its
crate boundary exists so this mock can be shared by both a unit test
module (#[cfg(test)] code inside src/) and any number of tests/*.rs
integration binaries, without duplicating it or tripping the dead-code
lint on whichever MockReply variants one particular consumer happens
not to construct” (crates/blockwatcher-stellar-testkit/src/lib.rs). See
Workspace map § Verifying the rings
for how scripts/check-dep-graph.sh checks the
dev-dependency-only half of that by name, the same way it checks
blockwatcher-testkit and blockwatcher-evm-testkit.
It declares no workspace crate in [dependencies]: the mock is a
self-contained axum listener. There is no production cycle with
blockwatcher-stellar; that crate reaches this one only as a
[dev-dependencies] edge.
The harness and the system under test line up like this: a scripted node
on one side, a real stellar-rpc source on the other, meeting over the
same JSON-RPC wire a real node would speak:
flowchart LR
mock["mock_node<br/>scripted json-rpc"] --> src["stellar-rpc source<br/>blockwatcher-stellar"]
src --> test["test asserts on<br/>decoded events, cursors"]
Its production dependencies:
axum: the HTTP servermock_nodebindsreqwest: the HTTP client this crate’s own unit test dialsserde_json: the JSON-RPC request/response shapeshandle_requestbuildstokio(net/rt-multi-thread/sync/timefeatures): the listener andDelayedResult’s wait run onurl: parsing the bound address intoMockNode::url
Key takeaways
blockwatcher-stellar-testkitis a scripted JSON-RPC mock nodeblockwatcher-stellar’s own tests share, so no test needs a live stellar-rpc endpoint.- It is consumed exclusively via
[dev-dependencies]; it declares no workspace crate in[dependencies]. - A scripted node stands in for a real node on one side, and a real
stellar-rpcsource runs unmodified on the other, meeting over the same wire protocol.
Responsibilities
- Runs a scripted JSON-RPC node on an ephemeral loopback port:
mock_nodetakes a handler closure over(method, params)and answers whateverMockReplyit returns, so a test swaps behavior by the closure it passes rather than by editing this crate (lib.rs).mock_stellar_rpcis the same constructor, returning(url, node)together. - Dispatches a JSON-RPC batch (an array body) through that same handler
in request order and answers with the members’ replies as an array.
MockNode::bodiesis the raw POST body of every request the node received, which is how a test tells one batched request from several single ones — the handler alone cannot, since it sees a batch member exactly as it sees a lone request (lib.rs). - Provides
CallLog, which records every JSON-RPC call a mock node’s handler observed, method and params rather than just method, so a test can assert not only call order but which ledger a specific call named (lib.rs).
Not this crate’s job: implementing stellar-rpc itself
(blockwatcher-stellar owns the source; this crate only mocks the wire
it speaks to), standing in for a chain-agnostic port directly
(blockwatcher-ports’ fakes feature does that; this crate mocks one
family’s wire protocol underneath a real module, not the port trait
above it), or building a Pool<StellarEndpoint> (blockwatcher-stellar’s
tests assemble that against MockNode::url). It has no in-memory chain
fixture and no WebSocket mock: stellar-rpc polls HTTP JSON-RPC for
closed ledgers.
Key types and functions
| Name | Kind | Role |
|---|---|---|
mock_node | async fn | Starts a scripted HTTP JSON-RPC node on an ephemeral port, returning once it accepts connections (lib.rs) |
mock_stellar_rpc | async fn | mock_node, returning (url, node) together (lib.rs) |
MockNode | struct | The running node’s handle; url is where to point an endpoint, bodies() is every raw POST body in arrival order, and dropping it aborts the listener task (lib.rs) |
MockReply | enum | One scripted answer: Result, Error, Status, StatusWithHeaders, RawBody, Hang, or DelayedResult (lib.rs) |
CallLog | struct | Records every call a handler observed, method and params, in order (lib.rs) |
The mock JSON-RPC node
mock_node‘s handler sees each request’s method and params and returns a
MockReply. A JSON object body is one call. A JSON array body is a
batch: each member is dispatched through the same handler in request
order, and the HTTP response is the members’ JSON-RPC replies as an
array.
RawBody, Hang, StatusWithHeaders, and DelayedResult exist for
shapes Result, Error, and Status cannot express: a body missing
both result and error, one carrying both, a well-formed JSON-RPC
error wrapped in a non-2xx status, a quota window named in a
Retry-After header rather than a body, a node that accepts the
connection and never answers (so the caller’s own timeout ends the
call), and a successful result held open for after so overlapping
in-flight calls stay in flight.
The crate doc names the mock liberties a batch test must not lean on:
Deliberate mock liberties a batch test must not lean on: a member reply
that is not a per-member body — a bare status, a raw body, or a hang —
answers the whole batch, and the members after it are never dispatched,
where a real node has no such coupling between one member and the
response's status line. There is no partial-batch reply at all.
(crates/blockwatcher-stellar-testkit/src/lib.rs)
Neighbours
This crate depends on no workspace crate in production. The following
crate depends on it, under [dev-dependencies] only (per the
dependency table, and checked by name
per Workspace map § Verifying the
rings):
blockwatcher-stellar:MockNode,MockReply,CallLog, andmock_node, imported inside#[cfg(test)]insource/run.rs
Reading the source
lib.rs’s crate doc comment: the crate-boundary rationale, the ephemeral-port binding rule, batch dispatch, and the mock liberties, before any of the code that implements them.MockReply,mock_node,MockNode,handle_request,dispatch(lib.rs): the HTTP JSON-RPC mock.CallLog(lib.rs): the call-recording typesource/run.rsasserts against.- The crate’s own
mock_stellar_rpc_answers_jsonrpctest: the smallest proof the listener speaks JSON-RPC.