test: MockDmlHandler generic over write input

Allow the MockDmlHandler to capture any input type given to the write()
method. This lets us reuse the mock across all handler implementations,
regardless of their expected write input type.
pull/24376/head
Dom Dwyer 2022-02-08 17:11:17 +00:00
parent e99922d518
commit 5c254339fa
2 changed files with 42 additions and 20 deletions

View File

@ -1,20 +1,20 @@
use std::{collections::VecDeque, sync::Arc}; use std::{collections::VecDeque, fmt::Debug, sync::Arc};
use async_trait::async_trait; use async_trait::async_trait;
use data_types::{delete_predicate::DeletePredicate, DatabaseName}; use data_types::{delete_predicate::DeletePredicate, DatabaseName};
use hashbrown::HashMap;
use mutable_batch::MutableBatch;
use parking_lot::Mutex; use parking_lot::Mutex;
use trace::ctx::SpanContext; use trace::ctx::SpanContext;
use super::{DmlError, DmlHandler}; use super::{DmlError, DmlHandler};
/// A captured call to a [`MockDmlHandler`], generic over `W`, the captured
/// [`DmlHandler::WriteInput`] type.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum MockDmlHandlerCall { pub enum MockDmlHandlerCall<W> {
Write { Write {
namespace: String, namespace: String,
batches: HashMap<String, MutableBatch>, write_input: W,
}, },
Delete { Delete {
namespace: String, namespace: String,
@ -23,23 +23,42 @@ pub enum MockDmlHandlerCall {
}, },
} }
#[derive(Debug, Default)] #[derive(Debug)]
struct Inner { struct Inner<W> {
calls: Vec<MockDmlHandlerCall>, calls: Vec<MockDmlHandlerCall<W>>,
write_return: VecDeque<Result<(), DmlError>>, write_return: VecDeque<Result<(), DmlError>>,
delete_return: VecDeque<Result<(), DmlError>>, delete_return: VecDeque<Result<(), DmlError>>,
} }
impl Inner { impl<W> Default for Inner<W> {
fn record_call(&mut self, call: MockDmlHandlerCall) { fn default() -> Self {
Self {
calls: Default::default(),
write_return: Default::default(),
delete_return: Default::default(),
}
}
}
impl<W> Inner<W> {
fn record_call(&mut self, call: MockDmlHandlerCall<W>) {
self.calls.push(call); self.calls.push(call);
} }
} }
#[derive(Debug, Default)] #[derive(Debug)]
pub struct MockDmlHandler(Mutex<Inner>); pub struct MockDmlHandler<W>(Mutex<Inner<W>>);
impl MockDmlHandler { impl<W> Default for MockDmlHandler<W> {
fn default() -> Self {
Self(Default::default())
}
}
impl<W> MockDmlHandler<W>
where
W: Clone,
{
pub fn with_write_return(self, ret: impl Into<VecDeque<Result<(), DmlError>>>) -> Self { pub fn with_write_return(self, ret: impl Into<VecDeque<Result<(), DmlError>>>) -> Self {
self.0.lock().write_return = ret.into(); self.0.lock().write_return = ret.into();
self self
@ -50,7 +69,7 @@ impl MockDmlHandler {
self self
} }
pub fn calls(&self) -> Vec<MockDmlHandlerCall> { pub fn calls(&self) -> Vec<MockDmlHandlerCall<W>> {
self.0.lock().calls.clone() self.0.lock().calls.clone()
} }
} }
@ -68,22 +87,25 @@ macro_rules! record_and_return {
} }
#[async_trait] #[async_trait]
impl DmlHandler for Arc<MockDmlHandler> { impl<W> DmlHandler for Arc<MockDmlHandler<W>>
where
W: Debug + Send + Sync,
{
type WriteError = DmlError; type WriteError = DmlError;
type DeleteError = DmlError; type DeleteError = DmlError;
type WriteInput = HashMap<String, MutableBatch>; type WriteInput = W;
async fn write( async fn write(
&self, &self,
namespace: DatabaseName<'static>, namespace: DatabaseName<'static>,
batches: Self::WriteInput, write_input: Self::WriteInput,
_span_ctx: Option<SpanContext>, _span_ctx: Option<SpanContext>,
) -> Result<(), Self::WriteError> { ) -> Result<(), Self::WriteError> {
record_and_return!( record_and_return!(
self, self,
MockDmlHandlerCall::Write { MockDmlHandlerCall::Write {
namespace: namespace.into(), namespace: namespace.into(),
batches, write_input,
}, },
write_return write_return
) )

View File

@ -363,9 +363,9 @@ mod tests {
assert_matches!(err, SchemaError::Validate(_)); assert_matches!(err, SchemaError::Validate(_));
// THe mock should observe exactly one write from the first call. // THe mock should observe exactly one write from the first call.
assert_matches!(mock.calls().as_slice(), [MockDmlHandlerCall::Write{namespace, batches}] => { assert_matches!(mock.calls().as_slice(), [MockDmlHandlerCall::Write{namespace, write_input}] => {
assert_eq!(namespace, NAMESPACE); assert_eq!(namespace, NAMESPACE);
let batch = batches.get("bananas").expect("table not found in write"); let batch = write_input.get("bananas").expect("table not found in write");
assert_eq!(batch.rows(), 1); assert_eq!(batch.rows(), 1);
let col = batch.column("val").expect("column not found in write"); let col = batch.column("val").expect("column not found in write");
assert_matches!(col.influx_type(), InfluxColumnType::Field(InfluxFieldType::Integer)); assert_matches!(col.influx_type(), InfluxColumnType::Field(InfluxFieldType::Integer));