blob: 3a24bde5d4f34ba72ea906729d0f94772dcc305e [file] [log] [blame]
use config::{Config, Environment, Source};
use serde_derive::Deserialize;
/// Reminder that tests using env variables need to use different env variable names, since
/// tests can be run in parallel
#[test]
fn test_default() {
temp_env::with_var("A_B_C", Some("abc"), || {
let environment = Environment::default();
assert!(environment.collect().unwrap().contains_key("a_b_c"));
})
}
#[test]
fn test_prefix_is_removed_from_key() {
temp_env::with_var("B_A_C", Some("abc"), || {
let environment = Environment::with_prefix("B");
assert!(environment.collect().unwrap().contains_key("a_c"));
})
}
#[test]
fn test_prefix_with_variant_forms_of_spelling() {
temp_env::with_var("a_A_C", Some("abc"), || {
let environment = Environment::with_prefix("a");
assert!(environment.collect().unwrap().contains_key("a_c"));
});
temp_env::with_var("aB_A_C", Some("abc"), || {
let environment = Environment::with_prefix("aB");
assert!(environment.collect().unwrap().contains_key("a_c"));
});
temp_env::with_var("Ab_A_C", Some("abc"), || {
let environment = Environment::with_prefix("ab");
assert!(environment.collect().unwrap().contains_key("a_c"));
});
}
#[test]
fn test_separator_behavior() {
temp_env::with_var("C_B_A", Some("abc"), || {
let environment = Environment::with_prefix("C").separator("_");
assert!(environment.collect().unwrap().contains_key("b.a"));
})
}
#[test]
fn test_empty_value_is_ignored() {
temp_env::with_var("C_A_B", Some(""), || {
let environment = Environment::default().ignore_empty(true);
assert!(!environment.collect().unwrap().contains_key("c_a_b"));
})
}
#[test]
fn test_keep_prefix() {
temp_env::with_var("C_A_B", Some(""), || {
// Do not keep the prefix
let environment = Environment::with_prefix("C");
assert!(environment.collect().unwrap().contains_key("a_b"));
let environment = Environment::with_prefix("C").keep_prefix(false);
assert!(environment.collect().unwrap().contains_key("a_b"));
// Keep the prefix
let environment = Environment::with_prefix("C").keep_prefix(true);
assert!(environment.collect().unwrap().contains_key("c_a_b"));
})
}
#[test]
fn test_custom_separator_behavior() {
temp_env::with_var("C.B.A", Some("abc"), || {
let environment = Environment::with_prefix("C").separator(".");
assert!(environment.collect().unwrap().contains_key("b.a"));
})
}
#[test]
fn test_custom_prefix_separator_behavior() {
temp_env::with_var("C-B.A", Some("abc"), || {
let environment = Environment::with_prefix("C")
.separator(".")
.prefix_separator("-");
assert!(environment.collect().unwrap().contains_key("b.a"));
})
}
#[test]
fn test_parse_int() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestIntEnum {
Int(TestInt),
}
#[derive(Deserialize, Debug)]
struct TestInt {
int_val: i32,
}
temp_env::with_var("INT_VAL", Some("42"), || {
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("tag", "Int")
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestIntEnum = config.try_deserialize().unwrap();
assert!(matches!(config, TestIntEnum::Int(TestInt { int_val: 42 })));
})
}
#[test]
fn test_parse_uint() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestUintEnum {
Uint(TestUint),
}
#[derive(Deserialize, Debug)]
struct TestUint {
int_val: u32,
}
temp_env::with_var("INT_VAL", Some("42"), || {
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("tag", "Uint")
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestUintEnum = config.try_deserialize().unwrap();
assert!(matches!(
config,
TestUintEnum::Uint(TestUint { int_val: 42 })
));
})
}
#[test]
fn test_parse_float() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestFloatEnum {
Float(TestFloat),
}
#[derive(Deserialize, Debug)]
struct TestFloat {
float_val: f64,
}
temp_env::with_var("FLOAT_VAL", Some("42.3"), || {
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("tag", "Float")
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestFloatEnum = config.try_deserialize().unwrap();
// can't use `matches!` because of float value
match config {
TestFloatEnum::Float(TestFloat { float_val }) => {
assert!(float_cmp::approx_eq!(f64, float_val, 42.3))
}
}
})
}
#[test]
fn test_parse_bool() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestBoolEnum {
Bool(TestBool),
}
#[derive(Deserialize, Debug)]
struct TestBool {
bool_val: bool,
}
temp_env::with_var("BOOL_VAL", Some("true"), || {
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("tag", "Bool")
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestBoolEnum = config.try_deserialize().unwrap();
assert!(matches!(
config,
TestBoolEnum::Bool(TestBool { bool_val: true })
));
})
}
#[test]
#[should_panic(expected = "invalid type: string \"42\", expected i32")]
fn test_parse_off_int() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestIntEnum {
Int(TestInt),
}
#[derive(Deserialize, Debug)]
struct TestInt {
#[allow(dead_code)]
int_val_1: i32,
}
temp_env::with_var("INT_VAL_1", Some("42"), || {
let environment = Environment::default().try_parsing(false);
let config = Config::builder()
.set_default("tag", "Int")
.unwrap()
.add_source(environment)
.build()
.unwrap();
config.try_deserialize::<TestIntEnum>().unwrap();
})
}
#[test]
#[should_panic(expected = "invalid type: string \"42.3\", expected f64")]
fn test_parse_off_float() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestFloatEnum {
Float(TestFloat),
}
#[derive(Deserialize, Debug)]
struct TestFloat {
#[allow(dead_code)]
float_val_1: f64,
}
temp_env::with_var("FLOAT_VAL_1", Some("42.3"), || {
let environment = Environment::default().try_parsing(false);
let config = Config::builder()
.set_default("tag", "Float")
.unwrap()
.add_source(environment)
.build()
.unwrap();
config.try_deserialize::<TestFloatEnum>().unwrap();
})
}
#[test]
#[should_panic(expected = "invalid type: string \"true\", expected a boolean")]
fn test_parse_off_bool() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestBoolEnum {
Bool(TestBool),
}
#[derive(Deserialize, Debug)]
struct TestBool {
#[allow(dead_code)]
bool_val_1: bool,
}
temp_env::with_var("BOOL_VAL_1", Some("true"), || {
let environment = Environment::default().try_parsing(false);
let config = Config::builder()
.set_default("tag", "Bool")
.unwrap()
.add_source(environment)
.build()
.unwrap();
config.try_deserialize::<TestBoolEnum>().unwrap();
})
}
#[test]
#[should_panic(expected = "invalid type: string \"not an int\", expected i32")]
fn test_parse_int_fail() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestIntEnum {
Int(TestInt),
}
#[derive(Deserialize, Debug)]
struct TestInt {
#[allow(dead_code)]
int_val_2: i32,
}
temp_env::with_var("INT_VAL_2", Some("not an int"), || {
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("tag", "Int")
.unwrap()
.add_source(environment)
.build()
.unwrap();
config.try_deserialize::<TestIntEnum>().unwrap();
})
}
#[test]
#[should_panic(expected = "invalid type: string \"not a float\", expected f64")]
fn test_parse_float_fail() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestFloatEnum {
Float(TestFloat),
}
#[derive(Deserialize, Debug)]
struct TestFloat {
#[allow(dead_code)]
float_val_2: f64,
}
temp_env::with_var("FLOAT_VAL_2", Some("not a float"), || {
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("tag", "Float")
.unwrap()
.add_source(environment)
.build()
.unwrap();
config.try_deserialize::<TestFloatEnum>().unwrap();
})
}
#[test]
#[should_panic(expected = "invalid type: string \"not a bool\", expected a boolean")]
fn test_parse_bool_fail() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestBoolEnum {
Bool(TestBool),
}
#[derive(Deserialize, Debug)]
struct TestBool {
#[allow(dead_code)]
bool_val_2: bool,
}
temp_env::with_var("BOOL_VAL_2", Some("not a bool"), || {
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("tag", "Bool")
.unwrap()
.add_source(environment)
.build()
.unwrap();
config.try_deserialize::<TestBoolEnum>().unwrap();
})
}
#[test]
fn test_parse_string_and_list() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestStringEnum {
String(TestString),
}
#[derive(Deserialize, Debug)]
struct TestString {
string_val: String,
string_list: Vec<String>,
}
temp_env::with_vars(
vec![
("LIST_STRING_LIST", Some("test,string")),
("LIST_STRING_VAL", Some("test,string")),
],
|| {
let environment = Environment::default()
.prefix("LIST")
.list_separator(",")
.with_list_parse_key("string_list")
.try_parsing(true);
let config = Config::builder()
.set_default("tag", "String")
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestStringEnum = config.try_deserialize().unwrap();
match config {
TestStringEnum::String(TestString {
string_val,
string_list,
}) => {
assert_eq!(String::from("test,string"), string_val);
assert_eq!(
vec![String::from("test"), String::from("string")],
string_list
);
}
}
},
)
}
#[test]
fn test_parse_string() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestStringEnum {
String(TestString),
}
#[derive(Deserialize, Debug)]
struct TestString {
string_val: String,
}
temp_env::with_var("STRING_VAL", Some("test string"), || {
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("tag", "String")
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestStringEnum = config.try_deserialize().unwrap();
let test_string = String::from("test string");
match config {
TestStringEnum::String(TestString { string_val }) => {
assert_eq!(test_string, string_val)
}
}
})
}
#[test]
fn test_parse_string_list() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestListEnum {
StringList(TestList),
}
#[derive(Deserialize, Debug)]
struct TestList {
string_list: Vec<String>,
}
temp_env::with_var("STRING_LIST", Some("test string"), || {
let environment = Environment::default().try_parsing(true).list_separator(" ");
let config = Config::builder()
.set_default("tag", "StringList")
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestListEnum = config.try_deserialize().unwrap();
let test_string = vec![String::from("test"), String::from("string")];
match config {
TestListEnum::StringList(TestList { string_list }) => {
assert_eq!(test_string, string_list)
}
}
})
}
#[test]
fn test_parse_off_string() {
// using a struct in an enum here to make serde use `deserialize_any`
#[derive(Deserialize, Debug)]
#[serde(tag = "tag")]
enum TestStringEnum {
String(TestString),
}
#[derive(Deserialize, Debug)]
struct TestString {
string_val_1: String,
}
temp_env::with_var("STRING_VAL_1", Some("test string"), || {
let environment = Environment::default().try_parsing(false);
let config = Config::builder()
.set_default("tag", "String")
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestStringEnum = config.try_deserialize().unwrap();
let test_string = String::from("test string");
match config {
TestStringEnum::String(TestString { string_val_1 }) => {
assert_eq!(test_string, string_val_1);
}
}
})
}
#[test]
fn test_parse_int_default() {
#[derive(Deserialize, Debug)]
struct TestInt {
int_val: i32,
}
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("int_val", 42_i32)
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestInt = config.try_deserialize().unwrap();
assert_eq!(config.int_val, 42);
}
#[test]
fn test_parse_uint_default() {
#[derive(Deserialize, Debug)]
struct TestUint {
int_val: u32,
}
let environment = Environment::default().try_parsing(true);
let config = Config::builder()
.set_default("int_val", 42_u32)
.unwrap()
.add_source(environment)
.build()
.unwrap();
let config: TestUint = config.try_deserialize().unwrap();
assert_eq!(config.int_val, 42);
}