use std::convert::TryInto;
use rand::seq::SliceRandom;
use rand::thread_rng;
use serde_yaml::{Number, Value};
use crate::interpolator::INTERPOLATION_REGEX;
use crate::actions::Request;
use crate::benchmark::Benchmark;
pub fn is_that_you(item: &Value) -> bool {
item.get("request").and_then(|v| v.as_mapping()).is_some() && item.get("with_items_range").and_then(|v| v.as_mapping()).is_some()
}
pub fn expand(item: &Value, benchmark: &mut Benchmark) {
if let Some(with_iter_items) = item.get("with_items_range").and_then(|v| v.as_mapping()) {
let lstart = Value::String("start".into());
let lstep = Value::String("step".into());
let lstop = Value::String("stop".into());
let vstart = with_iter_items.get(&lstart).expect("Start property is mandatory");
let default_step = Value::Number(Number::from(1));
let vstep = with_iter_items.get(&lstep).unwrap_or(&default_step);
let vstop = with_iter_items.get(&lstop).expect("Stop property is mandatory");
let start: &str = vstart.as_str().unwrap_or("");
let step: &str = vstep.as_str().unwrap_or("");
let stop: &str = vstop.as_str().unwrap_or("");
if INTERPOLATION_REGEX.is_match(start) {
panic!("Interpolations not supported in 'start' property!");
}
if INTERPOLATION_REGEX.is_match(step) {
panic!("Interpolations not supported in 'step' property!");
}
if INTERPOLATION_REGEX.is_match(stop) {
panic!("Interpolations not supported in 'stop' property!");
}
let start: i64 = vstart.as_i64().expect("Start needs to be a number");
let step: i64 = vstep.as_i64().expect("Step needs to be a number");
let stop: i64 = vstop.as_i64().expect("Stop needs to be a number");
let stop = stop + 1;
if stop > start && start > 0 {
let mut with_items: Vec<i64> = (start..stop).step_by(step as usize).collect();
if let Some(shuffle) = item.get("shuffle").and_then(|v| v.as_bool()) {
if shuffle {
let mut rng = thread_rng();
with_items.shuffle(&mut rng);
}
}
if let Some(pick) = item.get("pick").and_then(|v| v.as_i64()) {
with_items.truncate(pick.try_into().expect("pick can't be larger than size of range"))
}
for (index, value) in with_items.iter().enumerate() {
let index = index as u32;
benchmark.push(Box::new(Request::new(item, Some(Value::Number(Number::from(*value))), Some(index))));
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expand_multi_range() {
let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\nwith_items_range:\n start: 2\n step: 2\n stop: 20";
let docs = crate::reader::read_file_as_yml_from_str(text);
let doc = &docs[0];
let mut benchmark: Benchmark = Benchmark::new();
expand(doc, &mut benchmark);
assert!(is_that_you(doc));
assert_eq!(benchmark.len(), 10);
}
#[test]
fn expand_multi_range_should_limit_requests_using_the_pick_option() {
let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 3\nwith_items_range:\n start: 2\n step: 2\n stop: 20";
let docs = crate::reader::read_file_as_yml_from_str(text);
let doc = &docs[0];
let mut benchmark: Benchmark = Benchmark::new();
expand(doc, &mut benchmark);
assert!(is_that_you(doc));
assert_eq!(benchmark.len(), 3);
}
#[test]
#[should_panic]
fn invalid_expand() {
let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\nwith_items_range:\n start: 1\n step: 2\n stop: foo";
let docs = crate::reader::read_file_as_yml_from_str(text);
let doc = &docs[0];
let mut benchmark: Benchmark = Benchmark::new();
expand(doc, &mut benchmark);
}
#[test]
#[should_panic]
fn runtime_expand() {
let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\nwith_items_range:\n start: 1\n step: 2\n stop: \"{{ memory }}\"";
let docs = crate::reader::read_file_as_yml_from_str(text);
let doc = &docs[0];
let mut benchmark: Benchmark = Benchmark::new();
expand(doc, &mut benchmark);
}
}