Mock test case

This commit is contained in:
Chris Tsang 2021-06-13 23:38:21 +08:00
parent 6b7ea75393
commit faffc518ae
6 changed files with 85 additions and 40 deletions

View File

@ -34,9 +34,13 @@ sqlx = { version = "^0.5", optional = true }
strum = { version = "^0.20", features = [ "derive" ] } strum = { version = "^0.20", features = [ "derive" ] }
serde_json = { version = "^1", optional = true } serde_json = { version = "^1", optional = true }
[dev-dependencies]
async-std = { version = "^1.9", features = [ "attributes" ] }
maplit = { version = "^1" }
[features] [features]
debug-print = [] debug-print = []
default = [ "macros", "with-json", "mock", "sqlx-mysql", "runtime-async-std-native-tls" ] default = [ "macros", "with-json", "mock" ]
macros = [ "sea-orm-macros" ] macros = [ "sea-orm-macros" ]
mock = [] mock = []
with-json = [ "serde_json", "sea-query/with-json" ] with-json = [ "serde_json", "sea-query/with-json" ]

View File

@ -62,10 +62,7 @@ impl DatabaseConnection {
} }
} }
pub async fn execute( pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, ExecErr> {
&self,
stmt: Statement,
) -> Result<ExecResult, ExecErr> {
match self { match self {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.execute(stmt).await, DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.execute(stmt).await,
@ -75,10 +72,7 @@ impl DatabaseConnection {
} }
} }
pub async fn query_one( pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, QueryErr> {
&self,
stmt: Statement,
) -> Result<Option<QueryResult>, QueryErr> {
match self { match self {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_one(stmt).await, DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_one(stmt).await,
@ -88,10 +82,7 @@ impl DatabaseConnection {
} }
} }
pub async fn query_all( pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, QueryErr> {
&self,
stmt: Statement,
) -> Result<Vec<QueryResult>, QueryErr> {
match self { match self {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt).await, DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt).await,

View File

@ -1,15 +1,18 @@
use crate::{ExecErr, ExecResult, MockDatabaseTrait, QueryErr, QueryResult, Statement, TypeErr}; use crate::{
Database, DatabaseConnection, ExecErr, ExecResult, ExecResultHolder, MockDatabaseConnection,
MockDatabaseTrait, QueryErr, QueryResult, QueryResultRow, Statement, TypeErr,
};
use sea_query::{Value, ValueType}; use sea_query::{Value, ValueType};
use std::collections::BTreeMap; use std::collections::BTreeMap;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct MockDatabase { pub struct MockDatabase {
transaction_log: Vec<Statement>, transaction_log: Vec<Statement>,
exec_results: Vec<ExecResult>, exec_results: Vec<MockExecResult>,
query_results: Vec<Vec<QueryResult>>, query_results: Vec<Vec<MockRow>>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
pub struct MockExecResult { pub struct MockExecResult {
pub last_insert_id: u64, pub last_insert_id: u64,
pub rows_affected: u64, pub rows_affected: u64,
@ -24,14 +27,33 @@ impl MockDatabase {
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
pub fn into_database(self) -> Database {
Database {
connection: DatabaseConnection::MockDatabaseConnection(MockDatabaseConnection::new(
self,
)),
}
}
pub fn append_exec_results(mut self, mut vec: Vec<MockExecResult>) -> Self {
self.exec_results.append(&mut vec);
self
}
pub fn append_query_results(mut self, mut vec: Vec<Vec<MockRow>>) -> Self {
self.query_results.append(&mut vec);
self
}
} }
impl MockDatabaseTrait for MockDatabase { impl MockDatabaseTrait for MockDatabase {
fn execute(&mut self, counter: usize, statement: Statement) -> Result<ExecResult, ExecErr> { fn execute(&mut self, counter: usize, statement: Statement) -> Result<ExecResult, ExecErr> {
self.transaction_log.push(statement); self.transaction_log.push(statement);
if counter < self.exec_results.len() { if counter < self.exec_results.len() {
Err(ExecErr) Ok(ExecResult {
// Ok(self.exec_results[counter].clone()) result: ExecResultHolder::Mock(std::mem::take(&mut self.exec_results[counter])),
})
} else { } else {
Err(ExecErr) Err(ExecErr)
} }
@ -44,8 +66,12 @@ impl MockDatabaseTrait for MockDatabase {
) -> Result<Vec<QueryResult>, QueryErr> { ) -> Result<Vec<QueryResult>, QueryErr> {
self.transaction_log.push(statement); self.transaction_log.push(statement);
if counter < self.query_results.len() { if counter < self.query_results.len() {
Err(QueryErr) Ok(std::mem::take(&mut self.query_results[counter])
// Ok(self.query_results[counter].clone()) .into_iter()
.map(|row| QueryResult {
row: QueryResultRow::Mock(row),
})
.collect())
} else { } else {
Err(QueryErr) Err(QueryErr)
} }
@ -64,3 +90,11 @@ impl MockRow {
self.values.into_iter() self.values.into_iter()
} }
} }
impl From<BTreeMap<&str, Value>> for MockRow {
fn from(values: BTreeMap<&str, Value>) -> Self {
Self {
values: values.into_iter().map(|(k, v)| (k.to_owned(), v)).collect(),
}
}
}

View File

@ -21,8 +21,8 @@ pub trait MockDatabaseTrait: Send {
} }
impl MockDatabaseConnector { impl MockDatabaseConnector {
pub fn accepts(_string: &str) -> bool { pub fn accepts(string: &str) -> bool {
true string.starts_with("mock://")
} }
pub async fn connect(_string: &str) -> Result<DatabaseConnection, ConnectionErr> { pub async fn connect(_string: &str) -> Result<DatabaseConnection, ConnectionErr> {
@ -47,30 +47,20 @@ impl MockDatabaseConnection {
impl MockDatabaseConnection { impl MockDatabaseConnection {
pub async fn execute(&self, statement: Statement) -> Result<ExecResult, ExecErr> { pub async fn execute(&self, statement: Statement) -> Result<ExecResult, ExecErr> {
debug_print!("{}", statement); debug_print!("{}", statement);
self.counter.fetch_add(1, Ordering::SeqCst); let counter = self.counter.fetch_add(1, Ordering::SeqCst);
self.mocker self.mocker.lock().unwrap().execute(counter, statement)
.lock()
.unwrap()
.execute(self.counter.load(Ordering::SeqCst), statement)
} }
pub async fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, QueryErr> { pub async fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, QueryErr> {
debug_print!("{}", statement); debug_print!("{}", statement);
self.counter.fetch_add(1, Ordering::SeqCst); let counter = self.counter.fetch_add(1, Ordering::SeqCst);
let result = self let result = self.mocker.lock().unwrap().query(counter, statement)?;
.mocker
.lock()
.unwrap()
.query(self.counter.load(Ordering::SeqCst), statement)?;
Ok(result.into_iter().next()) Ok(result.into_iter().next())
} }
pub async fn query_all(&self, statement: Statement) -> Result<Vec<QueryResult>, QueryErr> { pub async fn query_all(&self, statement: Statement) -> Result<Vec<QueryResult>, QueryErr> {
debug_print!("{}", statement); debug_print!("{}", statement);
self.counter.fetch_add(1, Ordering::SeqCst); let counter = self.counter.fetch_add(1, Ordering::SeqCst);
self.mocker self.mocker.lock().unwrap().query(counter, statement)
.lock()
.unwrap()
.query(self.counter.load(Ordering::SeqCst), statement)
} }
} }

View File

@ -6,7 +6,7 @@ use sqlx::{
sea_query::sea_query_driver_mysql!(); sea_query::sea_query_driver_mysql!();
use sea_query_driver_mysql::bind_query; use sea_query_driver_mysql::bind_query;
use crate::{debug_print, executor::*, DatabaseConnection, Statement, ConnectionErr}; use crate::{debug_print, executor::*, ConnectionErr, DatabaseConnection, Statement};
pub struct SqlxMySqlConnector; pub struct SqlxMySqlConnector;

View File

@ -59,3 +59,29 @@ impl FromQueryResult for JsonValue {
} }
} }
} }
#[cfg(test)]
#[cfg(feature = "mock")]
mod tests {
use crate::tests_cfg::cake;
use crate::{entity::*, MockDatabase};
use sea_query::Value;
#[async_std::test]
async fn to_json_1() {
let db = MockDatabase::new()
.append_query_results(vec![vec![maplit::btreemap! {
"id" => Into::<Value>::into(128), "name" => Into::<Value>::into("apple")
}
.into()]])
.into_database();
assert_eq!(
cake::Entity::find().into_json().one(&db).await.unwrap(),
Some(serde_json::json!({
"id": 128,
"name": "apple"
}))
);
}
}