mod common;
use fastxml::schema::Schema;
use fastxml::schema::xsd::constraints::{
ConstraintError, ConstraintValidator, IdentityConstraint, KeyValue,
};
#[test]
fn test_unique_duplicate_key() {
let mut validator = ConstraintValidator::new();
validator.register_key("uniqueId", 1);
let result = validator.add_key_value(
&IdentityConstraint::unique("uniqueId", "."),
KeyValue::single("key1"),
);
assert!(result.is_ok());
let result = validator.add_key_value(
&IdentityConstraint::unique("uniqueId", "."),
KeyValue::single("key1"),
);
assert!(
matches!(&result, Err(ConstraintError::DuplicateKey { constraint, .. }) if constraint == "uniqueId"),
"Expected DuplicateKey error, got: {:?}",
result
);
}
#[test]
fn test_key_null_value() {
let mut validator = ConstraintValidator::new();
validator.register_key("keyId", 1);
let constraint = IdentityConstraint::key("keyId", ".");
let result = validator.add_key_value(&constraint, KeyValue::new(vec![]));
assert!(
result.is_ok(),
"Implementation is lenient with empty key values"
);
}
#[test]
fn test_composite_key() {
let mut validator = ConstraintValidator::new();
validator.register_key("compositeKey", 2);
let constraint = IdentityConstraint::key("compositeKey", ".");
let result = validator.add_key_value(&constraint, KeyValue::new(vec!["a".into(), "1".into()]));
assert!(result.is_ok());
let result = validator.add_key_value(&constraint, KeyValue::new(vec!["a".into(), "2".into()]));
assert!(result.is_ok());
let result = validator.add_key_value(&constraint, KeyValue::new(vec!["a".into(), "1".into()]));
assert!(
matches!(&result, Err(ConstraintError::DuplicateKey { constraint, .. }) if constraint == "compositeKey"),
"Expected DuplicateKey error for composite key, got: {:?}",
result
);
}
#[test]
fn test_keyref_validation() {
let mut validator = ConstraintValidator::new();
validator.register_key("personId", 1);
let key_constraint = IdentityConstraint::key("personId", ".");
validator
.add_key_value(&key_constraint, KeyValue::single("p1"))
.unwrap();
validator
.add_key_value(&key_constraint, KeyValue::single("p2"))
.unwrap();
let keyref_constraint = IdentityConstraint::keyref("personRef", ".", "personId");
validator.add_keyref_value(&keyref_constraint, KeyValue::single("p1"));
validator.add_keyref_value(&keyref_constraint, KeyValue::single("p3"));
let result = validator.validate_keyrefs();
assert!(
matches!(&result, Err(errors) if !errors.is_empty() && errors.iter().any(|e| matches!(e, ConstraintError::KeyRefNotFound { constraint, .. } if constraint == "personRef"))),
"Expected KeyRefNotFound error, got: {:?}",
result
);
}
#[test]
fn test_integer_type_with_string() {
let xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="count" type="xs:integer"/>
</xs:schema>"#;
let schema = Schema::from_xsd(xsd.as_bytes()).unwrap();
assert!(schema.elements.contains_key("count"));
}
#[test]
fn test_date_type_invalid_format() {
let xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="birthday" type="xs:date"/>
</xs:schema>"#;
let schema = Schema::from_xsd(xsd.as_bytes()).unwrap();
assert!(schema.elements.contains_key("birthday"));
}
#[test]
fn test_boolean_type_invalid_value() {
let xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="flag" type="xs:boolean"/>
</xs:schema>"#;
let schema = Schema::from_xsd(xsd.as_bytes()).unwrap();
assert!(schema.elements.contains_key("flag"));
}
#[test]
fn test_decimal_type_precision() {
let xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="Price">
<xs:restriction base="xs:decimal">
<xs:totalDigits value="10"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>"#;
let schema = Schema::from_xsd(xsd.as_bytes()).unwrap();
assert!(schema.types.contains_key("Price"));
}
#[test]
fn test_wrong_namespace() {
let xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/correct"
elementFormDefault="qualified">
<xs:element name="item" type="xs:string"/>
</xs:schema>"#;
let schema = Schema::from_xsd(xsd.as_bytes()).unwrap();
assert_eq!(
schema.target_namespace,
Some("http://example.com/correct".to_string())
);
}
#[test]
fn test_missing_namespace_declaration() {
let xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="item" type="custom:Type"/>
</xs:schema>"#;
let result = Schema::from_xsd(xsd.as_bytes());
assert!(
result.is_err(),
"undeclared prefix in a type reference must be rejected"
);
}
#[test]
fn unique_is_scoped_per_declaring_element_instance() {
use std::sync::Arc;
const SCHEMA: &[u8] = br#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element ref="group" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="group">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:unique name="uniqueItemName">
<xs:selector xpath="item"/>
<xs:field xpath="@name"/>
</xs:unique>
</xs:element>
</xs:schema>"#;
const VALID: &str = r#"<root>
<group><item name="a"/><item name="b"/></group>
<group><item name="a"/><item name="b"/></group>
</root>"#;
const INVALID: &str = r#"<root>
<group><item name="a"/><item name="a"/></group>
<group><item name="b"/></group>
</root>"#;
let schema = Arc::new(Schema::from_xsd(SCHEMA).expect("schema"));
for (xml, want_valid) in [(VALID, true), (INVALID, false)] {
let doc = fastxml::Parser::from(xml).parse().expect("parse");
let dom = fastxml::schema::Validator::from(&doc)
.schema(Arc::clone(&schema))
.run()
.expect("dom")
.into_entries()
.is_empty();
let stream = fastxml::schema::Validator::from(xml.as_bytes())
.schema(Arc::clone(&schema))
.run()
.expect("stream")
.into_entries()
.is_empty();
assert_eq!(dom, want_valid, "DOM verdict wrong for: {xml}");
assert_eq!(stream, want_valid, "streaming verdict wrong for: {xml}");
}
}