use kynos_openapi::{
Document, Severity, SpecError, SpecVersion, Violation,
emit::downgrade::three_two_only_constructs, validate::Validator,
};
use proptest::prelude::*;
#[path = "support/mod.rs"]
mod support;
use support::*;
fn to_json(document: &Document) -> String {
document
.to_json()
.expect("every generated value is representable in JSON")
}
fn parse(json: &str) -> Document {
serde_json::from_str(json).expect("what the model emits, the model reads")
}
fn rendered(violations: &[Violation]) -> Vec<String> {
let mut rendered: Vec<String> = violations.iter().map(ToString::to_string).collect();
rendered.sort();
rendered
}
fn rendered_errors(violations: &[Violation]) -> Vec<String> {
let mut rendered: Vec<String> = violations
.iter()
.filter(|violation| violation.severity == Severity::Error)
.map(ToString::to_string)
.collect();
rendered.sort();
rendered
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(128))]
#[test]
fn a_document_survives_a_json_round_trip(document in arb_document()) {
prop_assert_eq!(parse(&to_json(&document)), document);
}
#[test]
fn serialization_is_deterministic(document in arb_document()) {
let json = to_json(&document);
prop_assert_eq!(to_json(&document.clone()), json.clone());
prop_assert_eq!(to_json(&parse(&json)), json);
}
#[test]
fn validation_is_total(document in arb_document()) {
for &version in VERSIONS {
let validator = Validator::new(version);
let violations = validator.validate(&document);
prop_assert_eq!(rendered(&validator.validate(&document)), rendered(&violations));
let errors = document.validate(version).err().unwrap_or_default();
prop_assert_eq!(rendered(&errors), rendered_errors(&violations));
for violation in &violations {
prop_assert!(violation.location.starts_with('#'));
}
}
}
#[test]
fn emitting_refuses_a_lossy_downgrade(document in arb_document()) {
let blockers = three_two_only_constructs(&document);
match document.emit(SpecVersion::V3_1) {
Ok(emitted) => {
prop_assert!(blockers.is_empty());
prop_assert_eq!(&emitted.openapi, "3.1.2");
prop_assert_eq!(emitted.emit(SpecVersion::V3_1).ok(), Some(emitted.clone()));
prop_assert_eq!(three_two_only_constructs(&emitted), blockers);
}
Err(error) => {
prop_assert!(!blockers.is_empty());
prop_assert_eq!(error, SpecError::RequiresV3_2 { blockers });
}
}
}
#[cfg(feature = "openapi32")]
#[test]
fn emitting_as_the_newer_version_always_succeeds(document in arb_document()) {
let emitted = document.emit(SpecVersion::V3_2).expect("3.2 expresses everything");
prop_assert_eq!(&emitted.openapi, "3.2.0");
prop_assert_eq!(emitted.emit(SpecVersion::V3_2).ok(), Some(emitted.clone()));
prop_assert_eq!(emitted.spec_version(), Some(SpecVersion::V3_2));
}
}