PureXML/Tests/SchemaXSIAttributeTests.swift
Mihaela Mihaljevic 6e7f1b7bfc fix(schema): an unprefixed type ref to a target-namespace type is undeclared
isUndeclaredReferenceType matched a no-namespace type/base/itemType/memberTypes
reference by local name, ignoring that an unprefixed QName with no default
xmlns resolves to the absent namespace: type="t" is {}t, not {urn:x}t where the
type is declared, so the reference does not resolve (XSD 1.0 §3.15.3). It now
mirrors isUndeclaredReferenceRef's nil-namespace branch: a no-namespace
reference is undeclared when the schema has a non-empty target namespace; a
chameleon-included global and an imported no-namespace type (via the foreign
pools, checked first) remain accepted.

This corrects an asymmetry where the ref side already rejected the pattern but
the type side did not. The existing SchemaXSIAttributeTests case that asserted
this very pattern compiles used an invalid schema (the same shape as XSTS
addB009/test66059); its type reference is now qualified (tns:attRef), which
preserves the test's xsi:Not-Allowed intent while being valid.

Verified against the full W3C XSTS 2006-11-06 suite: invalid-schemas-accepted
27 -> 25 (addB009/test66059 and one more), valid-schemas-rejected held at 0,
no other bucket moved. Adds a reference test for the type-side rule.
2026-06-21 23:17:14 +02:00

98 lines
4.2 KiB
Swift

import Testing
@testable import PureXML
/// XSD 1.0 Schema Component Constraint "xsi: Not Allowed" (#183): an attribute
/// declaration's {target namespace} must not be the XSI namespace. A schema may
/// target XSI and declare unqualified attributes (they land in no namespace), but a
/// top-level or qualified attribute would land in XSI and is forbidden. Mirrors
/// corpus attKb018a (invalid) against attKb018 / attKc018 (valid).
@Suite("XSD xsi: Not Allowed for attribute declarations (#183)")
struct SchemaXSIAttributeTests {
private let xsi = "http://www.w3.org/2001/XMLSchema-instance"
private func compiles(_ schema: String) -> Bool {
(try? PureXML.Schema.Document(schema)) != nil
}
@Test("attKb018a: attributeFormDefault=qualified forces attributes into XSI: rejected")
func test_qualifiedAttributeIntoXSIRejected() {
let schema = """
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="\(xsi)" xmlns:x="\(xsi)" attributeFormDefault="qualified">
<xsd:attributeGroup name="attG">
<xsd:attribute name="aga1"/>
<xsd:attribute name="aga2"/>
</xsd:attributeGroup>
<xsd:complexType name="attRef">
<xsd:attributeGroup ref="x:attG"/>
</xsd:complexType>
<xsd:element name="doc" type="x:attRef"/>
</xsd:schema>
"""
#expect(!compiles(schema), "qualified attributes land in XSI; xsi: Not Allowed")
}
@Test("attKb018: same schema with unqualified attributes (no-namespace) stays valid")
func test_unqualifiedAttributesValid() {
let schema = """
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="\(xsi)" xmlns:x="\(xsi)">
<xsd:attributeGroup name="attG">
<xsd:attribute name="aga1"/>
<xsd:attribute name="aga2"/>
</xsd:attributeGroup>
<xsd:complexType name="attRef">
<xsd:attributeGroup ref="x:attG"/>
</xsd:complexType>
<xsd:element name="doc" type="x:attRef"/>
</xsd:schema>
"""
#expect(compiles(schema), "unqualified attributes land in no namespace, not XSI")
}
@Test("attKc018: local unqualified attributes targeting XSI stay valid")
func test_localUnqualifiedAttributesValid() {
let schema = """
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="\(xsi)" xmlns:x="\(xsi)">
<xsd:complexType name="attRef">
<xsd:attribute name="ca1"/>
<xsd:attribute name="ca2"/>
</xsd:complexType>
<xsd:element name="doc" type="x:attRef"/>
</xsd:schema>
"""
#expect(compiles(schema), "local unqualified attributes are in no namespace")
}
@Test("a single local attribute with form=qualified into XSI is rejected")
func test_localFormQualifiedIntoXSIRejected() {
let schema = """
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="\(xsi)">
<xsd:complexType name="attRef">
<xsd:attribute name="ca1" form="qualified"/>
</xsd:complexType>
<xsd:element name="doc" type="attRef"/>
</xsd:schema>
"""
#expect(!compiles(schema), "form=qualified puts ca1 into XSI; xsi: Not Allowed")
}
@Test("the same qualified attribute targeting a non-XSI namespace is valid")
func test_qualifiedAttributeOutsideXSIValid() {
// The type reference is qualified to the target namespace (`tns:attRef`); an
// unprefixed `attRef` with no default xmlns would resolve to {}attRef, not
// {urn:ok}attRef, and is itself invalid (see SchemaReferenceTests).
let schema = """
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="urn:ok"
targetNamespace="urn:ok" attributeFormDefault="qualified">
<xsd:complexType name="attRef">
<xsd:attribute name="ca1"/>
</xsd:complexType>
<xsd:element name="doc" type="tns:attRef"/>
</xsd:schema>
"""
#expect(compiles(schema), "qualifying into a non-XSI namespace is fine")
}
}