The two monolithic XSD content/identity rules only satisfied the OpenAPIKit validation idiom at the wrong granularity: no per-constraint removability, no isolable failing/near-miss tests. Split them into 32 named rules, each finding tagged at its origin and cached per XSDContext so the walk still runs once per document (the #101 decision against duplicating XSD's recursive type resolution still holds). Wired the streaming path's ID/IDREF and identity checks through the same framework instead of calling the validators imperatively, and added the unwrapAndLookup combinator the rule already named as required. Also includes the mandated repo-wide swiftformat/swiftlint pass.
206 lines
9.1 KiB
Swift
206 lines
9.1 KiB
Swift
import Testing
|
|
@testable import PureXML
|
|
|
|
/// Per-rule isolation tests (Validation rules XSD): Validation rules XSD: attributes.
|
|
@Suite("Validation rules XSD: attributes")
|
|
struct ValidationRuleXSDAttributeTests {
|
|
// MARK: attributeTypeValid / attributeFixedMatches / attributeRequiredPresent / attributeSingleID
|
|
|
|
private let requiredIntAttributeSchema = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:element name="n"><xs:complexType><xs:attribute name="a" type="xs:integer" use="required"/></xs:complexType></xs:element>
|
|
</xs:schema>
|
|
"""
|
|
|
|
@Test("attributeTypeValid rejects an attribute value invalid against its type")
|
|
func test_attributeTypeValid() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(PureXML.Validation.XSD.attributeTypeValid, schema: requiredIntAttributeSchema, instance: "<n a=\"x\"/>", rootName: "n")
|
|
#expect(errors.count == 1)
|
|
#expect(errors.first?.reason.contains("attribute 'a'") == true)
|
|
}
|
|
|
|
@Test("attributeTypeValid accepts a valid attribute value")
|
|
func test_attributeTypeValid_succeeds() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(PureXML.Validation.XSD.attributeTypeValid, schema: requiredIntAttributeSchema, instance: "<n a=\"0\"/>", rootName: "n")
|
|
#expect(errors.isEmpty)
|
|
}
|
|
|
|
@Test("attributeRequiredPresent rejects a missing required attribute")
|
|
func test_attributeRequiredPresent() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(PureXML.Validation.XSD.attributeRequiredPresent, schema: requiredIntAttributeSchema, instance: "<n/>", rootName: "n")
|
|
#expect(errors.count == 1)
|
|
#expect(errors.first?.reason.contains("missing required attribute") == true)
|
|
}
|
|
|
|
@Test("attributeRequiredPresent accepts a present required attribute")
|
|
func test_attributeRequiredPresent_succeeds() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(
|
|
PureXML.Validation.XSD.attributeRequiredPresent,
|
|
schema: requiredIntAttributeSchema,
|
|
instance: "<n a=\"0\"/>",
|
|
rootName: "n",
|
|
)
|
|
#expect(errors.isEmpty)
|
|
}
|
|
|
|
private let fixedAttributeSchema = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:element name="n"><xs:complexType><xs:attribute name="a" type="xs:string" fixed="5"/></xs:complexType></xs:element>
|
|
</xs:schema>
|
|
"""
|
|
|
|
@Test("attributeFixedMatches rejects a value that differs from the fixed value")
|
|
func test_attributeFixedMatches() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(PureXML.Validation.XSD.attributeFixedMatches, schema: fixedAttributeSchema, instance: "<n a=\"6\"/>", rootName: "n")
|
|
#expect(errors.count == 1)
|
|
#expect(errors.first?.reason.contains("is fixed and must be") == true)
|
|
}
|
|
|
|
@Test("attributeFixedMatches accepts a value equal to the fixed value")
|
|
func test_attributeFixedMatches_succeeds() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(PureXML.Validation.XSD.attributeFixedMatches, schema: fixedAttributeSchema, instance: "<n a=\"5\"/>", rootName: "n")
|
|
#expect(errors.isEmpty)
|
|
}
|
|
|
|
private let twoIDAttributesSchema = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:element name="n"><xs:complexType><xs:attribute name="a" type="xs:ID"/><xs:attribute name="b" type="xs:ID"/></xs:complexType></xs:element>
|
|
</xs:schema>
|
|
"""
|
|
|
|
@Test("attributeSingleID rejects more than one attribute of type ID")
|
|
func test_attributeSingleID() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(
|
|
PureXML.Validation.XSD.attributeSingleID,
|
|
schema: twoIDAttributesSchema,
|
|
instance: "<n a=\"x1\" b=\"x2\"/>",
|
|
rootName: "n",
|
|
)
|
|
#expect(errors.count == 1)
|
|
#expect(errors.first?.reason.contains("at most one attribute of type ID") == true)
|
|
}
|
|
|
|
@Test("attributeSingleID accepts a single attribute of type ID")
|
|
func test_attributeSingleID_succeeds() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(PureXML.Validation.XSD.attributeSingleID, schema: twoIDAttributesSchema, instance: "<n a=\"x1\"/>", rootName: "n")
|
|
#expect(errors.isEmpty)
|
|
}
|
|
|
|
// MARK: attributeDeclaredOrPermitted
|
|
|
|
private let emptyContentSchema = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:element name="n"><xs:complexType/></xs:element>
|
|
</xs:schema>
|
|
"""
|
|
|
|
@Test("attributeDeclaredOrPermitted rejects an undeclared attribute with no wildcard")
|
|
func test_attributeDeclaredOrPermitted() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(
|
|
PureXML.Validation.XSD.attributeDeclaredOrPermitted,
|
|
schema: emptyContentSchema,
|
|
instance: "<n foo=\"bar\"/>",
|
|
rootName: "n",
|
|
)
|
|
#expect(errors.count == 1)
|
|
#expect(errors.first?.reason.contains("undeclared attribute") == true)
|
|
}
|
|
|
|
@Test("attributeDeclaredOrPermitted accepts no undeclared attributes")
|
|
func test_attributeDeclaredOrPermitted_succeeds() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(PureXML.Validation.XSD.attributeDeclaredOrPermitted, schema: emptyContentSchema, instance: "<n/>", rootName: "n")
|
|
#expect(errors.isEmpty)
|
|
}
|
|
|
|
// MARK: wildcardAttributeUndeclared / wildcardAttributeTypeValid / wildcardAttributeFixedMatches
|
|
|
|
private let strictWildcardAttributeSchema = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:element name="n"><xs:complexType><xs:anyAttribute processContents="strict"/></xs:complexType></xs:element>
|
|
</xs:schema>
|
|
"""
|
|
|
|
@Test("wildcardAttributeUndeclared rejects a strict wildcard match with no global declaration")
|
|
func test_wildcardAttributeUndeclared() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(
|
|
PureXML.Validation.XSD.wildcardAttributeUndeclared,
|
|
schema: strictWildcardAttributeSchema,
|
|
instance: "<n foo=\"bar\"/>",
|
|
rootName: "n",
|
|
)
|
|
#expect(errors.count == 1)
|
|
#expect(errors.first?.reason.contains("no declaration for wildcard-matched attribute") == true)
|
|
}
|
|
|
|
@Test("wildcardAttributeUndeclared accepts no wildcard-matched attributes")
|
|
func test_wildcardAttributeUndeclared_succeeds() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(
|
|
PureXML.Validation.XSD.wildcardAttributeUndeclared,
|
|
schema: strictWildcardAttributeSchema,
|
|
instance: "<n/>",
|
|
rootName: "n",
|
|
)
|
|
#expect(errors.isEmpty)
|
|
}
|
|
|
|
private let laxWildcardIntAttributeSchema = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:attribute name="foo" type="xs:integer"/>
|
|
<xs:element name="n"><xs:complexType><xs:anyAttribute processContents="lax"/></xs:complexType></xs:element>
|
|
</xs:schema>
|
|
"""
|
|
|
|
@Test("wildcardAttributeTypeValid rejects a wildcard-matched attribute invalid against its type")
|
|
func test_wildcardAttributeTypeValid() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(
|
|
PureXML.Validation.XSD.wildcardAttributeTypeValid,
|
|
schema: laxWildcardIntAttributeSchema,
|
|
instance: "<n foo=\"x\"/>",
|
|
rootName: "n",
|
|
)
|
|
#expect(errors.count == 1)
|
|
#expect(errors.first?.reason.contains("attribute 'foo'") == true)
|
|
}
|
|
|
|
@Test("wildcardAttributeTypeValid accepts a valid wildcard-matched attribute")
|
|
func test_wildcardAttributeTypeValid_succeeds() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(
|
|
PureXML.Validation.XSD.wildcardAttributeTypeValid,
|
|
schema: laxWildcardIntAttributeSchema,
|
|
instance: "<n foo=\"5\"/>",
|
|
rootName: "n",
|
|
)
|
|
#expect(errors.isEmpty)
|
|
}
|
|
|
|
private let laxWildcardFixedAttributeSchema = """
|
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
<xs:attribute name="foo" type="xs:string" fixed="5"/>
|
|
<xs:element name="n"><xs:complexType><xs:anyAttribute processContents="lax"/></xs:complexType></xs:element>
|
|
</xs:schema>
|
|
"""
|
|
|
|
@Test("wildcardAttributeFixedMatches rejects a wildcard-matched attribute that differs from its fixed value")
|
|
func test_wildcardAttributeFixedMatches() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(
|
|
PureXML.Validation.XSD.wildcardAttributeFixedMatches,
|
|
schema: laxWildcardFixedAttributeSchema,
|
|
instance: "<n foo=\"6\"/>",
|
|
rootName: "n",
|
|
)
|
|
#expect(errors.count == 1)
|
|
#expect(errors.first?.reason.contains("is fixed and must be") == true)
|
|
}
|
|
|
|
@Test("wildcardAttributeFixedMatches accepts a wildcard-matched attribute equal to its fixed value")
|
|
func test_wildcardAttributeFixedMatches_succeeds() throws {
|
|
let errors = try ValidationRuleTestSupport.xsdErrors(
|
|
PureXML.Validation.XSD.wildcardAttributeFixedMatches,
|
|
schema: laxWildcardFixedAttributeSchema,
|
|
instance: "<n foo=\"5\"/>",
|
|
rootName: "n",
|
|
)
|
|
#expect(errors.isEmpty)
|
|
}
|
|
}
|