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.
72 lines
3.0 KiB
Swift
72 lines
3.0 KiB
Swift
import Testing
|
|
@testable import PureXML
|
|
|
|
enum ValidationRuleTestSupport {
|
|
typealias DTDSchema = PureXML.Validation.DTDSchema
|
|
typealias XSDContext = PureXML.Validation.XSDContext
|
|
|
|
/// `xmlns:xsi` namespace declaration, spliced into instance fixtures that need
|
|
/// `xsi:nil`/`xsi:type`.
|
|
static let xsiNS = "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
|
|
|
|
/// Compiles `schema`, parses `instance`, and runs `rule` alone (isolated via
|
|
/// `.blank.validating(rule)`) against the root element declared `rootName`.
|
|
/// Shared by every `ValidationRuleXSD*Tests` suite.
|
|
static func xsdErrors(
|
|
_ rule: PureXML.Validation.Validation<PureXML.Model.Node, XSDContext>,
|
|
schema: String,
|
|
instance: String,
|
|
rootName: String,
|
|
) throws -> [PureXML.Validation.ValidationError] {
|
|
let compiled = try PureXML.Schema.XSDParser.parse(schema)
|
|
let node = try PureXML.parse(instance)
|
|
guard case let .document(children) = node, let root = children.compactMap(\.element).first else {
|
|
Issue.record("no root element")
|
|
return []
|
|
}
|
|
let context = XSDContext(
|
|
types: compiled.types,
|
|
constraints: compiled.constraints,
|
|
rootDeclaration: compiled.elements[rootName],
|
|
nillableElements: compiled.nillableElements,
|
|
elementConstraints: compiled.elementConstraints,
|
|
abstractTypes: compiled.nsAbstractTypes,
|
|
typeBlock: compiled.nsTypeBlock,
|
|
elementBlock: compiled.nsElementBlock,
|
|
typeDerivation: compiled.nsTypeDerivation,
|
|
unionMembers: compiled.nsUnionMembers,
|
|
globalAttributes: compiled.globalAttributes,
|
|
identityFieldTypes: compiled.identityFieldTypes,
|
|
identityFieldConstraints: compiled.identityFieldConstraints,
|
|
identityNonSimpleFields: compiled.identityNonSimpleFields,
|
|
)
|
|
return PureXML.Validation.Validator<XSDContext>.blank
|
|
.validating(rule)
|
|
.errors(for: .element(root), in: context)
|
|
}
|
|
|
|
static func dtd(_ xml: String, resolver: PureXML.Parsing.EntityResolver = .refusing) throws -> (PureXML.Model.Node, DTDSchema) {
|
|
let parsed = try PureXML.Parsing.Parser().parseWithDocumentType(xml, limits: .init(allowDoctype: true), resolver: resolver)
|
|
let standalone = parsed.declaration?.standalone == true
|
|
return (parsed.node, DTDSchema(parsed.documentType, standalone: standalone))
|
|
}
|
|
|
|
static let standaloneExternalDTD = """
|
|
<!ELEMENT root (child)*>
|
|
<!ELEMENT child (#PCDATA)>
|
|
<!ATTLIST root token (a|b|c) "a" id ID #IMPLIED>
|
|
"""
|
|
|
|
static func standaloneResolver() -> PureXML.Parsing.EntityResolver {
|
|
let dtd = standaloneExternalDTD
|
|
return PureXML.Parsing.EntityResolver(
|
|
resolveEntity: { _, _ in nil },
|
|
resolveExternalSubset: { _ in dtd },
|
|
)
|
|
}
|
|
|
|
static func path(_ error: PureXML.Validation.ValidationError) -> [String] {
|
|
error.codingPath.map(\.stringValue)
|
|
}
|
|
}
|