PureXML/Tests/SchemaReferenceTests.swift
Mihaela Mihaljevic 7efc64479b fix(schema): a ref resolving to the XSD namespace is undeclared
isUndeclaredReferenceRef fell through to a lenient accept when a ref or
substitutionGroup resolved to the XSD namespace but the schema did not target
it. The XSD namespace declares no referenceable user component (element,
group, attribute, attribute group), so such a reference is undeclared, the
asymmetry isUndeclaredReferenceType already handled for type/base.

This is reached when an unprefixed reference picks up a default xmlns of the
XSD namespace, so it is not in the target namespace where the schema's own
components live: xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="foo"
with <element ref="root"/> resolves to {XSD}root, not {foo}root (XSTS
xsd013/xsd014). A schema that itself targets the XSD namespace is the
inTargetNamespace case, and a genuinely loaded XSD-namespace document is the
foreignPools case before this branch, so both are unaffected.

Verified against the full W3C XSTS 2006-11-06 suite: invalid-schemas-accepted
29 -> 27, valid-schemas-rejected held at 0, no other bucket moved. Adds
reference tests for both reject cases and a qualified-ref guard.
2026-06-21 22:57:20 +02:00

137 lines
6.5 KiB
Swift

import Testing
@testable import PureXML
/// Every QName a schema names (a `type`/`base`/`itemType`/`memberTypes` type, an
/// element/attribute/group/attributeGroup `ref`, an element `substitutionGroup`)
/// must resolve to a declared component or a built-in. An undeclared reference
/// was previously accepted at compile time and only surfaced (if at all) lazily
/// during instance validation. The check is skipped when the document pulls in
/// external definitions through `import`/`include`/`redefine`, which the default
/// compile does not load.
@Suite("Schema reference resolution")
struct SchemaReferenceTests {
private func compile(_ body: String) throws {
_ = try PureXML.Schema.Document("""
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
\(body)
</xs:schema>
""")
}
private func rejects(_ body: String) -> Bool {
do { try compile(body)
return false
} catch { return true }
}
@Test("a reference to a declared component or a built-in resolves")
func test_resolves() throws {
try compile(#"<xs:element name="a" type="xs:string"/>"#)
try compile(#"<xs:simpleType name="t"><xs:restriction base="xs:integer"/></xs:simpleType><xs:element name="a" type="t"/>"#)
try compile(#"<xs:element name="g" type="xs:string"/><xs:complexType name="c"><xs:sequence><xs:element ref="g"/></xs:sequence></xs:complexType>"#)
}
@Test("an undeclared reference is rejected")
func test_undeclared() {
#expect(rejects(#"<xs:element name="a" type="DoesNotExist"/>"#))
#expect(rejects(#"<xs:simpleType name="t"><xs:restriction base="Missing"/></xs:simpleType>"#))
#expect(rejects(#"<xs:complexType name="c"><xs:sequence><xs:element ref="ghost"/></xs:sequence></xs:complexType>"#))
#expect(rejects(#"<xs:complexType name="c"><xs:group ref="noGroup"/></xs:complexType>"#))
}
@Test("a reference value tolerates surrounding whitespace (whiteSpace collapse)")
func test_whitespacePadded() throws {
try compile("<xs:simpleType name=\"t\"><xs:restriction base=\" xs:string \"/></xs:simpleType>")
}
@Test("a ref resolving to the XSD namespace is undeclared")
func test_refResolvingToXSDNamespaceIsUndeclared() throws {
// The default xmlns is the XSD namespace, so an unprefixed `ref="root"`
// resolves to {XSD}root, not {foo}root, and the XSD namespace declares no
// referenceable user element: undeclared (XSTS xsd013/xsd014).
let refRoot = """
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:foo="foo" targetNamespace="foo">
<element name="root"><complexType><sequence><element ref="root"/></sequence></complexType></element>
</schema>
"""
#expect(throws: (any Error).self) { _ = try PureXML.Schema.Document(refRoot) }
let refUndef = """
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:foo="foo" targetNamespace="foo">
<element name="root"><complexType><sequence><element ref="undef"/></sequence></complexType></element>
</schema>
"""
#expect(throws: (any Error).self) { _ = try PureXML.Schema.Document(refUndef) }
// Guard: the same shape with the ref qualified to the target resolves, so the
// XSD-namespace rule does not reject a correctly-namespaced reference.
let qualified = """
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:foo="foo" targetNamespace="foo">
<element name="root"><complexType><sequence><element ref="foo:root"/></sequence></complexType></element>
</schema>
"""
_ = try PureXML.Schema.Document(qualified)
}
@Test("references are not checked when an import may supply them")
func test_importSkipsCheck() throws {
// A dangling reference into an imported namespace is tolerated: the default
// compile does not load the import, so the reference cannot be verified.
try compile(#"""
<xs:import namespace="urn:other"/>
<xs:element name="a" type="other:Foo" xmlns:other="urn:other"/>
"""#)
}
@Test("references are checked strictly against the targetNamespace")
func test_referenceNamespaceChecking() throws {
// Without targetNamespace, unprefixed ref resolves to no namespace and is accepted
_ = try PureXML.Schema.Document("""
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:attribute name="number" type="xs:integer"/>
<xs:element name="elementWithAttr">
<xs:complexType>
<xs:attribute ref="number" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
""")
// With targetNamespace, unprefixed ref without default namespace resolves to no namespace but declaration is in targetNamespace -> rejected
#expect(throws: Error.self) {
_ = try PureXML.Schema.Document("""
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="AttrUse/attrDecl" xmlns:tn="AttrUse/attrDecl">
<xs:attribute name="number" type="xs:integer"/>
<xs:element name="elementWithAttr">
<xs:complexType>
<xs:attribute ref="number" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
""")
}
// With targetNamespace, prefix matching targetNamespace is accepted
_ = try PureXML.Schema.Document("""
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="AttrUse/attrDecl" xmlns:tn="AttrUse/attrDecl">
<xs:attribute name="number" type="xs:integer"/>
<xs:element name="elementWithAttr">
<xs:complexType>
<xs:attribute ref="tn:number" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
""")
// With targetNamespace, default namespace matching targetNamespace is accepted
_ = try PureXML.Schema.Document("""
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="AttrUse/attrDecl" xmlns="AttrUse/attrDecl">
<xs:attribute name="number" type="xs:integer"/>
<xs:element name="elementWithAttr">
<xs:complexType>
<xs:attribute ref="number" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
""")
}
}