import Testing
@testable import PureXML
@Suite("XSD type resolution: unknown xsi:type and circular references")
struct XSDTypeResolutionTests {
private let schema = """
"""
@Test("An xsi:type naming an undeclared type is a located error, not a silent fallback")
func test_unknownXsiType() throws {
let xml = "x"
let document = try PureXML.Schema.Document(schema)
let tree = try document.validate(xml)
#expect(tree.contains { $0.reason.contains("unknown xsi:type 'Nope'") }, "\(tree.map(\.reason))")
// Streaming reports the same problem.
let streamed = try document.validate(streaming: xml)
#expect(streamed.contains { $0.reason.contains("unknown xsi:type 'Nope'") }, "\(streamed.map(\.reason))")
// A known xsi:type still validates cleanly.
let known = "x"
try #expect(document.validate(known).isEmpty)
}
@Test("A circular typeReference chain is a located error, not silent truncation")
func test_circularReference() {
let validator = PureXML.Schema.ComplexValidator(
types: ["A": .typeReference("B"), "B": .typeReference("A")],
)
let element = PureXML.Model.Element("e")
let errors = validator.validate(element, as: .typeReference("A"), at: [.element("e")])
#expect(errors.contains { $0.reason.contains("circular type reference") }, "\(errors.map(\.reason))")
// The shallow (streaming) check reports it too, and terminates (this
// previously recursed without bound on a cyclic table).
let shallow = validator.validateShallow(element, as: .typeReference("A"), at: [.element("e")])
#expect(shallow.contains { $0.reason.contains("circular type reference") }, "\(shallow.map(\.reason))")
}
@Test("The shared resolver distinguishes resolved, unknown, and circular")
func test_resolver() {
let types: [String: PureXML.Schema.ElementType] = [
"Alias": .typeReference("Real"),
"Real": .complex(PureXML.Schema.ComplexType(content: .empty)),
"Loop": .typeReference("Loop"),
]
if case .resolved = PureXML.Schema.ComplexValidator.resolveReference(.typeReference("Alias"), in: types) {} else {
Issue.record("alias chain should resolve")
}
if case let .unknown(name) = PureXML.Schema.ComplexValidator.resolveReference(.typeReference("Missing"), in: types) {
#expect(name == "Missing")
} else {
Issue.record("missing name should be unknown")
}
if case let .circular(name) = PureXML.Schema.ComplexValidator.resolveReference(.typeReference("Loop"), in: types) {
#expect(name == "Loop")
} else {
Issue.record("self-reference should be circular")
}
}
@Test("Conformance: the instance-override corpus passes through the harness")
func test_conformanceCases() throws {
let document = try PureXML.Schema.Document(schema)
let bad = "x"
let good = "x"
let cases = try [
PureXML.Validation.ConformanceCase(
name: "unknown-xsi-type-rejected",
actual: document.validate(bad).isEmpty ? "valid" : "invalid",
expected: "invalid",
),
PureXML.Validation.ConformanceCase(
name: "plain-instance-accepted",
actual: document.validate(good).isEmpty ? "valid" : "invalid",
expected: "valid",
),
]
let failures = PureXML.Validation.Conformance.failures(in: cases)
#expect(failures.isEmpty, "\(failures.map(\.reason))")
}
}