use fastxml::Parser;
use fastxml::schema::{Schema, Validator};
const BASE_XSD: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="PersonType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="SizeType">
<xs:restriction base="xs:integer">
<xs:maxInclusive value="100"/>
</xs:restriction>
</xs:simpleType>
<xs:group name="InfoGroup">
<xs:sequence>
<xs:element name="info" type="xs:string"/>
</xs:sequence>
</xs:group>
</xs:schema>"#;
const REDEFINING_XSD: &str = r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:redefine schemaLocation="base.xsd">
<xs:complexType name="PersonType">
<xs:complexContent>
<xs:extension base="PersonType">
<xs:sequence>
<xs:element name="age" type="xs:int"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:simpleType name="SizeType">
<xs:restriction base="SizeType">
<xs:maxInclusive value="10"/>
</xs:restriction>
</xs:simpleType>
<xs:group name="InfoGroup">
<xs:sequence>
<xs:group ref="InfoGroup"/>
<xs:element name="extra" type="xs:string"/>
</xs:sequence>
</xs:group>
</xs:redefine>
<xs:element name="person" type="PersonType"/>
<xs:element name="size" type="SizeType"/>
<xs:element name="record">
<xs:complexType>
<xs:group ref="InfoGroup"/>
</xs:complexType>
</xs:element>
</xs:schema>"#;
fn schema() -> Schema {
Schema::builder()
.add("file:///test/base.xsd", BASE_XSD.as_bytes().to_vec())
.add("file:///test/entry.xsd", REDEFINING_XSD.as_bytes().to_vec())
.resolve()
.expect("schema with redefine should compile")
}
fn validate(schema: &Schema, xml: &str) -> Vec<String> {
let doc = Parser::from(xml).parse().unwrap();
Validator::from(&doc)
.schema(schema.clone())
.run()
.unwrap()
.errors()
.iter()
.map(|e| e.message.to_string())
.collect()
}
#[test]
fn redefined_complex_type_extends_original() {
let schema = schema();
let valid = "<person><name>taro</name><age>10</age></person>";
let errors = validate(&schema, valid);
assert!(errors.is_empty(), "expected valid, got: {errors:?}");
let missing_age = "<person><name>taro</name></person>";
let errors = validate(&schema, missing_age);
assert!(!errors.is_empty(), "expected missing-age error");
let missing_name = "<person><age>10</age></person>";
let errors = validate(&schema, missing_name);
assert!(!errors.is_empty(), "expected missing-name error");
}
#[test]
fn redefined_simple_type_restricts_original() {
let schema = schema();
assert!(validate(&schema, "<size>5</size>").is_empty());
assert!(
!validate(&schema, "<size>50</size>").is_empty(),
"redefined SizeType must enforce the narrower bound"
);
}
#[test]
fn redefined_group_wraps_original() {
let schema = schema();
let valid = "<record><info>i</info><extra>e</extra></record>";
let errors = validate(&schema, valid);
assert!(errors.is_empty(), "expected valid, got: {errors:?}");
let missing_extra = "<record><info>i</info></record>";
assert!(
!validate(&schema, missing_extra).is_empty(),
"redefined group requires the extra element"
);
}
#[test]
fn non_redefined_components_still_resolve() {
let schema = schema();
assert!(validate(&schema, "<record><info>i</info><extra>e</extra></record>").is_empty());
}