mod common;
use std::sync::Arc;
use fastxml::event::{XmlEvent, XmlEventHandler};
use fastxml::schema::types::CompiledSchema;
use fastxml::schema::validator::OnePassSchemaValidator;
use fastxml::schema::xsd::{create_builtin_schema, parse_xsd};
fn create_test_schema() -> CompiledSchema {
let xsd = r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="RootType"/>
<xs:complexType name="RootType">
<xs:sequence>
<xs:element name="required" type="xs:string"/>
<xs:element name="optional" type="xs:string" minOccurs="0"/>
<xs:element name="bounded" type="xs:string" minOccurs="1" maxOccurs="3"/>
<xs:element name="integer" type="xs:integer" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="RestrictedString">
<xs:restriction base="xs:string">
<xs:minLength value="3"/>
<xs:maxLength value="10"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="EnumType">
<xs:restriction base="xs:string">
<xs:enumeration value="A"/>
<xs:enumeration value="B"/>
<xs:enumeration value="C"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="PatternType">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{3}-[0-9]{4}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="RangeType">
<xs:restriction base="xs:integer">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="100"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>"#;
parse_xsd(xsd.as_bytes()).unwrap()
}
#[test]
fn test_validator_with_builtin_types() {
let schema = create_builtin_schema();
let validator = OnePassSchemaValidator::new(Arc::new(schema));
assert!(validator.is_valid());
}
#[test]
fn test_validator_events() {
let schema = create_builtin_schema();
let mut validator = OnePassSchemaValidator::new(Arc::new(schema));
validator
.handle(&XmlEvent::StartElement {
name: "root".into(),
prefix: None,
namespace: None,
attributes: vec![],
namespace_decls: vec![],
line: Some(1),
column: Some(1),
})
.unwrap();
validator
.handle(&XmlEvent::Text("some text".into()))
.unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "root".into(),
prefix: None,
})
.unwrap();
validator.handle(&XmlEvent::Eof).unwrap();
validator.finish().unwrap();
assert!(validator.is_valid());
}
#[test]
fn test_validator_collects_errors() {
let schema = create_test_schema();
let mut validator = OnePassSchemaValidator::new(Arc::new(schema));
validator
.handle(&XmlEvent::StartElement {
name: "root".into(),
prefix: None,
namespace: None,
attributes: vec![],
namespace_decls: vec![],
line: Some(1),
column: Some(1),
})
.unwrap();
validator
.handle(&XmlEvent::EndElement {
name: "root".into(),
prefix: None,
})
.unwrap();
validator.handle(&XmlEvent::Eof).unwrap();
let errors = validator.errors();
let _ = errors;
}
const XSD_MAX_OCCURS: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="item" type="xs:string" maxOccurs="2"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#;
const XML_MAX_OCCURS_VALID: &str = r#"<?xml version="1.0"?>
<root>
<item>first</item>
<item>second</item>
</root>"#;
const XML_MAX_OCCURS_EXCEEDED: &str = r#"<?xml version="1.0"?>
<root>
<item>first</item>
<item>second</item>
<item>third</item>
</root>"#;
test_validation!(max_occurs_valid, XML_MAX_OCCURS_VALID, XSD_MAX_OCCURS, true);
test_validation!(
max_occurs_exceeded,
XML_MAX_OCCURS_EXCEEDED,
XSD_MAX_OCCURS,
false
);
const XSD_MIN_OCCURS: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="required" type="xs:string" minOccurs="1"/>
<xs:element name="optional" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#;
const XML_MIN_OCCURS_VALID: &str = r#"<?xml version="1.0"?>
<root>
<required>value</required>
</root>"#;
const XML_MIN_OCCURS_MISSING: &str = r#"<?xml version="1.0"?>
<root>
<optional>value</optional>
</root>"#;
test_validation!(min_occurs_valid, XML_MIN_OCCURS_VALID, XSD_MIN_OCCURS, true);
test_validation!(
min_occurs_missing,
XML_MIN_OCCURS_MISSING,
XSD_MIN_OCCURS,
false
);
const XSD_SEQUENCE: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="first" type="xs:string"/>
<xs:element name="second" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#;
const XML_SEQUENCE_VALID: &str = r#"<?xml version="1.0"?>
<root>
<first>1</first>
<second>2</second>
</root>"#;
const XML_SEQUENCE_WRONG_ORDER: &str = r#"<?xml version="1.0"?>
<root>
<second>2</second>
<first>1</first>
</root>"#;
test_validation!(sequence_order_valid, XML_SEQUENCE_VALID, XSD_SEQUENCE, true);
test_validation!(
sequence_order_invalid,
XML_SEQUENCE_WRONG_ORDER,
XSD_SEQUENCE,
false
);
const XSD_KNOWN_ELEMENTS: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="known" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"#;
const XML_UNKNOWN_ELEMENT: &str = r#"<?xml version="1.0"?>
<root>
<known>ok</known>
<unknown>bad</unknown>
</root>"#;
test_validation!(
unknown_element,
XML_UNKNOWN_ELEMENT,
XSD_KNOWN_ELEMENTS,
false
);
const XSD_PATTERN: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="code">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{3}-[0-9]{4}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>"#;
const XML_PATTERN_VALID: &str = r#"<?xml version="1.0"?><code>ABC-1234</code>"#;
const XML_PATTERN_INVALID: &str = r#"<?xml version="1.0"?><code>abc-1234</code>"#;
test_validation!(pattern_valid, XML_PATTERN_VALID, XSD_PATTERN, true);
test_validation!(pattern_invalid, XML_PATTERN_INVALID, XSD_PATTERN, false);
const XSD_ENUM: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="status">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="active"/>
<xs:enumeration value="inactive"/>
<xs:enumeration value="pending"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>"#;
const XML_ENUM_VALID: &str = r#"<?xml version="1.0"?><status>active</status>"#;
const XML_ENUM_INVALID: &str = r#"<?xml version="1.0"?><status>unknown</status>"#;
test_validation!(enumeration_valid, XML_ENUM_VALID, XSD_ENUM, true);
test_validation!(enumeration_invalid, XML_ENUM_INVALID, XSD_ENUM, false);
const XSD_LENGTH: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="name">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="3"/>
<xs:maxLength value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>"#;
const XML_LENGTH_VALID: &str = r#"<?xml version="1.0"?><name>hello</name>"#;
const XML_LENGTH_TOO_SHORT: &str = r#"<?xml version="1.0"?><name>ab</name>"#;
const XML_LENGTH_TOO_LONG: &str = r#"<?xml version="1.0"?><name>this is way too long</name>"#;
test_validation!(length_valid, XML_LENGTH_VALID, XSD_LENGTH, true);
test_validation!(length_too_short, XML_LENGTH_TOO_SHORT, XSD_LENGTH, false);
test_validation!(length_too_long, XML_LENGTH_TOO_LONG, XSD_LENGTH, false);