From 486e2ffe35b2497efc5f7215a02cbd3992fed307 Mon Sep 17 00:00:00 2001 From: benoit Date: Tue, 12 Mar 2024 14:56:52 +0100 Subject: [PATCH] Add string constant provider --- Cargo.lock | 1 + Cargo.toml | 1 + docs/columns/providers/constant.md | 36 +++ src/providers/constant/builder.rs | 48 ++++ src/providers/constant/mod.rs | 3 + src/providers/constant/string.rs | 404 ++++++++++++++++++++++++++++ src/providers/mod.rs | 1 + src/providers/parameters/mod.rs | 1 + src/providers/parameters/wstring.rs | 331 +++++++++++++++++++++++ src/providers/provider.rs | 22 ++ tests/csv_all_options.yaml | 16 ++ tests/parquet_all_options.yaml | 21 +- 12 files changed, 884 insertions(+), 1 deletion(-) create mode 100644 docs/columns/providers/constant.md create mode 100644 src/providers/constant/builder.rs create mode 100644 src/providers/constant/mod.rs create mode 100644 src/providers/constant/string.rs create mode 100644 src/providers/parameters/wstring.rs diff --git a/Cargo.lock b/Cargo.lock index 857f442..412fad2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -710,6 +710,7 @@ dependencies = [ "env_logger", "fastrand", "fastrand-contrib", + "linked-hash-map", "log", "mockall", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 22e0bed..0a81469 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ csv = "1.3.0" env_logger = "0.10" fastrand = "2.0.1" fastrand-contrib = "0.1.0" +linked-hash-map = "0.5.6" log = "0.4" once_cell = "1.8" parquet = "50.0.0" diff --git a/docs/columns/providers/constant.md b/docs/columns/providers/constant.md new file mode 100644 index 0000000..8735f9c --- /dev/null +++ b/docs/columns/providers/constant.md @@ -0,0 +1,36 @@ +Constant provider +------- + +### string +#### unique value +```yaml + - name: constant_as_string + provider: Constant.string + data: trout +``` + +#### list of values +```yaml + - name: list_of_constants_as_string + provider: Constant.string + data: [trout, salmon, carp] +``` + +#### list of weighted values +```yaml + - name: list_of_weighted_constants_as_string + provider: Constant.string + data: + - value: trout + - value: salmon + weight: 8 + - value: carp +``` + +Data value can be unique value, a list of values or a dictionnary. +Integer, float or string can be specify into the configuration but the result will be stored as a string. +If a unique value is specified, all lines will have this value. +If a list of values is specified, value will randomly assigned for each line. +If a weighted list of values is specified, value will weighted randomly assigned for each line: for example is useful to generate data skewing. + +[Options](../options.md) are also possible. \ No newline at end of file diff --git a/src/providers/constant/builder.rs b/src/providers/constant/builder.rs new file mode 100644 index 0000000..4c2928a --- /dev/null +++ b/src/providers/constant/builder.rs @@ -0,0 +1,48 @@ +use yaml_rust::Yaml; + +use crate::{errors::FakeLakeError, providers::provider::Provider}; + +use super::string; + +pub fn get_corresponding_provider( + mut provider_split: std::str::Split<'_, char>, + column: &Yaml, +) -> Result, FakeLakeError> { + match provider_split.next() { + Some("string") => Ok(string::new_from_yaml(column)), + _ => Err(FakeLakeError::BadYAMLFormat("".to_string())), + } +} + +#[cfg(test)] +mod tests { + use super::get_corresponding_provider; + + use yaml_rust::YamlLoader; + + #[test] + fn given_string_should_return_provider() { + let provider_name = "string"; + let yaml_str = format!("name: is_suscribed{}provider: {}", '\n', provider_name); + let column = &YamlLoader::load_from_str(yaml_str.as_str()).unwrap()[0]; + + let provider_split = provider_name.split('.'); + match get_corresponding_provider(provider_split, column) { + Ok(_) => (), + _ => panic!(), + } + } + + #[test] + fn given_wrong_provider_should_return_error() { + let provider_name = "not_a_provider"; + let yaml_str = format!("name: email{}provider: {}", '\n', provider_name); + let column = &YamlLoader::load_from_str(yaml_str.as_str()).unwrap()[0]; + + let provider_split = provider_name.split('.'); + match get_corresponding_provider(provider_split, column) { + Err(_) => (), + _ => panic!(), + } + } +} diff --git a/src/providers/constant/mod.rs b/src/providers/constant/mod.rs new file mode 100644 index 0000000..6eb5b43 --- /dev/null +++ b/src/providers/constant/mod.rs @@ -0,0 +1,3 @@ +pub mod builder; + +pub mod string; diff --git a/src/providers/constant/string.rs b/src/providers/constant/string.rs new file mode 100644 index 0000000..7ef0284 --- /dev/null +++ b/src/providers/constant/string.rs @@ -0,0 +1,404 @@ +use yaml_rust::Yaml; + +use crate::providers::parameters::wstring::WStringParameter; +use crate::providers::provider::{Provider, Value}; +use crate::providers::utils::string::random_alphanumeric; + +const DEFAULT_CONSTANT: &str = "constant"; + +#[derive(Clone)] +pub struct ConstantStringProvider { + data: String, +} + +impl Provider for ConstantStringProvider { + fn value(&self, _: u32) -> Value { + Value::String(self.data.to_string()) + } + + fn corrupted_value(&self, _: u32) -> Value { + Value::String(random_alphanumeric(10)) + } +} + +#[derive(Clone)] +pub struct ListStringProvider { + data: Vec, +} + +impl Provider for ListStringProvider { + fn value(&self, _: u32) -> Value { + let index = fastrand::usize(..self.data.len()); + Value::String(self.data[index].to_string()) + } + + fn corrupted_value(&self, _: u32) -> Value { + Value::String(random_alphanumeric(10)) + } +} + +#[derive(Clone)] +pub struct WeightedListStringProvider { + data: Vec, + sum: u32, +} + +impl WeightedListStringProvider { + fn new(parameters: Vec) -> Self { + WeightedListStringProvider { + data: parameters.clone(), + sum: parameters.into_iter().map(|w| w.weight).sum::(), + } + } + + fn weighted_random(&self) -> String { + let random: u32 = fastrand::u32(0..self.sum) + 1; + self.get_from_weight(random) + } + + fn get_from_weight(&self, random: u32) -> String { + let mut weight_sum: u32 = 0; + let mut random_value: Option = None; + for weighted_string in self.data.iter() { + weight_sum += weighted_string.weight; + if weight_sum >= random { + random_value = Some(weighted_string.value.to_owned()); + break; + } + } + + match random_value { + Some(value) => value, + None => panic!("Random value can't be empty"), + } + } +} + +impl Provider for WeightedListStringProvider { + fn value(&self, _: u32) -> Value { + Value::String(self.weighted_random()) + } + + fn corrupted_value(&self, _: u32) -> Value { + Value::String(random_alphanumeric(10)) + } +} + +pub fn new_from_yaml(column: &Yaml) -> Box { + let data_option = WStringParameter::new(column, "data", DEFAULT_CONSTANT); + let length: u32 = data_option.len() as u32; + if length == 1 { + Box::new(ConstantStringProvider { + data: data_option[0].value.to_string(), + }) + } else { + let w = WeightedListStringProvider::new(data_option); + if w.sum == length { + Box::new(ListStringProvider { + data: w.data.into_iter().map(|v| v.value).collect(), + }) + } else { + Box::new(w) + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use super::*; + use crate::providers::provider::{Provider, Value}; + + use yaml_rust::YamlLoader; + + fn generate_provider(data: Option<&str>) -> Box { + let yaml_data = match data { + Some(value) => format!("{}data: {}", "\n", value), + None => String::new(), + }; + let yaml_str = format!("name: id{}", yaml_data); + let yaml = YamlLoader::load_from_str(yaml_str.as_str()).unwrap(); + super::new_from_yaml(&yaml[0]) + } + + #[test] + fn given_weighted_list_string_provider_should_compute_weight_sum() { + let provider: WeightedListStringProvider = WeightedListStringProvider::new(vec![ + WStringParameter { + value: "constant".to_string(), + weight: 1, + }, + WStringParameter { + value: "my_data".to_string(), + weight: 2, + }, + WStringParameter { + value: "voila".to_string(), + weight: 3, + }, + WStringParameter { + value: "my_value".to_string(), + weight: 4, + }, + ]); + assert_eq!(provider.sum, 10); + } + + #[test] + fn given_data_as_string_should_return_string_constant_type() { + let provider: ConstantStringProvider = ConstantStringProvider { + data: "my_value".to_string(), + }; + for i in 0..10 { + match provider.value(i) { + Value::String(s) => assert_eq!(s, "my_value"), + _ => panic!(), + } + } + } + + #[test] + fn given_data_as_array_should_return_string_constant_randomly_from_array_type() { + let input: Vec = vec![ + "constant".to_string(), + "my_data".to_string(), + "voila".to_string(), + "my_value".to_string(), + ]; + let provider: ListStringProvider = ListStringProvider { + data: vec![ + "constant".to_string(), + "my_data".to_string(), + "voila".to_string(), + "my_value".to_string(), + ], + }; + for i in 0..10 { + match provider.value(i) { + Value::String(s) => assert!(input.contains(&s)), + _ => panic!(), + } + } + } + + #[test] + fn given_data_as_weighted_list_should_return_string_constant_weighted_randomly() { + let input: Vec = vec![ + "constant".to_string(), + "my_data".to_string(), + "voila".to_string(), + "my_value".to_string(), + ]; + let provider: WeightedListStringProvider = WeightedListStringProvider::new(vec![ + WStringParameter { + value: "constant".to_string(), + weight: 1, + }, + WStringParameter { + value: "my_data".to_string(), + weight: 1, + }, + WStringParameter { + value: "voila".to_string(), + weight: 1, + }, + WStringParameter { + value: "my_value".to_string(), + weight: 1, + }, + ]); + for i in 0..10 { + match provider.value(i) { + Value::String(s) => assert!(input.contains(&s)), + _ => panic!(), + } + } + } + + // Validate value calculation + #[test] + fn given_no_config_should_return_default() { + let provider = generate_provider(None); + assert_eq!( + provider.value(0), + Value::String(DEFAULT_CONSTANT.to_string()) + ); + } + + #[test] + fn given_string_config_should_return_array_value() { + let provider = generate_provider(Some("my_data")); + for i in 0..10 { + assert_eq!(provider.value(i), Value::String("my_data".to_string())); + } + } + + #[test] + fn given_array_config_should_return_array_value() { + let expected_input: Vec = vec![ + "my_data".to_string(), + "example".to_string(), + "array".to_string(), + ]; + let input: &str = "[my_data, example, array]"; + let provider = generate_provider(Some(input)); + for i in 0..10 { + match provider.value(i) { + Value::String(s) => assert!(expected_input.contains(&s)), + _ => panic!(), + } + } + } + + #[test] + fn given_array_with_weight_config_should_return_array_value() { + let expected_input: Vec = vec![ + "trout".to_string(), + "salmon".to_string(), + "carp".to_string(), + ]; + let input: &str = + "\n - value: trout \n - value: salmon \n weight: 8 \n - value: carp"; + let provider = generate_provider(Some(input)); + for i in 0..10 { + match provider.value(i) { + Value::String(s) => assert!(expected_input.contains(&s)), + _ => panic!(), + } + } + } + + // Test weighted random + #[test] + #[should_panic(expected = "Random value can't be empty")] + fn given_random_greater_than_weight_sum_should_return_default_value() { + let weighted_strings: Vec = vec![WStringParameter { + value: "test".to_string(), + weight: 1, + }]; + WeightedListStringProvider::new(weighted_strings).get_from_weight(2); + } + + #[test] + fn given_random_should_return_corresponding_value() { + let weighted_strings: Vec = vec![ + WStringParameter { + value: "first".to_string(), + weight: 1, + }, + WStringParameter { + value: "middle".to_string(), + weight: 8, + }, + WStringParameter { + value: "last".to_string(), + weight: 1, + }, + ]; + let values = [ + (0, "first"), + (1, "first"), + (2, "middle"), + (9, "middle"), + (10, "last"), + ]; + let w: WeightedListStringProvider = WeightedListStringProvider::new(weighted_strings); + for value in values { + let result: String = w.get_from_weight(value.0); + assert_eq!(value.1.to_owned(), result); + } + } + + #[test] + fn given_weighted_list_string_provider_should_return_values_according_to_weights() { + let provider: WeightedListStringProvider = WeightedListStringProvider::new(vec![ + WStringParameter { + value: "one".to_string(), + weight: 1, + }, + WStringParameter { + value: "two".to_string(), + weight: 6, + }, + WStringParameter { + value: "three".to_string(), + weight: 3, + }, + ]); + + let mut result: HashMap = HashMap::new(); + for i in 0..1000 { + match provider.value(i) { + Value::String(value) => { + let count: u32 = result.get(&value).map_or_else(|| 1, |v| v + 1); + result.insert(value, count); + } + _ => panic!("Should never happened !"), + }; + } + + let expected = vec![("one", 1), ("two", 6), ("three", 3)]; + for e in expected { + let v: &u32 = &result + .get(&e.0.to_string()) + .map_or_else(|| 0, |v| (*v as f64 / 100.0).round() as u32); + assert_eq!(v, &e.1); + } + } + + #[test] + fn given_no_config_should_return_corrupted_value() { + let provider = generate_provider(None); + assert_ne!( + provider.corrupted_value(0), + Value::String(DEFAULT_CONSTANT.to_string()) + ); + } + + #[test] + fn given_string_config_should_return_corrupted_value() { + let provider = generate_provider(Some("my_data")); + for i in 0..10 { + assert_ne!( + provider.corrupted_value(i), + Value::String("my_data".to_string()) + ); + } + } + + #[test] + fn given_array_config_should_return_corrupted_value() { + let expected_input: Vec = vec![ + "my_data".to_string(), + "example".to_string(), + "array".to_string(), + ]; + let input: &str = "[my_data, example, array]"; + let provider = generate_provider(Some(input)); + for i in 0..10 { + match provider.corrupted_value(i) { + Value::String(s) => assert!(!expected_input.contains(&s)), + _ => panic!(), + } + } + } + + #[test] + fn given_array_with_weight_config_should_return_corrupted() { + let expected_input: Vec = vec![ + "trout".to_string(), + "salmon".to_string(), + "carp".to_string(), + ]; + let input: &str = + "\n - value: trout \n - value: salmon \n weight: 8 \n - value: carp"; + let provider = generate_provider(Some(input)); + for i in 0..10 { + match provider.corrupted_value(i) { + Value::String(s) => assert!(!expected_input.contains(&s)), + _ => panic!(), + } + } + } +} diff --git a/src/providers/mod.rs b/src/providers/mod.rs index abb2bf8..9969cc5 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -3,6 +3,7 @@ pub mod provider; pub mod parameters; pub mod utils; +pub mod constant; pub mod increment; pub mod person; pub mod random; diff --git a/src/providers/parameters/mod.rs b/src/providers/parameters/mod.rs index a21bb6c..798d862 100644 --- a/src/providers/parameters/mod.rs +++ b/src/providers/parameters/mod.rs @@ -5,6 +5,7 @@ pub mod i32; pub mod percentage; pub mod string; pub mod urange; +pub mod wstring; use yaml_rust::Yaml; diff --git a/src/providers/parameters/wstring.rs b/src/providers/parameters/wstring.rs new file mode 100644 index 0000000..bfb56d9 --- /dev/null +++ b/src/providers/parameters/wstring.rs @@ -0,0 +1,331 @@ +use linked_hash_map::LinkedHashMap; +use log::warn; +use yaml_rust::Yaml; + +use super::get_column_name; + +const WEIGHT_KEY: &str = "weight"; +const VALUE_KEY: &str = "value"; + +// WString for weighted string +#[derive(Clone, PartialEq, Debug)] +pub struct WStringParameter { + pub value: String, + pub weight: u32, +} + +impl WStringParameter { + pub fn new(column: &Yaml, param_name: &str, default_value: &str) -> Vec { + let column_name = get_column_name(column); + match &column[param_name] { + Yaml::Integer(i) => vec![WStringParameter::new_from_i64(i)], + Yaml::Real(r) => vec![WStringParameter::new_from_str(r)], + Yaml::String(s) => vec![WStringParameter::new_from_str(s.as_str())], + Yaml::Array(a) => extract_array(column_name, a, default_value), + _ => { + print_wrong_param( + column_name, + param_name, + "string or integer or real or array", + default_value, + ); + vec![WStringParameter::new_from_str(default_value)] + } + } + } + + pub fn new_from_str(value: &str) -> WStringParameter { + WStringParameter { + value: value.to_string(), + weight: 1, + } + } + + fn new_from_str_and_weight( + column_name: &str, + value: &str, + weight: Option, + ) -> WStringParameter { + WStringParameter { + value: value.to_string(), + weight: extract_weight(column_name, weight), + } + } + + fn new_from_i64(value: &i64) -> WStringParameter { + WStringParameter { + value: value.to_string(), + weight: 1, + } + } + + fn new_from_i64_and_weight( + column_name: &str, + value: &i64, + weight: Option, + ) -> WStringParameter { + WStringParameter { + value: value.to_string(), + weight: extract_weight(column_name, weight), + } + } +} + +fn extract_weight(column_name: &str, weight: Option) -> u32 { + let w: i64 = weight.unwrap_or(1); + if w >= u32::MIN as i64 && w <= u32::MAX as i64 { + w as u32 + } else { + print_wrong_param(column_name, WEIGHT_KEY, "u32", "1"); + 1 + } +} + +fn extract_array(column_name: &str, array: &[Yaml], default_value: &str) -> Vec { + array + .iter() + .map(|i| match i { + Yaml::Hash(h) => extract_hash(column_name, h, default_value), + Yaml::String(s) => WStringParameter::new_from_str(s.as_str()), + Yaml::Integer(i) => WStringParameter::new_from_i64(i), + Yaml::Real(r) => WStringParameter::new_from_str(r), + _ => WStringParameter::new_from_str(default_value), + }) + .collect::>() +} + +fn extract_weight_from_hash(column_name: &str, hash: &LinkedHashMap) -> Option { + hash.get(&Yaml::String(String::from(WEIGHT_KEY))) + .and_then(|w| match w { + Yaml::Integer(i) => Some(i.to_owned()), + _ => { + print_wrong_param(column_name, WEIGHT_KEY, "u32", "1"); + None + } + }) +} + +fn extract_hash( + column_name: &str, + hash: &LinkedHashMap, + default_value: &str, +) -> WStringParameter { + let weight = extract_weight_from_hash(column_name, hash); + match hash + .get(&Yaml::String(String::from(VALUE_KEY))) + .map(|f| match f { + Yaml::String(s) => WStringParameter::new_from_str_and_weight(column_name, s, weight), + Yaml::Real(r) => WStringParameter::new_from_str_and_weight(column_name, r, weight), + Yaml::Integer(i) => WStringParameter::new_from_i64_and_weight(column_name, i, weight), + _ => { + print_wrong_param( + column_name, + VALUE_KEY, + "string or integer or real", + default_value, + ); + WStringParameter::new_from_str(default_value) + } + }) { + Some(value) => value, + None => panic!("Yaml value is invalid"), + } +} + +fn print_wrong_param(column_name: &str, param_name: &str, value_type: &str, new_value: &str) { + warn!( + "Column {} param {} should be {}. Value {} is taken instead.", + column_name, param_name, value_type, new_value + ); +} + +#[cfg(test)] +mod tests { + use linked_hash_map::LinkedHashMap; + use yaml_rust::{Yaml, YamlLoader}; + + use crate::providers::parameters::wstring::extract_weight; + use crate::providers::parameters::wstring::extract_weight_from_hash; + use crate::providers::parameters::wstring::WStringParameter; + + use super::extract_hash; + use super::WEIGHT_KEY; + + fn generate_yaml(name: Option<&str>, param_name: &str, param_value: Option<&str>) -> Vec { + let yaml_name = match name { + Some(value) => format!("name: {}{}", value, "\n"), + None => String::new(), + }; + + let yaml_param = match param_value { + Some(value) => format!("{}: {}", param_name, value), + None => String::new(), + }; + + let yaml_str = format!("{}{}", yaml_name, yaml_param); + YamlLoader::load_from_str(yaml_str.as_str()).unwrap() + } + + #[test] + fn given_correct_float_param_should_give_value() { + let yaml_param = generate_yaml(Some("col"), "param", Some("15.5")); + let wstringparameters = WStringParameter::new(&yaml_param[0], "param", "constant"); + assert_eq!( + wstringparameters, + vec![WStringParameter { + value: "15.5".to_string(), + weight: 1 + }] + ); + } + + #[test] + fn given_correct_u32_param_should_give_value() { + let yaml_param = generate_yaml(Some("col"), "param", Some("15")); + let wstringparameters = WStringParameter::new(&yaml_param[0], "param", "constant"); + assert_eq!( + wstringparameters, + vec![WStringParameter { + value: "15".to_string(), + weight: 1 + }] + ); + } + + #[test] + fn given_correct_u32_as_string_param_should_give_value() { + let yaml_param = generate_yaml(Some("col"), "param", Some("\"15\"")); + let wstringparameters = WStringParameter::new(&yaml_param[0], "param", "constant"); + assert_eq!( + wstringparameters, + vec![WStringParameter { + value: "15".to_string(), + weight: 1 + }] + ); + } + + #[test] + fn given_correct_string_param_should_give_value() { + let yaml_param = generate_yaml(Some("col"), "param", Some("salmon")); + let wstringvecparameters = WStringParameter::new(&yaml_param[0], "param", "constant"); + assert_eq!( + wstringvecparameters, + vec![WStringParameter { + value: "salmon".to_string(), + weight: 1 + }] + ); + } + + #[test] + fn given_correct_array_param_should_give_value() { + let yaml_param = generate_yaml(Some("col"), "param", Some("[salmon, 15, 20.55]")); + let wstringvecparameters = WStringParameter::new(&yaml_param[0], "param", "constant"); + assert_eq!( + wstringvecparameters, + vec![ + WStringParameter { + value: "salmon".to_string(), + weight: 1 + }, + WStringParameter { + value: "15".to_string(), + weight: 1 + }, + WStringParameter { + value: "20.55".to_string(), + weight: 1 + } + ] + ); + } + + #[test] + fn given_correct_list_param_should_give_value() { + let yaml_param = generate_yaml( + Some("col"), + "param", + Some("\n - value: salmon \n - value: 15 \n - value: 13.6"), + ); + let wstringvecparameters = WStringParameter::new(&yaml_param[0], "param", "constant"); + assert_eq!( + wstringvecparameters, + vec![ + WStringParameter { + value: "salmon".to_string(), + weight: 1 + }, + WStringParameter { + value: "15".to_string(), + weight: 1 + }, + WStringParameter { + value: "13.6".to_string(), + weight: 1 + } + ] + ); + } + + #[test] + fn given_correct_weighted_list_param_should_give_value() { + let yaml_param = generate_yaml( + Some("col"), + "param", + Some("\n - value: 15 \n - value: salmon \n weight: 8 \n - value: 12.3"), + ); + let wstringvecparameters = WStringParameter::new(&yaml_param[0], "param", "constant"); + assert_eq!( + wstringvecparameters, + vec![ + WStringParameter { + value: "15".to_string(), + weight: 1 + }, + WStringParameter { + value: "salmon".to_string(), + weight: 8 + }, + WStringParameter { + value: "12.3".to_string(), + weight: 1 + } + ] + ); + } + + #[test] + fn given_weight_should_return_u32() { + let data: [(i64, u32); 6] = [ + (0, 0), + (-1, 1), + (u32::MAX as i64, u32::MAX), + (u32::MAX as i64 + 1, 1), + (u32::MIN as i64, u32::MIN), + (u32::MIN as i64 - 1, 1), + ]; + for d in data { + let w = extract_weight("col", Some(d.0)); + assert_eq!(w, d.1); + } + } + + #[test] + #[should_panic(expected = "Yaml value is invalid")] + fn given_empty_hash_should_panic_extract_hash() { + let hash = LinkedHashMap::new(); + extract_hash("column_name", &hash, "default_value"); + } + + #[test] + fn given_wrong_weight_should_return_none() { + let mut hash = LinkedHashMap::new(); + hash.insert( + Yaml::String(WEIGHT_KEY.to_string()), + Yaml::String("bad".to_string()), + ); + let result = extract_weight_from_hash("col", &hash); + assert_eq!(None, result); + } +} diff --git a/src/providers/provider.rs b/src/providers/provider.rs index 9b38b8d..46fffa5 100644 --- a/src/providers/provider.rs +++ b/src/providers/provider.rs @@ -94,6 +94,9 @@ impl ProviderBuilder { let mut provider_split = lowercased.split('.'); match provider_split.next() { + Some("constant") => { + providers::constant::builder::get_corresponding_provider(provider_split, column) + } Some("increment") => { providers::increment::builder::get_corresponding_provider(provider_split, column) } @@ -172,6 +175,25 @@ mod tests { } } + #[test] + fn given_valid_provider_should_return_provider() { + let provider_names = [ + "constant.string", + "increment.integer", + "person.email", + "random.string.alphanumeric", + ]; + for provider_name in provider_names { + let yaml_str = format!("name: name{}provider: {}", '\n', provider_name); + let column = &YamlLoader::load_from_str(yaml_str.as_str()).unwrap()[0]; + + match ProviderBuilder::get_corresponding_provider(provider_name, column) { + Ok(_) => (), + _ => panic!(), + } + } + } + #[test] fn given_wrong_provider_should_return_error() { let provider_name = "not_a_provider"; diff --git a/tests/csv_all_options.yaml b/tests/csv_all_options.yaml index d0b5346..90efc09 100644 --- a/tests/csv_all_options.yaml +++ b/tests/csv_all_options.yaml @@ -57,6 +57,22 @@ columns: min: -1000 max: 1000 + - name: constant_string + provider: Constant.string + data: my_constant + + - name: constant_string_list + provider: Constant.string + data: [15, my_constant, 22.22] + + - name: constant_string_weighted_list + provider: Constant.string + data: + - value: 42 + - value: my_second_constant + weight: 8 + - value: my_third_constant + info: output_name: csv_all_options output_format: csv diff --git a/tests/parquet_all_options.yaml b/tests/parquet_all_options.yaml index d132d49..8994b91 100644 --- a/tests/parquet_all_options.yaml +++ b/tests/parquet_all_options.yaml @@ -58,7 +58,26 @@ columns: min: -1000 max: 1000 + - name: constant_string + provider: Constant.string + data: my_constant + corrupted: 0.0001 + + - name: constant_string_list + provider: Constant.string + data: [15, my_constant, 22.22] + corrupted: 0.0001 + + - name: constant_string_weighted_list + provider: Constant.string + data: + - value: 42 + - value: my_second_constant + weight: 8 + - value: my_third_constant + corrupted: 0.0001 + info: output_name: parquet_all_options output_format: parquet - rows: 174_957 \ No newline at end of file + rows: 174_957