#![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)]
use std::collections::BTreeMap;
use oneyaml::{Limits, Sequence, Settings, Value, code};
use serde::{Serialize, Serializer};
#[derive(Serialize, serde::Deserialize, Debug, PartialEq)]
struct Config {
name: String,
port: u16,
tags: Vec<String>,
limits: BTreeMap<String, f64>,
note: Option<String>,
}
fn config() -> Config {
Config {
name: "web".into(),
port: 8080,
tags: vec!["a".into(), "yes".into()],
limits: BTreeMap::from([("cpu".into(), 0.5)]),
note: None,
}
}
#[test]
fn to_value_is_what_the_serialized_text_reads_as() {
let v = oneyaml::to_value(config()).unwrap();
let text = oneyaml::to_string(&config()).unwrap();
let read: Value = oneyaml::from_str(&text).unwrap();
assert_eq!(v, read);
assert_eq!(v["port"].as_u64(), Some(8080));
assert!(v["note"].is_null());
let back: Config = oneyaml::from_value(v).unwrap();
assert_eq!(back, config());
}
#[test]
fn to_value_of_a_value_is_the_value() {
let src = "a: !t [1, {b: 2}]\n? [k]\n: 0x10\n";
let v: Value = oneyaml::from_str(src).unwrap();
assert_eq!(oneyaml::to_value(&v).unwrap(), v);
}
#[test]
fn to_value_with_applies_max_typed_depth() {
let shallow = Settings::new().limits(Limits::default().max_typed_depth(2));
assert!(oneyaml::to_value_with(vec![vec![1]], &shallow).is_ok());
let e = oneyaml::to_value_with(vec![vec![vec![1]]], &shallow).unwrap_err();
assert_eq!(e.code(), code::TYPED_DEPTH_EXCEEDED);
let mut deep = serde_json::json!(1);
for _ in 0..200 {
deep = serde_json::json!([deep]);
}
let e = oneyaml::to_value(&deep).unwrap_err();
assert_eq!(e.code(), code::TYPED_DEPTH_EXCEEDED);
}
#[test]
fn to_value_refuses_duplicate_keys() {
struct Twice;
impl Serialize for Twice {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let mut m = s.serialize_map(Some(2))?;
m.serialize_entry("a", &1)?;
m.serialize_entry("a", &2)?;
m.end()
}
}
let e = oneyaml::to_value(&Twice).unwrap_err();
assert_eq!(e.code(), code::SERIALIZE_DUPLICATE_KEY);
}
#[test]
fn to_value_reports_a_failing_serialize_impl() {
struct Fails;
impl Serialize for Fails {
fn serialize<S: Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
Err(serde::ser::Error::custom("no"))
}
}
let e = oneyaml::to_value(vec![Fails]).unwrap_err();
assert_eq!(e.code(), code::SERIALIZE_CUSTOM);
}
fn deep(depth: usize) -> Value {
let mut v = Value::from(1);
for _ in 0..depth {
v = Value::Sequence(Sequence::from(vec![v]));
}
v
}
#[test]
fn to_value_applies_max_depth() {
let v = deep(2_000);
let e = oneyaml::to_value(&v).unwrap_err();
assert_eq!(e.code(), code::VALUE_DEPTH_EXCEEDED);
assert_eq!(
oneyaml::to_string(&v).unwrap_err().code(),
code::VALUE_DEPTH_EXCEEDED
);
assert_eq!(
oneyaml::from_value::<Value>(v.clone()).unwrap_err().code(),
code::VALUE_DEPTH_EXCEEDED
);
assert_eq!(
oneyaml::to_value(v).unwrap_err().code(),
code::VALUE_DEPTH_EXCEEDED
);
}
#[test]
fn to_value_checks_the_combined_depth() {
#[derive(Serialize)]
struct Holder {
a: Value,
b: Value,
}
let limit = Settings::new().max_depth(10);
let fits = deep(10);
assert!(oneyaml::to_value_with(&fits, &limit).is_ok());
let h = Holder {
a: fits.clone(),
b: fits,
};
let e = oneyaml::to_value_with(&h, &limit).unwrap_err();
assert_eq!(e.code(), code::VALUE_DEPTH_EXCEEDED);
assert_eq!(
oneyaml::to_string_with(&h, &limit).unwrap_err().code(),
code::VALUE_DEPTH_EXCEEDED
);
let roomy = Settings::new().max_depth(11);
assert!(oneyaml::to_value_with(&h, &roomy).is_ok());
let one = Settings::new().max_depth(1);
assert!(oneyaml::to_value_with(vec![1], &one).is_ok());
let e = oneyaml::to_value_with(vec![vec![1]], &one).unwrap_err();
assert_eq!(e.code(), code::VALUE_DEPTH_EXCEEDED);
}
#[test]
fn a_deep_value_converts_on_a_small_stack() {
std::thread::Builder::new()
.stack_size(128 * 1024)
.spawn(|| {
let v = deep(100_000);
let e = oneyaml::to_value(&v).unwrap_err();
assert_eq!(e.code(), code::VALUE_DEPTH_EXCEEDED);
let unlimited = Settings::new().limits(Limits::unlimited());
let out = oneyaml::to_value_with(&v, &unlimited).unwrap();
assert!(out == v);
})
.unwrap()
.join()
.unwrap();
}