Skip to main content

InProcess

Trait InProcess 

Source
pub trait InProcess: Broker<Connected: TestableBroker> {
    // Required method
    fn connect_in_process(
        self,
    ) -> impl Future<Output = Result<Self::Connected, Self::Error>> + Send;
}
Expand description

A broker that runs inside the test process.

connect_in_process is the consuming transition a test takes instead of connect: it produces the broker’s own connected form, backed by an in-process transport, with no I/O. The connected form is the one production gets, so the app’s routes, publish policies and subscription descriptors resolve against it unchanged, and TestApp runs the service’s production app as it is.

The transport has no configuration of its own. It reads every setting from the broker it was built from (default group, prefetch, topology declaration, whatever the real broker applies), so a test cannot run against settings production does not have. It never succeeds where the real broker fails: a publish, a settlement or a subscription the server would reject is rejected in process the same way.

A broker crate implements this under its testing feature, next to TestableBroker on the connected form, and registers the broker with register_testable_broker!. MemoryBroker is the reference: it has no server, so its in-process transition is its ordinary connect.

The in-process mode lives in the production types: the connected form, its subscriber, its publishers and its delivery each gain an in-process variant that exists only under the testing feature. Without the feature there is one variant and no branch, and a service’s production build is unchanged, because Cargo does not enable a dev-dependency’s features in a production binary. A mount that compiles against the broker compiles in process, and a capability is the same type in both.

The same test also runs against a stand: TestApp::start_live connects the broker through its ordinary connect and needs nothing beyond the connected form and its DefaultPublish policy, through which the harness publishes a test’s input.

§Writing one you can trust

A service’s whole test suite runs against this transport, so every difference from the real one is a green test for behaviour production does not have.

  • Run the conformance suites in process, not only against a server: run_suite, then lifecycle and the capability suites through InProcessBroker. lifecycle asks what an in-process transport is rarely asked: whether a publisher created before the shutdown errors afterwards. A transport whose publish is a channel send accepts the message instead.
  • Settle the way the transport settles. Where the real ack answers AckError::Unsupported, the in-process one answers the same; Ok(()) there is how a handler returning retry() passes in a test and loses the message in production.
  • Reproduce what the client does; do not fake what the broker does. Competing consumers, group distribution, correlation, reply routing and buffering until commit are client-side or routing-level, and an in-process copy of them is exact. Cluster atomicity, fencing, broker-held timeouts and exactly-once are broker-side, and the live mode covers them. Competing consumers matter most: handing every message of a queue to every subscriber of it is a fan-out, and two workers sharing the queue then each run the whole stream with no error anywhere.
  • Keep what the real broker keeps. A queue or a log that exists before the subscription delivers what reached it earlier; declare that with TestableBroker::backlog, and read a setting that decides it (an earliest offset, a deliver-all consumer) off the connected form. backlog_matches_server checks the declaration against the server.
  • Read the production wiring. A check the real broker makes at subscribe or publish time (a declaration it refuses, a name too long for the protocol, a header it cannot carry) runs in process through the same code.
  • Give every gap a comment naming the assertion it makes unsound and what covers it instead, and pin the gap with a test asserting the handler is not reached, which fails the day someone routes what the transport deliberately does not.
// No fencing: a second producer claiming the same transactional id is not rejected here, so a
// test cannot assert the first one is fenced out. `capabilities::transactions` against a real
// server is what covers that.

§Examples

use std::future::Future;

use ruststream::Broker;
use ruststream::memory::{ConnectedMemoryBroker, MemoryBroker, MemoryError};
use ruststream::testing::InProcess;

/// A broker crate built over the in-memory bus: its connected form is the bus's own, so its
/// in-process transition is the bus's too.
pub struct EdgeBroker(MemoryBroker);

impl Broker for EdgeBroker {
    type Error = MemoryError;
    type Connected = ConnectedMemoryBroker;

    fn connect(
        self,
    ) -> impl Future<Output = Result<ConnectedMemoryBroker, MemoryError>> + Send {
        self.0.connect()
    }
}

impl InProcess for EdgeBroker {
    fn connect_in_process(
        self,
    ) -> impl Future<Output = Result<ConnectedMemoryBroker, MemoryError>> + Send {
        self.0.connect_in_process()
    }
}

// Under the crate's `testing` feature: `TestApp::start` now runs an app on `EdgeBroker`.
ruststream::register_testable_broker!(EdgeBroker);

Required Methods§

Source

fn connect_in_process( self, ) -> impl Future<Output = Result<Self::Connected, Self::Error>> + Send

Connects to the in-process transport, consuming the configuration.

§Errors

Returns the broker’s own error where the configuration is one the real broker refuses at connect.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl<Log: LogMode> InProcess for MemoryBroker<Log>

Available on crate feature testing only.