swift-sdk/Tests/MCPTests/MockResponses.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

216 lines
8.6 KiB
Swift

@preconcurrency import Foundation
import Testing
@testable import MCP
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
#if swift(>=6.1)
enum MockResponses {
typealias Route = @Sendable (URLRequest) async throws -> (HTTPURLResponse, Data)
static func ephemeralConfiguration() -> URLSessionConfiguration {
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [MockURLProtocol.self]
return config
}
static func mockError(_ message: String) -> NSError {
NSError(
domain: "MockURLProtocolError", code: 0,
userInfo: [NSLocalizedDescriptionKey: message]
)
}
static func jsonRPCResult(id: Int) -> Data {
#"{"jsonrpc":"2.0","result":{"ok":true},"id":\#(id)}"#.data(using: .utf8)!
}
// MARK: - Route Builders
static func jsonSuccess(body: Data) -> Route {
{ request in
let response = HTTPURLResponse(
url: request.url!, statusCode: 200, httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (response, body)
}
}
static func bearerChallenge(
statusCode: Int = 401,
resourceMetadataURL: URL? = nil,
scope: String? = nil,
error: String? = nil,
errorDescription: String? = nil
) -> Route {
{ request in
var params: [String] = []
if let url = resourceMetadataURL {
params.append("resource_metadata=\"\(url.absoluteString)\"")
}
if let scope { params.append("scope=\"\(scope)\"") }
if let error { params.append("error=\"\(error)\"") }
if let errorDescription { params.append("error_description=\"\(errorDescription)\"") }
let headerValue = "Bearer \(params.joined(separator: ", "))"
let response = HTTPURLResponse(
url: request.url!, statusCode: statusCode, httpVersion: "HTTP/1.1",
headerFields: ["WWW-Authenticate": headerValue])!
return (response, Data())
}
}
static func resourceMetadata(
authorizationServers: [String],
scopesSupported: [String]? = nil,
resource: String? = nil
) -> Route {
{ request in
var dict: [String: Any] = ["authorization_servers": authorizationServers]
if let scopes = scopesSupported { dict["scopes_supported"] = scopes }
if let resource { dict["resource"] = resource }
let data = try JSONSerialization.data(withJSONObject: dict)
let response = HTTPURLResponse(
url: request.url!, statusCode: 200, httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (response, data)
}
}
static func asMetadata(
issuer: String,
tokenEndpoint: String,
authorizationEndpoint: String? = nil,
registrationEndpoint: String? = nil,
codeChallengeMethodsSupported: [String]? = nil,
tokenEndpointAuthMethodsSupported: [String]? = nil,
clientIDMetadataDocumentSupported: Bool? = nil
) -> Route {
{ request in
var dict: [String: Any] = ["issuer": issuer, "token_endpoint": tokenEndpoint]
if let v = authorizationEndpoint { dict["authorization_endpoint"] = v }
if let v = registrationEndpoint { dict["registration_endpoint"] = v }
if let v = codeChallengeMethodsSupported {
dict["code_challenge_methods_supported"] = v
}
if let v = tokenEndpointAuthMethodsSupported {
dict["token_endpoint_auth_methods_supported"] = v
}
if let v = clientIDMetadataDocumentSupported {
dict["client_id_metadata_document_supported"] = v
}
let data = try JSONSerialization.data(withJSONObject: dict)
let response = HTTPURLResponse(
url: request.url!, statusCode: 200, httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (response, data)
}
}
static func tokenSuccess(
accessToken: String,
expiresIn: Int = 3600,
scope: String? = nil,
refreshToken: String? = nil
) -> Route {
{ request in
var dict: [String: Any] = [
"access_token": accessToken, "token_type": "Bearer", "expires_in": expiresIn,
]
if let scope { dict["scope"] = scope }
if let refreshToken { dict["refresh_token"] = refreshToken }
let data = try JSONSerialization.data(withJSONObject: dict)
let response = HTTPURLResponse(
url: request.url!, statusCode: 200, httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (response, data)
}
}
static func tokenResponse(
accessToken: String,
tokenType: String,
expiresIn: Int = 3600
) -> Route {
{ request in
let dict: [String: Any] = [
"access_token": accessToken, "token_type": tokenType, "expires_in": expiresIn,
]
let data = try JSONSerialization.data(withJSONObject: dict)
let response = HTTPURLResponse(
url: request.url!, statusCode: 200, httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (response, data)
}
}
static func tokenError(
statusCode: Int = 400,
error: String,
errorDescription: String? = nil,
extraFields: [String: String] = [:]
) -> Route {
{ request in
var dict: [String: Any] = ["error": error]
if let errorDescription { dict["error_description"] = errorDescription }
for (key, value) in extraFields { dict[key] = value }
let data = try JSONSerialization.data(withJSONObject: dict)
let response = HTTPURLResponse(
url: request.url!, statusCode: statusCode, httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (response, data)
}
}
static func httpError(statusCode: Int) -> Route {
{ request in
let response = HTTPURLResponse(
url: request.url!, statusCode: statusCode, httpVersion: "HTTP/1.1",
headerFields: nil)!
return (response, Data())
}
}
static func registrationSuccess(
clientID: String,
clientSecret: String? = nil
) -> Route {
{ request in
var dict: [String: Any] = ["client_id": clientID]
if let clientSecret { dict["client_secret"] = clientSecret }
let data = try JSONSerialization.data(withJSONObject: dict)
let response = HTTPURLResponse(
url: request.url!, statusCode: 201, httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (response, data)
}
}
static func redirect(to location: String, statusCode: Int = 302) -> Route {
{ request in
let response = HTTPURLResponse(
url: request.url!, statusCode: statusCode, httpVersion: "HTTP/1.1",
headerFields: ["Location": location])!
return (response, Data())
}
}
// MARK: - Routing Handler
static func routingHandler(
routes: [URL: Route]
) -> @Sendable (URLRequest) async throws -> (HTTPURLResponse, Data) {
{ request in
guard let url = request.url else { throw mockError("Missing request URL") }
guard let handler = routes[url] else {
throw mockError("Unexpected URL: \(url.absoluteString)")
}
return try await handler(request)
}
}
}
#endif // swift(>=6.1)