PureXML/Tests/XSDSchemaTests.swift

158 lines
6.0 KiB
Swift

import Testing
@testable import PureXML
@Suite("XSD schema documents")
struct XSDSchemaTests {
private func validate(_ xsd: String, _ xml: String) throws -> [PureXML.Validation.ValidationError] {
try PureXML.Schema.Document(xsd).validate(xml)
}
@Test("A nested type violation is located by coding path")
func test_violationCarriesPath() throws {
let xsd = """
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="order">
<xs:complexType>
<xs:sequence><xs:element name="qty" type="xs:integer"/></xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
"""
let found = try validate(xsd, "<order><qty>lots</qty></order>")
#expect(found.count == 1)
#expect(String(describing: found[0]).hasSuffix("at path: order/qty"))
}
@Test("A simple element with a built-in type")
func test_simpleElement() throws {
let xsd = """
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="age" type="xs:nonNegativeInteger"/>
</xs:schema>
"""
#expect(try validate(xsd, "<age>30</age>").isEmpty)
#expect(try !validate(xsd, "<age>-5</age>").isEmpty)
#expect(try !validate(xsd, "<age>x</age>").isEmpty)
}
@Test("A named simpleType with facets")
func test_namedSimpleType() throws {
let xsd = """
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="sku">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]{3}-\\d{4}"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="code" type="sku"/>
</xs:schema>
"""
#expect(try validate(xsd, "<code>ABC-1234</code>").isEmpty)
#expect(try !validate(xsd, "<code>abc-1234</code>").isEmpty)
}
@Test("A complex type with a sequence and attributes")
func test_complexType() throws {
let xsd = """
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="book">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="year" type="xs:gYear" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="isbn" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
"""
#expect(try validate(xsd, "<book isbn=\"1\"><title>T</title><year>2026</year></book>").isEmpty)
#expect(try validate(xsd, "<book isbn=\"1\"><title>T</title></book>").isEmpty)
#expect(try !validate(xsd, "<book><title>T</title></book>").isEmpty) // missing isbn
#expect(try !validate(xsd, "<book isbn=\"1\"><year>2026</year></book>").isEmpty) // missing title
#expect(try !validate(xsd, "<book isbn=\"1\"><title>T</title><year>nope</year></book>").isEmpty)
}
@Test("A huge finite maxOccurs is preserved beyond Int range")
func test_hugeFiniteMaxOccurs() throws {
let xsd = """
<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="100000000000000000000"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
"""
#expect(try validate(xsd, "<root><item/><item/></root>").isEmpty)
#expect(try !validate(xsd, "<root/>").isEmpty)
}
@Test("A named complex type referenced by elements")
func test_namedComplexType() throws {
let xsd = """
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="point">
<xs:sequence>
<xs:element name="x" type="xs:decimal"/>
<xs:element name="y" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
<xs:element name="location" type="point"/>
</xs:schema>
"""
#expect(try validate(xsd, "<location><x>1.5</x><y>2.5</y></location>").isEmpty)
#expect(try !validate(xsd, "<location><x>1.5</x></location>").isEmpty)
}
@Test("Recursive types validate to any depth")
func test_recursiveType() throws {
let xsd = """
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="node">
<xs:sequence>
<xs:element name="value" type="xs:int"/>
<xs:element name="child" type="node" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:element name="tree" type="node"/>
</xs:schema>
"""
let nested = "<tree><value>1</value><child><value>2</value><child><value>3</value></child></child></tree>"
#expect(try validate(xsd, nested).isEmpty)
let bad = "<tree><value>1</value><child><value>x</value></child></tree>"
#expect(try !validate(xsd, bad).isEmpty)
}
@Test("Simple content restricts the text and keeps attributes")
func test_simpleContent() throws {
let xsd = """
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="price">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="currency" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>
"""
#expect(try validate(xsd, "<price currency=\"USD\">9.99</price>").isEmpty)
#expect(try !validate(xsd, "<price>nope</price>").isEmpty)
}
@Test("An unknown root element is reported")
func test_unknownRoot() throws {
let xsd = """
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="known" type="xs:string"/>
</xs:schema>
"""
#expect(try !validate(xsd, "<unknown/>").isEmpty)
}
}