import Foundation import Testing @testable import LegadoCore struct ApplicationSettingsTests { @Test func validatesAndPersistsWithoutOverwritingOnFailure() throws { let suite = "legado.settings.tests.\(UUID().uuidString)" let defaults = UserDefaults(suiteName: suite)! defer { defaults.removePersistentDomain(forName: suite) } let store = ApplicationSettingsStore(defaults: defaults) #expect(store.settings == ApplicationSettings()) var value = ApplicationSettings() value.userAgent = " Test Browser " value.timeoutSeconds = 60 value.concurrentSources = 3 value.hosts = "127.0.0.1 books.example.com" try store.save(value) #expect(store.settings.userAgent == "Test Browser") #expect(store.settings.timeoutSeconds == 60) value.userAgent = "invalid\r\nHeader: injected" #expect(throws: SettingsError.self) { try store.save(value) } #expect(store.settings.userAgent == "Test Browser") } @Test func parsesIPv4IPv6CommentsAndMultipleDomains() throws { let hosts = try ApplicationSettings.parseHosts(""" # local mirror 127.0.0.1 Books.Example.com mirror.example.com # comment ::1 ipv6.example.com 192.168.0.2 books.example.com """) #expect(hosts["books.example.com"] == "192.168.0.2") #expect(hosts["mirror.example.com"] == "127.0.0.1") #expect(hosts["ipv6.example.com"] == "::1") for invalid in ["no-ip example.com", "256.0.0.1 example.com", "127.0.0.1 https://example.com", "127.0.0.1 bad..example", "127.0.0.1 -bad.com"] { #expect(throws: SettingsError.self) { _ = try ApplicationSettings.parseHosts(invalid) } } } @Test func proxyAndRequestPrecedence() throws { var settings = ApplicationSettings() settings.proxy = "socks5://127.0.0.1:1089" settings.userAgent = "Global UA" settings.timeoutSeconds = 45 let config = URLSessionConfiguration.ephemeral try settings.configure(config) #expect(config.connectionProxyDictionary?["SOCKSPort"] as? Int == 1089) try settings.configure(config, proxyOverride: "http://localhost:8000") #expect(config.connectionProxyDictionary?["HTTPPort"] as? Int == 8000) var request = URLRequest(url: URL(string: "https://example.com")!, timeoutInterval: 5) #expect(settings.configure(request).value(forHTTPHeaderField: "User-Agent") == "Global UA") request.setValue("Source UA", forHTTPHeaderField: "user-agent") #expect(settings.configure(request).value(forHTTPHeaderField: "User-Agent") == "Source UA") #expect(settings.configure(request).timeoutInterval == 45) request.setValue("", forHTTPHeaderField: "User-Agent") #expect(settings.configure(request).value(forHTTPHeaderField: "User-Agent") == "") for invalid in ["ftp://localhost:3", "http://localhost:0", "http://localhost:65536", "http://localhost/path", "http://user:pass@localhost:3"] { #expect(throws: SettingsError.self) { _ = try ApplicationSettings.proxyDictionary(invalid) } } } @Test func existingTransportUsesSavedSettingsOnNextRequest() async throws { let suite = "legado.transport.tests.\(UUID().uuidString)" let defaults = UserDefaults(suiteName: suite)! defer { defaults.removePersistentDomain(forName: suite) } let store = ApplicationSettingsStore(defaults: defaults) let config = URLSessionConfiguration.ephemeral config.protocolClasses = [SettingsEchoURLProtocol.self] let transport = URLSessionHTTPTransport(configuration: config, settingsStore: store) let request = URLRequest(url: URL(string: "https://settings.example/test")!) var settings = ApplicationSettings() settings.userAgent = "First UA" try store.save(settings) #expect(try await transport.data(for: request).decodedText == "First UA") settings.userAgent = "Second UA" settings.timeoutSeconds = 15 try store.save(settings) #expect(try await transport.data(for: request).decodedText == "Second UA") var explicit = request explicit.setValue("Source UA", forHTTPHeaderField: "User-Agent") #expect(try await transport.data(for: explicit).decodedText == "Source UA") } @Test func suppressedSourceUAStaysSuppressedInURLRequest() throws { let prepared = try LegadoURLBuilder().prepare(#"https://example.com,{headers:{'User-Agent':'null'}}"#) #expect(prepared.headers["User-Agent"] == nil) #expect(prepared.urlRequest().value(forHTTPHeaderField: "User-Agent") == nil) var settings = ApplicationSettings() settings.userAgent = "Global UA" #expect(settings.configure(prepared.urlRequest()).value(forHTTPHeaderField: "User-Agent") == nil) } } private final class SettingsEchoURLProtocol: URLProtocol, @unchecked Sendable { override class func canInit(with request: URLRequest) -> Bool { true } override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } override func startLoading() { let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) client?.urlProtocol(self, didLoad: Data((request.value(forHTTPHeaderField: "User-Agent") ?? "").utf8)) client?.urlProtocolDidFinishLoading(self) } override func stopLoading() {} }