-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Create class skeleton for a mock server * Try to return data to the TcpStream * First passing test * Move MockError to error mod * Small cleanup to start to make Server real * Small cleanup * Use SocketAddr * Pipe the state to the request handler * Start to flesh out the Mock * Start of the match function * A bit more mock work * Make tests pass in a more real way * Add mock module * Add state module * Add server module * Run cargo fmt * Connect mockito to the feature flag * Add feature around deps mod
- Loading branch information
1 parent
b70193d
commit f98fdda
Showing
8 changed files
with
217 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use std::error::Error; | ||
use std::fmt; | ||
|
||
#[derive(Debug)] | ||
pub struct MockError { | ||
message: String, | ||
} | ||
|
||
impl fmt::Display for MockError { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "MockError: {}", self.message) | ||
} | ||
} | ||
|
||
impl Error for MockError {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
use hyper::StatusCode; | ||
use hyper::{Body, Request}; | ||
use rand; | ||
use std::sync::{Arc, RwLock}; | ||
|
||
use super::state::State; | ||
|
||
#[derive(Clone)] | ||
pub struct Response { | ||
pub status: StatusCode, | ||
pub body: Vec<u8>, | ||
} | ||
|
||
impl Default for Response { | ||
fn default() -> Self { | ||
Response { | ||
status: StatusCode::OK, | ||
body: vec![], | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct InnerMock { | ||
pub id: usize, | ||
pub method: String, | ||
pub path: String, | ||
pub response: Response, | ||
pub num_called: usize, | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct Mock { | ||
pub state: Arc<RwLock<State>>, | ||
pub inner: InnerMock, | ||
} | ||
|
||
impl Mock { | ||
pub fn new(state: Arc<RwLock<State>>, method: &str, path: &str) -> Mock { | ||
Mock { | ||
state, | ||
inner: InnerMock { | ||
id: rand::random(), | ||
method: method.to_owned().to_uppercase(), | ||
path: path.to_owned(), | ||
response: Response::default(), | ||
num_called: 0, | ||
}, | ||
} | ||
} | ||
|
||
pub fn with_status(mut self, status: u16) -> Mock { | ||
self.inner.response.status = StatusCode::from_u16(status).unwrap(); | ||
self | ||
} | ||
|
||
pub fn with_body(mut self, body: Vec<u8>) -> Mock { | ||
self.inner.response.body = body; | ||
self | ||
} | ||
|
||
pub fn create(self) -> Mock { | ||
let state = self.state.clone(); | ||
let mut state = state.write().unwrap(); | ||
state.mocks.push(self.clone()); | ||
self | ||
} | ||
|
||
pub fn assert(&self) { | ||
let state = self.state.clone(); | ||
let state = state.read().unwrap(); | ||
let num_called = state | ||
.mocks | ||
.iter() | ||
.find(|mock| mock.inner.id == self.inner.id) | ||
.unwrap() | ||
.inner | ||
.num_called; | ||
|
||
if num_called == 0 { | ||
panic!("Mock not called"); | ||
} | ||
} | ||
|
||
pub fn matches(&self, request: &Request<Body>) -> bool { | ||
let method = request.method().to_string(); | ||
let path = request.uri().path().to_string(); | ||
|
||
method == self.inner.method && path == self.inner.path | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
mod error; | ||
mod mock; | ||
mod server; | ||
mod state; | ||
|
||
pub use server::Server; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
use hyper::server::conn::Http; | ||
use hyper::service::service_fn; | ||
use hyper::{Body, Request, Response as HyperResponse}; | ||
use std::net::SocketAddr; | ||
use std::sync::{Arc, RwLock}; | ||
use std::thread; | ||
use tokio::net::TcpListener; | ||
use tokio::runtime; | ||
use tokio::task::spawn; | ||
|
||
use super::error::MockError; | ||
use super::mock::Mock; | ||
use super::state::State; | ||
|
||
pub struct Server { | ||
address: SocketAddr, | ||
state: Arc<RwLock<State>>, | ||
} | ||
|
||
impl Server { | ||
pub fn new() -> Server { | ||
let address = SocketAddr::from(([127, 0, 0, 1], 5001)); | ||
let state = Arc::new(RwLock::new(State::new())); | ||
|
||
let runtime = runtime::Builder::new_current_thread() | ||
.enable_all() | ||
.build() | ||
.unwrap(); | ||
|
||
let state_b = state.clone(); | ||
thread::spawn(move || { | ||
runtime.block_on(async { | ||
let listener = TcpListener::bind(address).await.unwrap(); | ||
|
||
while let Ok((stream, _)) = listener.accept().await { | ||
let state_c = state_b.clone(); | ||
spawn(async move { | ||
let _ = Http::new() | ||
.serve_connection( | ||
stream, | ||
service_fn(move |request: Request<Body>| { | ||
handle_request(request, state_c.clone()) | ||
}), | ||
) | ||
.await; | ||
}); | ||
} | ||
}); | ||
}); | ||
|
||
Server { address, state } | ||
} | ||
|
||
pub fn mock(&self, method: &str, path: &str) -> Mock { | ||
Mock::new(self.state.clone(), method, path) | ||
} | ||
|
||
pub fn url(&self) -> String { | ||
format!("http://{}", self.address.to_string()) | ||
} | ||
} | ||
|
||
async fn handle_request( | ||
request: Request<Body>, | ||
state: Arc<RwLock<State>>, | ||
) -> Result<HyperResponse<Body>, MockError> { | ||
let state_b = state.clone(); | ||
let mut state = state_b.write().unwrap(); | ||
let mut matching: Vec<&mut Mock> = vec![]; | ||
|
||
for mock in state.mocks.iter_mut() { | ||
if mock.matches(&request) { | ||
matching.push(mock); | ||
} | ||
} | ||
let mock = matching.first_mut(); | ||
|
||
if let Some(mock) = mock { | ||
mock.inner.num_called += 1; | ||
let response = HyperResponse::new(Body::from(mock.inner.response.body.clone())); | ||
Ok(response) | ||
} else { | ||
panic!("No matching mock found"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
use super::mock::Mock; | ||
|
||
pub struct State { | ||
pub mocks: Vec<Mock>, | ||
} | ||
|
||
impl State { | ||
pub fn new() -> Self { | ||
State { mocks: vec![] } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters