import Testing @testable import PureXML @Suite("XSD include composition (#161)") struct SchemaIncludeTests { @Test("chameleon include: no-namespace library merges into a target-namespace main schema") func test_chameleonIncludeWithTargetNamespace() throws { let library = """ """ let main = """ """ let doc = try PureXML.Schema.Document(main, schemaLoader: { $0 == "lib.xsd" ? library : nil }) #expect(try doc.validate("ABC").isEmpty) #expect(try !doc.validate("abc").isEmpty) } /// src-redefine.5: a type inside xs:redefine must restrict/extend the type it /// redefines, which lives in the redefining schema's own target namespace. A base /// bound to a foreign namespace with the same local name (an imported type) is not /// self and is rejected; an unprefixed/own-namespace self-reference is valid. @Test("a redefined type must derive from itself, not a same-named foreign type") func test_redefineBaseMustBeSelfNamespace() { let redefined = """ """ let imported = """ """ let loader: (String) -> String? = { $0 == "a.xsd" ? redefined : ($0 == "foo.xsd" ? imported : nil) } // Redefine of T whose extension base is foo:T (a foreign same-local-name type). let foreign = """ """ #expect((try? PureXML.Schema.Document(foreign, schemaLoader: loader)) == nil) // A proper self-redefine (base resolves to the redefining target namespace). let selfRedefine = """ """ #expect((try? PureXML.Schema.Document(selfRedefine, schemaLoader: loader)) != nil) } /// src-redefine.6.1.2: a group inside xs:redefine may reference itself, but that /// self-reference must occur exactly once (minOccurs = maxOccurs = 1). A /// self-reference with minOccurs="0" or maxOccurs="unbounded" is invalid; a unit /// self-reference (or absent occurrence, defaulting to 1) is valid. @Test("a redefined group's self-reference must occur exactly once") func test_redefineGroupSelfReferenceOccurrence() { let original = """ """ let loader: (String) -> String? = { $0 == "a.xsd" ? original : nil } func redefine(_ selfRef: String) -> String { """ \(selfRef) """ } // A non-unit self-reference is invalid. #expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) == nil) #expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) == nil) // A unit self-reference is valid. #expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) != nil) } /// A redefined schema's targetNamespace must match the redefining schema's (or be /// chameleon, no targetNamespace), exactly as for include; a redefine of a /// different-namespace schema is invalid. @Test("a redefine's schema must match the redefiner's targetNamespace") func test_redefineTargetNamespaceMatch() { let main = """ """ func redefined(_ namespace: String) -> String { "" } // A different targetNamespace is invalid; the same one is valid. #expect((try? PureXML.Schema.Document(main, schemaLoader: { $0 == "r.xsd" ? redefined(" targetNamespace=\"urn:other\"") : nil })) == nil) #expect((try? PureXML.Schema.Document(main, schemaLoader: { $0 == "r.xsd" ? redefined(" targetNamespace=\"urn:a\"") : nil })) != nil) } /// src-redefine.7.2.1: a redefined attributeGroup may contain at most one /// self-reference; two references to the group being redefined are invalid. @Test("a redefined attributeGroup may have at most one self-reference") func test_redefineAttributeGroupSelfReferenceCount() { let original = """ """ let loader: (String) -> String? = { $0 == "a.xsd" ? original : nil } func redefine(_ refs: String) -> String { """ \(refs) """ } // Two self-references are invalid; one is valid. #expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) == nil) #expect((try? PureXML.Schema.Document(redefine(#""#), schemaLoader: loader)) != nil) } /// src-resolve: when an include/import/redefine schemaLocation IS resolved (the /// loader returns content), that content must be a well-formed schema. Not-well- /// formed XML, or well-formed XML that is not an xs:schema, is rejected. A /// location the loader does not resolve (returns nil) is not an error. @Test("a resolved schemaLocation that is not a valid schema is rejected") func test_resolvedSchemaLocationMustBeSchema() { let main = """ """ // Resolved to not-well-formed XML. #expect(throws: Error.self) { _ = try PureXML.Schema.Document(main, schemaLoader: { $0 == "bad.xsd" ? "" : nil }) } // An UNRESOLVED location (loader returns nil) is not an error. #expect((try? PureXML.Schema.Document(main, schemaLoader: { _ in nil })) != nil) } /// src-import.3.1/3.2: an import's namespace attribute must equal the imported /// schema's targetNamespace (both equal, or both absent). Checked only when the /// schemaLocation resolves to a loaded schema. @Test("an import's namespace must match the imported targetNamespace") func test_importNamespaceMustMatchImportedTarget() { let mainNS = """ """ // Imported doc's targetNamespace does NOT match the declared import namespace. let mismatched = "" #expect((try? PureXML.Schema.Document(mainNS, schemaLoader: { $0 == "b.xsd" ? mismatched : nil })) == nil) // Imported doc has NO targetNamespace but the import declares one. let noTarget = "" #expect((try? PureXML.Schema.Document(mainNS, schemaLoader: { $0 == "b.xsd" ? noTarget : nil })) == nil) // Matching targetNamespace compiles. let matched = "" #expect((try? PureXML.Schema.Document(mainNS, schemaLoader: { $0 == "b.xsd" ? matched : nil })) != nil) // An unresolved import (loader returns nil) is not an error. #expect((try? PureXML.Schema.Document(mainNS, schemaLoader: { _ in nil })) != nil) } @Test("xs:include with a mismatched targetNamespace is rejected") func test_invalidIncludeDifferentTargetNamespace() { let included = """ """ let main = """ """ #expect(throws: Error.self) { _ = try PureXML.Schema.Document(main, schemaLoader: { $0 == "other.xsd" ? included : nil }) } } @Test("xs:include with matching targetNamespace is accepted") func test_includeMatchingTargetNamespace() throws { let included = """ """ let main = """ """ let doc = try PureXML.Schema.Document(main, schemaLoader: { $0 == "lib.xsd" ? included : nil }) #expect(try doc.validate("ABC").isEmpty) } @Test("chameleon include: attribute refs inside the included schema resolve after merge") func test_chameleonIncludeAttributeGroupRef() throws { let library = """ """ let main = """ """ let doc = try PureXML.Schema.Document(main, schemaLoader: { $0 == "lib.xsd" ? library : nil }) #expect(try doc.validate("").isEmpty) } @Test("undeclared refs inside an included chameleon schema are rejected at compile time") func test_invalidReferenceInsideIncludedSchema() { let library = """ """ let main = """ """ #expect(throws: Error.self) { _ = try PureXML.Schema.Document(main, schemaLoader: { $0 == "lib.xsd" ? library : nil }) } } }