// ===----------------------------------------------------------------------=== // // This source file is part of the Marvel Service open source project // // Copyright (c) 2025 Röck+Cöde VoF. and the Marvel Service project authors // Licensed under Apache license v2.0 // // See LICENSE for license information // See CONTRIBUTORS for the list of Marvel Service project authors // // SPDX-License-Identifier: Apache-2.0 // // ===----------------------------------------------------------------------=== import Testing import struct Foundation.URL import struct Foundation.URLComponents import struct HTTPTypes.HTTPRequest import struct HTTPTypes.HTTPResponse @testable import MarvelService @Suite("Auth Middleware", .tags(.middleware)) struct AuthMiddlewareTest { // MARK: Functions #if swift(>=6.2) @Test(arguments: Input.pathRequests) func `intercept with API key`( path: String? ) async throws { try await assertIntercept( path: path, with: .Key.api ) } @Test(arguments: Input.pathRequests) func `intercept with private and public keys`( path: String? ) async throws { try await assertIntercept( path: path, with: .Key.public, and: .Key.private ) } #else @Test("intercept with API key", arguments: Input.pathRequests) func interceptWithAPIKey( path: String? ) async throws { try await assertIntercept( path: path, with: .Key.api ) } @Test("intercept with private and public keys", arguments: Input.pathRequests) func interceptWithKeys( path: String? ) async throws { try await assertIntercept( path: path, with: .Key.public, and: .Key.private ) } #endif } // MARK: - Assertions private extension AuthMiddlewareTest { // MARK: Functions /// Asserts the interception of a request to add authentication parameters in it. /// - Parameter path: A URI path for a request. /// - Parameter apiKey: A Marvel (public) API key. /// - Parameter privateKey: <#publicKey description#> /// - Throws: An error in case func assertIntercept( path: String?, with apiKey: String, and privateKey: String? = nil ) async throws { // GIVEN let baseURL: URL = .baseURL let request: HTTPRequest = .init(path: path) let middleware: AuthMiddleware = if let privateKey { .init( privateKey: privateKey, publicKey: apiKey ) } else { .init(apiKey: apiKey) } // WHEN _ = try await confirmation { confirmation in try await middleware.intercept( request, body: nil, baseURL: baseURL, operationID: .operationId ) { request, _, _ in // THEN if path != nil { let pathRequest = try #require(request.path) let urlComponents = try #require(URLComponents(string: pathRequest)) let queryItems = try #require(urlComponents.queryItems) #expect(queryItems.contains(where: { $0.name == .Parameter.apiKey })) if privateKey == nil { #expect(queryItems.count >= 1) } else { #expect(queryItems.contains(where: { $0.name == .Parameter.hash })) #expect(queryItems.contains(where: { $0.name == .Parameter.timestamp })) #expect(queryItems.count >= 3) } } else { #expect(request.path == nil) } confirmation() return (.init(status: .ok) , nil) } } } } // MARK: - Helpers private extension HTTPRequest { // MARK: Initializers /// Initializes a HTTP request with a method and a path. /// - Parameters: /// - method: The request method. /// - path: The value of the “:path” pseudo header field. init( method: HTTPRequest.Method = .get, path: String? ) { self.init( method: method, scheme: nil, authority: nil, path: path ) } } // MARK: - Constants private extension Input { /// A list of URI path to a resource for a request. static let pathRequests: [String?] = [ nil, "/path/to/resource", "/path/to/resource?boolean", "/path/to/resource?query=value", "/path/to/resource?query=value&anotherQuery=anotherValue" ] } private extension String { /// An operation ID sample. static let operationId = "SomeOperationId" } private extension URL { /// A base URL sample. static let baseURL = URL(string: "https://sample.domain.com")! }