Skip to content

Commit

Permalink
add expect feature
Browse files Browse the repository at this point in the history
  • Loading branch information
xmxu committed Jan 17, 2022
1 parent 50689bf commit e2ad053
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 63 deletions.
11 changes: 3 additions & 8 deletions anole/src/engine.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::time::Instant;

use log::{debug, info};

use log::debug;

use crate::report::Reporter;
use crate::task::Task;
Expand Down Expand Up @@ -33,20 +33,15 @@ impl<'a> Engine<'a> {
}

pub async fn run(mut self) -> crate::Result<()> {
let cost = Instant::now();
for mut ele in self.tasks.into_iter() {
match ele.execute(self.ctx.as_mut()).await {
Ok(r) => {
self.ctx.report(r);
continue;
},
Ok(_) => continue,
Err(e) => return Err(e)
};
}

debug!("store:{:?}", self.ctx.store);
self.ctx.store.clear();
info!("execute completed! cost_time:{:?}", cost.elapsed());
Ok(())
}
}
Expand Down
23 changes: 21 additions & 2 deletions anole/src/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,31 @@ pub struct ReportItem {
pub task_id: String,
pub code: i32,
pub description: String,
pub(crate) success: bool,
}

impl ReportItem {
pub fn new(task_id: String, code: i32, description: String) -> Self {

pub(crate) fn success(task_id: &str, code: i32, description: String) -> Self {
ReportItem {
task_id: task_id.to_string(), code, description, success: true
}
}

pub(crate) fn failed(task_id: &str, code: i32, description: String) -> Self {
ReportItem {
task_id, code, description
task_id: task_id.to_string(), code, description, success: false
}
}

}

impl std::fmt::Display for ReportItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.success {
write!(f, "{} SUCCESS", self.task_id)
} else{
write!(f, "{} FAILED ({})", self.task_id, self.description)
}
}
}
Expand Down
71 changes: 38 additions & 33 deletions anole/src/task/db/mysql.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@

use std::{vec, time::Duration};

use log::debug;
use sqlx::{mysql::*, Pool, Row, types::time};
use sqlx::{mysql::{self, *}, Pool, Row, types::time, ConnectOptions};

use crate::{context::Context, task, capture::Capture, value::Value, faker, report::ReportItem};

Expand Down Expand Up @@ -32,7 +31,7 @@ impl<'a> MysqlTask<'a> {
self
}

pub async fn execute(&self, ctx: &mut Context) -> crate::Result<ReportItem> {
pub async fn execute(&self, ctx: &mut Context) -> crate::Result<()> {
let options = match &self.options {
Some(o) => o,
None => return Err(crate::error::create_client("DBClientOptions Empty".into()))
Expand All @@ -45,20 +44,23 @@ impl<'a> MysqlTask<'a> {

for tt in &self.tasks {
match client.execute(tt, ctx).await {
Ok(_) => continue,
Ok(r) => {
ctx.report(r);
continue;
},
Err(e) => return Err(e)
}
}
Ok(ReportItem::new("".to_string(), 0, "".to_string()))
Ok(())
}
}

#[derive(Debug)]
pub struct DBTask<'a> {
// client: Vec<dyn DBClient>
pub sql: &'a str,
params: Option<Vec<&'a str>>,
capture: Option<Vec<Capture<'a>>>,
expect: Option<(&'a str, Value)>,
pub task_id: String,
}

Expand All @@ -70,6 +72,7 @@ impl <'a> DBTask<'a> {
params: None,
capture: None,
task_id: faker::uuid_v4(),
expect: None,
}
}

Expand All @@ -84,13 +87,12 @@ impl <'a> DBTask<'a> {
self
}

pub(crate) fn handle_rows(&self, rows: &[MySqlRow], ctx: &mut Context) -> crate::Result<()> {
if self.capture.is_none() {
return Ok(())
}
if rows.is_empty() {
return Ok(())
}
pub fn expect(mut self, tup: (&'a str, Value)) -> Self {
self.expect = Some(tup);
self
}

pub(crate) fn handle_rows(&self, rows: &[MySqlRow], ctx: &mut Context) -> crate::Result<ReportItem> {
if let Some(ref _caps) = self.capture {
for (idx, r) in rows.iter().enumerate() {
for _cap in _caps {
Expand Down Expand Up @@ -141,21 +143,28 @@ impl <'a> DBTask<'a> {
}
}
}
// debug!("column_name:{:?}", r.get::<&str, usize>(0));
// let date: time::Date = r.get(1);
// debug!("column_date:{}", date);
}
}
Ok(())

let task_id = self.task_id.to_owned();
let status_code: i32 = 0;
if let Some(_expect) = &self.expect {
if let Some(_value) = ctx.store.get(_expect.0.to_string()) {
if _value == &_expect.1 {
return Ok(ReportItem::success(&task_id, status_code, format!("{} expect {:?} pass", _expect.0, _expect.1)))
} else {
return Ok(ReportItem::failed(&task_id, status_code, format!("{} expect {:?} but {:?}", _expect.0, _expect.1, _value)))
}
} else {
return Ok(ReportItem::failed(&task_id, status_code, format!("{} expect {:?} but not found", _expect.0, _expect.1)))
}
}

Ok(ReportItem::success(&task_id, status_code, "database execute succeed".to_string()))
}

}

// trait DBClient {
// fn create(&mut self, options: DBClientOption);
// fn execute<R>(&mut self, sql: &str) -> R;
// }

#[derive(Debug, Default)]
struct MysqlClient {
pool: Option<Pool<MySql>>
Expand All @@ -164,11 +173,16 @@ struct MysqlClient {
impl MysqlClient {

async fn create(&mut self, options: &DBClientOption<'_>) -> crate::Result<()> {
let mut opts = match options.url.parse::<mysql::MySqlConnectOptions>() {
Ok(o) => o,
Err(e) => return Err(crate::error::create_client(e.into()))
};
opts.disable_statement_logging();
let pool = match MySqlPoolOptions::new()
.connect_timeout(Duration::from_secs(5))
.idle_timeout(Duration::from_secs(20))
.max_connections(options.max_connections)
.connect(options.url).await {
.connect_with(opts).await {
Ok(p) => p,
Err(e) => return Err(crate::error::create_client(e.into()))
};
Expand All @@ -178,32 +192,23 @@ impl MysqlClient {
Ok(())
}

async fn execute(&self, t: &DBTask<'_>, ctx: &mut Context) -> crate::Result<()> {
async fn execute(&self, t: &DBTask<'_>, ctx: &mut Context) -> crate::Result<ReportItem> {
let pool = &self.pool.as_ref().unwrap();

let mut sql = t.sql.to_owned();

if let Some(_params) = &t.params {
// let _params = _params.to_owned();
for _k in _params {
if let Some(v) = ctx.store.get(_k.to_string()) {
sql = sql.replace(format!("#{}#", _k).as_str(), v.as_str().as_str());
}
}
}
debug!("execute_sql:{}", sql);

let rows = match sqlx::query(&sql).fetch_all(*pool).await {
Ok(r) => r,
Err(e) => return Err(crate::error::request(e.into()))
};
t.handle_rows(&rows, ctx)
// for r in rows {
// debug!("column_name:{:?}", r.get::<&str, usize>(0));
// let date: time::Date = r.get(1);
// debug!("column_date:{}", date);
// }
// Ok(())
}

}
40 changes: 29 additions & 11 deletions anole/src/task/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub struct HttpTask<'a> {


impl HttpTask<'_> {
pub async fn execute(&mut self, ctx: &mut Context) -> crate::Result<ReportItem> {
pub async fn execute(&mut self, ctx: &mut Context) -> crate::Result<()> {
let client = match reqwest::Client::builder()
.connect_timeout(Duration::from_secs(5))
.connection_verbose(self.config.verbose)
Expand Down Expand Up @@ -166,18 +166,29 @@ impl HttpTask<'_> {
let status_code = &rsp.status().as_u16();
let task_id = self.task_id.to_owned();
let is_success = (&rsp.status()).is_success();
let mut report_item = ReportItem::failed(&task_id, *status_code as i32, "http request failed".to_string());
if is_success {

// self.rsp = Some(rsp);
match self.capture(ctx, rsp).await {
Ok(_) => Ok(ReportItem::new(task_id, *status_code as i32, "rsp succeed".to_string())),
Err(e) => Err(e)
Ok(_) => {
if let Some(_expect) = &self.config.expect {
if let Some(_capture_value) = ctx.store.get(_expect.0.to_string()) {
if _expect.1 == *_capture_value {
report_item = ReportItem::success(&task_id, *status_code as i32, format!("{} expect pass", _expect.0));
} else {
report_item = ReportItem::failed(&task_id, *status_code as i32, format!("{} expect {:?} but {:?}", _expect.0, _expect.1, _capture_value));
}
} else {
report_item = ReportItem::failed(&task_id, *status_code as i32, format!("{} expect {:?} but not found", _expect.0, _expect.1));
}
} else {
report_item = ReportItem::success(&task_id, *status_code as i32, "http request succeed".to_string());
}
},
Err(e) => return Err(e)
}

} else {
Ok(ReportItem::new(task_id, *status_code as i32, "rsp is not success".to_string()))
}

}
ctx.report(report_item);
Ok(())
}

pub(crate) async fn capture(&mut self, ctx: &mut Context, rsp: Response) -> crate::Result<()> {
Expand Down Expand Up @@ -246,6 +257,7 @@ pub struct HttpTaskBuilder<'a> {
pub(crate) body: Option<Body>,
pub(crate) capture: Option<Vec<Capture<'a>>>,
pub(crate) verbose: bool,
pub(crate) expect: Option<(&'a str, Value)>,
}

impl<'a> HttpTaskBuilder<'a> {
Expand All @@ -259,7 +271,8 @@ impl<'a> HttpTaskBuilder<'a> {
form: None,
body: None,
capture: None,
verbose: false
verbose: false,
expect: None,
}
}

Expand Down Expand Up @@ -323,6 +336,11 @@ impl<'a> HttpTaskBuilder<'a> {
self
}

pub fn expect(mut self, tup: (&'a str, Value)) -> Self {
self.expect = Some(tup);
self
}

pub(crate) fn filter_caps<T>(&self, f: T) -> Option<Vec<&Capture>> where T: FnMut(&&Capture) -> bool {
if let Some(ref caps) = self.capture {
let v = caps.iter().filter(f).collect::<Vec<&Capture>>();
Expand Down
4 changes: 2 additions & 2 deletions anole/src/task/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{context::Context, report::ReportItem};
use crate::context::Context;

use self::{http::HttpTask, db::mysql::MysqlTask};

Expand All @@ -14,7 +14,7 @@ pub enum Task<'a> {
}

impl<'a> Task<'a> {
pub async fn execute(&mut self, ctx: &mut Context) -> crate::Result<ReportItem> {
pub async fn execute(&mut self, ctx: &mut Context) -> crate::Result<()> {
match self {
Self::Http(t) => t.execute(ctx).await,
Self::Mysql(t) => t.execute(ctx).await
Expand Down
1 change: 1 addition & 0 deletions anole/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use sqlx::types::time::{self, Date, Time};
use crate::{context::Context, error};

#[derive(Debug, Clone)]
#[derive(std::cmp::PartialEq)]
pub enum Value {
I32(i32),
U32(u32),
Expand Down
2 changes: 1 addition & 1 deletion anole/tests/engin.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

use anole::{engine::Engine, task::http::{HttpTaskBuilder, Method, Deserializer}, capture};
use anole::{engine::Engine, task::http::{HttpTaskBuilder, Method}, capture};

#[macro_use]
extern crate log;
Expand Down
15 changes: 11 additions & 4 deletions examples/http.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync;
use std::{sync, time::Instant};

use anole::{engine::Engine, task::http::{HttpTaskBuilder, Method, Deserializer}, capture, report::{ReportItem, StdReporter}};
use anole::{engine::Engine, task::http::{HttpTaskBuilder, Method, Deserializer}, capture, report::{ReportItem, StdReporter}, value::Value};

#[macro_use]
extern crate log;
Expand All @@ -10,11 +10,13 @@ async fn main() {
env_logger::init();
info!("startup");

let cost = Instant::now();

let (sender, recv) = sync::mpsc::channel::<ReportItem>();

tokio::spawn(async {
let reporter_printer = tokio::spawn(async {
for r in recv {
info!("report:{:?}", r);
info!("{}", r);
}
});

Expand All @@ -26,7 +28,9 @@ async fn main() {
.capture(vec![
capture::json("data.1", "tag"),
capture::header("content-length", "cl"),
capture::json("code", "code")
])
.expect(("code", Value::I32(0)))
.build())
.with_http(HttpTaskBuilder::new()
.url("https://tvapi.dykkan.com/v1/tag/:tag")
Expand All @@ -44,6 +48,9 @@ async fn main() {
.build())
.run().await.unwrap();

reporter_printer.await.unwrap();

info!("execute completed! cost_time:{:?}", cost.elapsed());


}
Loading

0 comments on commit e2ad053

Please sign in to comment.