Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ serde_json = ["dep:serde_json"]

[dependencies]
axum = { version = "0.8.1", features = ["ws"] }
futures = "0.3.31"
serde_json = { version = "1.0.133", optional = true }
tokio = { version = "1.42.0", features = ["sync", "net"] }
tokio-stream = { version = "0.1.17", features = ["sync"] }
tokio-tungstenite = { version = "0.26.0", optional = true }
tracing = "0.1.41"
tungstenite = "0.26.0"
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! API slightly based off wiremock in that you start a server
use crate::responder::{pending, ResponseStream};
use crate::utils::*;
use axum::{
extract::{
Expand All @@ -25,6 +26,7 @@ use tungstenite::{
};

pub mod matchers;
pub mod responder;
pub mod utils;

pub mod prelude {
Expand All @@ -48,6 +50,7 @@ pub struct MockServer {
#[derive(Clone)]
pub struct Mock {
matcher: Vec<Arc<dyn Match + Send + Sync + 'static>>,
responder: Arc<dyn ResponseStream + Send + Sync + 'static>,
expected_calls: Arc<Times>,
calls: Arc<AtomicU64>,
}
Expand All @@ -57,6 +60,7 @@ impl Mock {
Self {
matcher: vec![Arc::new(matcher)],
expected_calls: Default::default(),
responder: Arc::new(pending()),
calls: Default::default(),
}
}
Expand Down
44 changes: 44 additions & 0 deletions src/responder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use futures::{
stream::{self, BoxStream},
Stream, StreamExt,
};
use std::time::Duration;
use tokio::sync::mpsc;
use tokio::time::sleep;
use tokio_stream::wrappers::ReceiverStream;
use tungstenite::Message;

// Design thoughts I want:
//
// 1. Ability to send a message (always good)
// 2. Send messages in response to a message from the client
// 3. Send messages without receiving anything from the client
// 4. Interleave these potential different sources of messages
//
// Now maybe the easiest way to do this is just creating a stream that outputs messages, people can
// use futures crate things to make streams from iterators etc etc. But `Stream` isn't super
// ergonomic so maybe there's a better way. (Internally for this I'd use the fact the socket
// implements `Sink` to forward the `Stream` to the `Sink`.
//
// Single messages being sent out could be solved by putting the websocket (more likely something
// that sends to it into the `Match` trait so people can send responses on matches). Or I make the
// responder take the last client message as an output.

pub trait ResponseStream {
fn handle(self, input: mpsc::Receiver<Message>) -> BoxStream<'static, Message>;
}

impl<S> ResponseStream for S
where
S: Stream<Item = Message> + Send + Sync + 'static,
{
fn handle(self, _: mpsc::Receiver<Message>) -> BoxStream<'static, Message> {
self.boxed()
}
}

pub fn pending() -> impl ResponseStream + Send + Sync + 'static {
stream::pending()
}

// TODO we need rate throttling UX at some point