swift-sdk/Tests/MCPTests/OAuthComponentTestHelpers.swift
Maksym Mova 6132fd4b5b
V0.12.x (#215)
* fix #203 NetworkTransport race, with main-isolated variables (#206)

Co-authored-by: wti <wti@users.noreply.github.com>

* feat/auth: Authentication implementation (#205)

* feat: added authentication

* feat(auth): improvement to error handling

* feat(auth): updated errors and tests in OAuthClientRegistrar

* feat(auth): updated ci conformance package version

* feat(auth): bumped node version

* feat(auth): updated ci conformance step

* feat(auth): improvement to Linux support

* chore: added docc plugin

* chore: added spi yml

* chore: added documentation step to ci

* chore: increased timeout for test from 5 to 10m

---------

Co-authored-by: Paal Øye-Strømme <paal.o.eye@gmail.com>
Co-authored-by: wti <wti@users.noreply.github.com>
Co-authored-by: yehorsobko <yehorsobko@macpaw.com>
2026-03-24 11:05:22 +02:00

90 lines
3.4 KiB
Swift

@preconcurrency import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
/// Key-isolated mock URLProtocol.
///
/// Each test creates a session with a unique key and registers its handler under that key.
/// Multiple suites run concurrently without interference because each test's requests carry
/// a different key, so they never read the wrong handler.
#if swift(>=6.1)
actor IsolatedMockStorage {
static let shared = IsolatedMockStorage()
private var handlers:
[String: @Sendable (URLRequest) async throws -> (HTTPURLResponse, Data)] = [:]
func set(
key: String,
handler: @escaping @Sendable (URLRequest) async throws -> (HTTPURLResponse, Data)
) {
handlers[key] = handler
}
func execute(key: String, request: URLRequest) async throws -> (HTTPURLResponse, Data) {
guard let handler = handlers[key] else {
throw NSError(
domain: "IsolatedMockError", code: 0,
userInfo: [NSLocalizedDescriptionKey: "No handler for key '\(key)'"])
}
return try await handler(request)
}
}
final class IsolatedMockURLProtocol: URLProtocol, @unchecked Sendable {
static let sessionKeyHeader = "X-Mock-Key"
static func makeSession(key: String) -> URLSession {
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [IsolatedMockURLProtocol.self]
config.httpAdditionalHeaders = [sessionKeyHeader: key]
return URLSession(configuration: config)
}
static func setHandler(
key: String,
_ handler: @escaping @Sendable (URLRequest) async throws -> (HTTPURLResponse, Data)
) async {
await IsolatedMockStorage.shared.set(key: key, handler: handler)
}
func executeHandler(for request: URLRequest) async throws -> (HTTPURLResponse, Data) {
let key = request.value(forHTTPHeaderField: IsolatedMockURLProtocol.sessionKeyHeader)
?? "unknown"
return try await IsolatedMockStorage.shared.execute(key: key, request: request)
}
override class func canInit(with request: URLRequest) -> Bool { true }
override class func canonicalRequest(for request: URLRequest) -> URLRequest { request }
override func startLoading() {
Task {
do {
let (response, data) = try await self.executeHandler(for: request)
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
client?.urlProtocol(self, didLoad: data)
client?.urlProtocolDidFinishLoading(self)
} catch {
client?.urlProtocol(self, didFailWithError: error)
}
}
}
override func stopLoading() {}
}
// MARK: - Per-test session factory
/// Creates an isolated URLSession + unique key for one test.
///
/// Usage:
/// ```swift
/// let (session, key) = makeIsolatedSession()
/// await IsolatedMockURLProtocol.setHandler(key: key) { _ in (response, data) }
/// ```
func makeIsolatedSession() -> (session: URLSession, key: String) {
let key = UUID().uuidString
return (IsolatedMockURLProtocol.makeSession(key: key), key)
}
#endif