PureXML/Tests/ValidationConfigurationTests.swift
Mihaela Mihaljevic af5d7882b0 fix(schema): enforce model-group redefine restriction (cos-group-restrict)
A non-self-referencing model group redefined inside xs:redefine must be a valid
restriction of the group it redefines (cos-group-restrict / src-redefine 6.1.2),
so its content model may not widen the original's language. The check reuses the
particle-restriction oracle used for complex-type restriction, resolves the
original group by namespaced identity with a {}name chameleon fallback, and runs
post-compile where element type compatibility is available.

Now rejected: adding required content to an empty base (schZ006), dropping a
required all member (schL1), reordering an all (schL5/L6, the Recurse rule is
order-preserving even for all), growing to a superset (schL8), and the
multi-document/chained redefines schN4/5/6/7 and schL10. To stay
false-positive-free the check skips a redefinition carrying a self-reference and
declines to judge a base with a maxOccurs=0 pointless particle (the contested
case W3C accepts, mgO013); genuine restrictions (dropping an optional member, an
unchanged redefinition) are accepted.

XSTS invalid-schemas-accepted 64 -> 51, no false positive (valid-schemas-rejected
and valid-instances-rejected held at 0; an over-rejection of mgO013 surfaced by
the gate was fixed before commit, and an adversarial critic confirmed FP-safety),
no other bucket moved. Disclosed under-rejections: self-referencing redefinitions
(schN10) and maxOccurs=0-base redefines.
2026-06-18 18:24:58 +02:00

128 lines
6.2 KiB
Swift

@testable import PureXML
import Testing
/// Configuration-pin tests: every validator variant lists exactly its active rule
/// descriptions, so adding or rewording a rule fails a test before it is accidental.
@Suite("Validation configuration pins")
struct ValidationConfigurationTests {
@Test("The structural default validator lists its rules")
func test_structuralDefault() {
#expect(PureXML.Validation.Validator<Void>().validationDescriptions == [
"Element attribute names are unique",
])
}
@Test("The blank validator carries no rules")
func test_blank() {
#expect(PureXML.Validation.Validator<Void>.blank.validationDescriptions.isEmpty)
#expect(PureXML.Validation.Validator<PureXML.Validation.DTDSchema>.blank.validationDescriptions.isEmpty)
}
@Test("The lenient DTD validator lists its non-strict rules in order")
func test_dtdLenient() {
let validator = PureXML.Validation.DTD.validator(strict: false)
#expect(validator.nonReferenceValidationDescriptions == [
"Element content matches its DTD content model",
"Required DTD attributes are present",
"#FIXED DTD attributes hold their fixed value",
"Enumerated DTD attributes hold a listed value",
"Tokenized DTD attributes match their declared type",
"NOTATION DTD attributes name a declared notation",
"The DTD declarations satisfy their validity constraints",
"The root element matches the DOCTYPE name",
"Standalone documents do not depend on external attribute declarations",
"Standalone documents have no whitespace in externally-declared element content",
"Parse-time DTD advisories are reviewed",
])
#expect(validator.referenceValidationDescriptions == [
"DTD ID values are unique and IDREFs resolve",
])
}
@Test("The strict DTD validator appends undeclared element and attribute rules to the reference tier")
func test_dtdStrict() {
let validator = PureXML.Validation.DTD.validator(strict: true)
#expect(validator.referenceValidationDescriptions.suffix(2) == [
"Element is declared in the DTD",
"Every attribute is declared in the DTD",
])
}
@Test("The HTML validator splits local and document-scoped rules across tiers")
func test_html() {
let validator = PureXML.Validation.HTML.validator()
#expect(validator.nonReferenceValidationDescriptions == [
"Void HTML elements have no content",
"HTML elements appear inside their required parent",
])
#expect(validator.referenceValidationDescriptions == [
"HTML id attributes are unique",
])
}
@Test("The XSD instance validator splits content and identity rules across tiers")
func test_xsdInstance() {
let validator = PureXML.Validation.XSD.validator()
#expect(validator.nonReferenceValidationDescriptions == [
"The document element is valid against its XSD type",
])
#expect(validator.referenceValidationDescriptions == [
"XSD identity constraints hold",
])
}
@Test("The XSD schema-consistency validator lists named-type rules in tier order")
func test_xsdSchemaConsistency() {
let validator = PureXML.Validation.XSDSchema.validator()
#expect(validator.nonReferenceValidationDescriptions == [
"A type derives from its base only by methods the base permits",
])
#expect(validator.referenceValidationDescriptions == [
"A restriction's content model accepts a subset of its base's",
])
}
@Test("The schema compile validators pin their pre and post rule sets")
func test_schemaCompile() {
#expect(PureXML.Validation.SchemaCompile.preCompileValidator().validationDescriptions == [
"Schema component id attributes are valid NCNames and unique",
"Schema vocabulary elements follow the schema-for-schemas structure",
"Global schema component names are unique within their symbol spaces",
"Simple-type final controls are declared consistently",
"Schema content models are deterministic (UPA)",
"A resolved schemaLocation reference is a valid schema",
"A redefined type restricts or extends itself",
"A redefined group or attribute group has a well-formed self-reference",
"A redefined attribute group keeps the original's required attributes",
"Type derivation chains contain no cycles",
"Schema type references contain no cycles",
"xs:all group references appear only where permitted",
"Included schemas are chameleon or match the includer targetNamespace",
])
#expect(PureXML.Validation.SchemaCompile.postCompileValidator().validationDescriptions == [
"Attribute uses are unique and declare at most one ID attribute",
"An attribute's type is a simple type",
"ID-typed value constraints are valid",
"Element value constraints are valid against their declared types",
"Complex types extending xs:all groups satisfy XSD placement rules",
"Anonymous complex-type restrictions accept a subset of their base's",
"A content-model derivation is consistent with its base type",
"A redefined model group restricts the group it redefines",
"Attribute restrictions are faithful to their bases",
"Simple types do not derive from complex types",
"Simple-type varieties are declared consistently",
"Notation declarations are valid",
"Simple-type facets are valid for their base type",
"Every schema reference resolves to a declared component",
"Substitution-group members derive correctly from their head",
])
}
@Test("Every builtin description is unique and pinned")
func test_builtinRegistry() {
let descriptions = PureXML.Validation.BuiltinValidation.allDescriptions
#expect(descriptions.count == Set(descriptions).count)
#expect(descriptions.count == 24)
}
}