PureXML/Tests/SchemaRedefineAttributeGroupTests.swift
Mihaela Mihaljevic 1d17d2089f fix(schema): reject relaxing a fixed value in an attribute-group redefine
A redefinition of an attribute group restricts the original (src-redefine /
cos-ct-restricts), so it may not relax a fixed value the original sets. schM10
(changing a fixed attribute to a default one) is now rejected. The check fires
only when the redefinition declares the matching attribute as non-prohibited
with a differing fixed value; keeping the same fixed value, prohibiting it, or
omitting it is left alone, and it rides the existing namespaced base lookup, so
no valid redefinition is rejected.

XSTS invalid-schemas-accepted 65 -> 64, no false positive (valid-schemas-rejected
and valid-instances-rejected held at 0), no other bucket moved.
2026-06-18 17:57:43 +02:00

65 lines
2.9 KiB
Swift

@testable import PureXML
import Testing
/// A redefinition of an attribute group restricts the original (src-redefine), so
/// it may neither eliminate a required attribute nor re-introduce a prohibited one
/// as usable. Keeping a required attribute, and re-declaring a prohibited attribute
/// as prohibited, are both valid (W3C attgZ002/attgZ003, schT3).
@Suite("redefined attribute group restriction")
struct SchemaRedefineAttributeGroupTests {
private func redefine(group body: String, base: String) throws -> PureXML.Schema.Document {
let main = "<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'>"
+ "<xs:redefine schemaLocation='b.xsd'><xs:attributeGroup name='g'>\(body)</xs:attributeGroup></xs:redefine>"
+ "<xs:element name='e'/></xs:schema>"
let baseDoc = "<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'>"
+ "<xs:attributeGroup name='g'>\(base)</xs:attributeGroup></xs:schema>"
return try PureXML.Schema.Document(main, schemaLoader: { _ in baseDoc })
}
@Test("eliminating a required attribute is rejected")
func test_dropRequiredRejected() throws {
#expect(throws: (any Error).self) {
try redefine(
group: "<xs:attribute name='b' type='xs:string'/>",
base: "<xs:attribute name='a' use='required' type='xs:string'/><xs:attribute name='b' type='xs:string'/>",
)
}
}
@Test("re-introducing a prohibited attribute as usable is rejected")
func test_reintroduceProhibitedRejected() throws {
#expect(throws: (any Error).self) {
try redefine(
group: "<xs:attribute name='a' type='xs:string'/>",
base: "<xs:attribute name='a' use='prohibited' type='xs:string'/>",
)
}
}
@Test("keeping a required attribute and re-prohibiting a prohibited one is valid")
func test_validRedefinitionAccepted() throws {
_ = try redefine(
group: "<xs:attribute name='a' use='required' type='xs:string'/><xs:attribute name='p' use='prohibited' type='xs:string'/>",
base: "<xs:attribute name='a' use='required' type='xs:string'/><xs:attribute name='p' use='prohibited' type='xs:string'/><xs:attribute name='o' type='xs:string'/>",
)
}
@Test("relaxing a fixed value in the redefinition is rejected")
func test_relaxFixedRejected() throws {
#expect(throws: (any Error).self) {
try redefine(
group: "<xs:attribute name='a' type='xs:string' default='abc'/>",
base: "<xs:attribute name='a' type='xs:string' fixed='abc'/>",
)
}
}
@Test("keeping the same fixed value in the redefinition is valid")
func test_keepFixedAccepted() throws {
_ = try redefine(
group: "<xs:attribute name='a' type='xs:string' fixed='abc'/>",
base: "<xs:attribute name='a' type='xs:string' fixed='abc'/>",
)
}
}