PureXML/Tests/SchemaIdentityDefaultValueTests.swift
Mihaela Mihaljevic ca56caf11c fix(schema): default/fixed value for identity-constraint fields; invalid-instances-accepted 50 to 45 (#147)
An identity-constraint (unique/key/keyref) field that selects an absent attribute
or an empty element whose declaration carries a default or fixed value now takes
that value as its identity-tuple component, so two members can collide on the
defaulted value (idG011/idG012 default, idF016/idF017 fixed) where the duplicate
was previously missed.

The default/fixed value constraints on identity-field targets are gathered at
compile time (identityFieldConstraints, mirroring identityFieldTypes) and threaded
through to the identity validator. The substitution is exact about absent versus
present-empty: an @attr field is defaulted only when the attribute is ABSENT (an
empty node-set), never when present with value ""; a "." element field only when
the selected element has no character content. Applying a default only adds an
identity value where the schema declares one, so it can create a genuine
duplicate-key detection but never reject a valid instance.

To keep within the file/type-size and one-type-per-file limits, FieldValue,
XPathQueryCache, and the schema-location hints were extracted to their own files
(behavior-preserving moves).

invalid-instances-accepted 50 to 45, no false positive (valid-instances-rejected
and valid-schemas-rejected held at 0), no other bucket moved. An adversarial
review probed present-empty attributes, element content winning over a default,
fixed values, keyref matching, multi-field keys, and merged-schema defaults, and
found no over-rejection.
2026-06-18 14:47:47 +02:00

88 lines
3.8 KiB
Swift

@testable import PureXML
import Testing
/// An identity-constraint field that selects an absent attribute or an empty
/// element whose declaration carries a `default`/`fixed` takes that value as its
/// identity component, so two members can collide on the defaulted value
/// (idG011/idG012, idF016/idF017). A present attribute (even value "") and a
/// non-empty element are instance-supplied and never defaulted.
@Suite("Identity-constraint fields take a declared default or fixed value")
struct SchemaIdentityDefaultValueTests {
private let xsd = "xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
private func errors(_ schema: String, _ instance: String) -> [PureXML.Validation.ValidationError] {
guard let document = try? PureXML.Schema.Document("<xs:schema \(xsd)>\(schema)</xs:schema>") else {
Issue.record("schema failed to compile")
return []
}
return (try? document.validate(instance)) ?? []
}
/// An `@val` attribute with `default="test"`: one member omits it (takes the
/// default), another carries `val="test"`, so the two keys collide.
private func attributeSchema(_ constraint: String) -> String {
"""
<xs:element name="root">
<xs:complexType><xs:sequence>
<xs:element ref="uid" maxOccurs="unbounded"/>
</xs:sequence></xs:complexType>
<xs:unique name="uuid">
<xs:selector xpath=".//uid"/><xs:field xpath="@val"/>
</xs:unique>
</xs:element>
<xs:element name="uid">
<xs:complexType>
<xs:attribute name="val" type="xs:string" \(constraint)/>
</xs:complexType>
</xs:element>
"""
}
@Test("Absent attribute with a default collides with the same explicit value")
func test_defaultAttributeDuplicate() {
let instance = "<root><uid val=\"test\"/><uid/></root>"
#expect(!errors(attributeSchema("default=\"test\""), instance).isEmpty)
}
@Test("Absent attribute with a fixed value collides with the same explicit value")
func test_fixedAttributeDuplicate() {
let instance = "<root><uid val=\"test\"/><uid/></root>"
#expect(!errors(attributeSchema("fixed=\"test\""), instance).isEmpty)
}
/// A `.` element-value field where one member is empty (takes the element's
/// `default`) and another carries the same text, so the two keys collide.
@Test("Empty element with a default collides with the same explicit value")
func test_defaultElementDuplicate() {
let schema = """
<xs:element name="root">
<xs:complexType><xs:sequence>
<xs:element ref="uid" maxOccurs="unbounded"/>
</xs:sequence></xs:complexType>
<xs:unique name="uuid">
<xs:selector xpath=".//uid"/><xs:field xpath="."/>
</xs:unique>
</xs:element>
<xs:element name="uid" type="xs:string" default="test"/>
"""
let instance = "<root><uid>test</uid><uid/></root>"
#expect(!errors(schema, instance).isEmpty)
}
/// FP-GUARD: an attribute PRESENT with value "" and NO default is its own
/// value, distinct from another member's "test", so the instance is valid.
@Test("A present empty attribute is its own value, not a default")
func test_presentEmptyAttributeNotDefaulted() {
let instance = "<root><uid val=\"\"/><uid val=\"test\"/></root>"
#expect(errors(attributeSchema(""), instance).isEmpty)
}
/// FP-GUARD: two genuinely distinct present values never collide, with or
/// without a declared default.
@Test("Two distinct present values are accepted")
func test_distinctPresentValuesAccepted() {
let instance = "<root><uid val=\"a\"/><uid val=\"b\"/></root>"
#expect(errors(attributeSchema("default=\"test\""), instance).isEmpty)
}
}