import Testing @testable import PureXML @Suite("XML Catalog") struct CatalogTests { private let catalog = """ """ private func resolver() throws -> PureXML.Catalog.Resolver { try PureXML.Catalog.Resolver(catalog) } @Test("System and public identifiers resolve to their URIs") func test_exactEntries() throws { let resolver = try resolver() #expect(resolver.resolveSystem("http://example.com/note.dtd") == "local/note.dtd") #expect(resolver.resolvePublic("-//W3C//DTD XHTML 1.0//EN") == "local/xhtml1.dtd") #expect(resolver.resolveURI("urn:logo") == "local/logo.png") } @Test("rewriteSystem applies the longest matching prefix") func test_rewriteSystem() throws { let resolver = try resolver() #expect(resolver.resolveSystem("http://example.com/dtd/foo.dtd") == "local/dtd/foo.dtd") } @Test("rewriteURI inside a group is honored") func test_rewriteURIInGroup() throws { #expect(try resolver().resolveURI("http://example.com/img/a.png") == "local/img/a.png") } @Test("An unmapped identifier resolves to nil") func test_unmapped() throws { #expect(try resolver().resolveSystem("http://other/x.dtd") == nil) } @Test("The system identifier takes precedence over the public one") func test_systemPrecedence() throws { let resolver = try resolver() let uri = resolver.resolveExternalIdentifier( publicID: "-//W3C//DTD XHTML 1.0//EN", systemID: "http://example.com/note.dtd", ) #expect(uri == "local/note.dtd") } @Test("The catalog drives an entity resolver through an injected loader") func test_entityResolverIntegration() throws { let resolver = try resolver().entityResolver { uri in uri == "local/note.dtd" ? "" : nil } let xml = "&greeting;" let node = try PureXML.parse(xml, limits: .init(allowDoctype: true), resolver: resolver) #expect(stringValue(node) == "hi") } @Test("Without a loader the catalog resolver loads nothing, keeping XXE closed") func test_noLoaderRefuses() throws { let resolver = try resolver().entityResolver { _ in nil } let xml = "&greeting;" // Nothing is fetched; the reference the unread DTD might have // declared stays literal (production 68 makes it a validity issue, // not a well-formedness error). let node = try PureXML.parse(xml, limits: .init(allowDoctype: true), resolver: resolver) #expect(stringValue(node) == "&greeting;") } private func stringValue(_ node: PureXML.Model.Node) -> String { switch node { case let .document(children): children.map(stringValue).joined() case let .element(element): element.children.map(stringValue).joined() case let .text(value), let .cdata(value): value default: "" } } }