@testable import PureXML import Testing @Suite("XSD identity constraints") struct XSDIdentityTests { private func validate(_ xsd: String, _ xml: String) throws -> [PureXML.Validation.ValidationError] { try PureXML.Schema.Document(xsd).validate(xml) } private let head = "" @Test("xs:unique rejects a duplicated field value") func test_unique() throws { let xsd = """ \(head) """ #expect(try validate(xsd, "").isEmpty) #expect(try !validate(xsd, "").isEmpty) // unique tolerates a missing field #expect(try validate(xsd, "").isEmpty) } @Test("xs:key requires every field to be present and distinct") func test_key() throws { let xsd = """ \(head) """ #expect(try validate(xsd, "").isEmpty) #expect(try !validate(xsd, "").isEmpty) // a key field may not be absent #expect(try !validate(xsd, "").isEmpty) } @Test("xs:keyref requires a matching key value") func test_keyref() throws { let xsd = """ \(head) """ let valid = "" let dangling = "" #expect(try validate(xsd, valid).isEmpty) // The dangling keyref is located at the offending field @product. let failure = try #require(validate(xsd, dangling).first) #expect(failure.codingPath.map(\.stringValue) == ["orders", "line", "@product"]) #expect(String(describing: failure).hasSuffix("at path: orders/line/@product")) } @Test("Identity-constraint errors are located at the offending field") func test_identityErrorsCarryPath() throws { let xsd = """ \(head) """ // The error locates the offending field (the duplicate item[2]'s @id), // not just the element that declares the key. let errors = try validate(xsd, "") let failure = try #require(errors.first) #expect(failure.codingPath.map(\.stringValue) == ["list", "item", "@id"]) #expect(String(describing: failure).hasSuffix("at path: list/item[2]/@id")) } @Test("A nested selector locates the offender at full depth with an intermediate index") func test_identityErrorDeepPath() throws { let xsd = """ \(head) """ // The duplicate id "1" appears in the second group, so the offender is // list/group[2]/item: a grandchild, with the index on the intermediate group. let xml = "" let failure = try #require(validate(xsd, xml).first) #expect(failure.codingPath.map(\.stringValue) == ["list", "group", "item", "@id"]) #expect(String(describing: failure).hasSuffix("at path: list/group[2]/item/@id")) } @Test("A keyref resolves against a key declared on an ancestor (cross-scope)") func test_keyrefCrossScope() throws { // pk is declared on ; the keyref is declared on the deeper // , so resolution must find the ancestor's key in scope. let xsd = """ \(head) """ let valid = "" let dangling = "" #expect(try validate(xsd, valid).isEmpty) #expect(try validate(xsd, dangling).contains { $0.reason.contains("no matching key 'pk'") }) } @Test("A malformed field XPath is a located error, not a silently disabled constraint") func test_malformedFieldReported() throws { let xsd = """ \(head) """ let errors = try validate(xsd, "") #expect(errors.contains { $0.reason.contains("invalid field XPath '@id['") }, "\(errors.map(\.reason))") } @Test("A malformed selector XPath is a located error") func test_malformedSelectorReported() throws { let xsd = """ \(head) """ let errors = try validate(xsd, "") #expect(errors.contains { $0.reason.contains("invalid selector XPath 'item['") }, "\(errors.map(\.reason))") } @Test("A child-element field locates the error at that child") func test_childElementField() throws { let xsd = """ \(head) """ let xml = "xx" let failure = try #require(validate(xsd, xml).first) #expect(String(describing: failure).hasSuffix("at path: list/item[2]/code")) } }