Implemented the "metadata()" helper function for the LoggerMetadata+Helpers extension in the library target.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
// ===----------------------------------------------------------------------===
|
||||
//
|
||||
// This source file is part of the Hummingbird DocC Middleware open source project
|
||||
//
|
||||
// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors
|
||||
// Licensed under the EUPL 1.2 or later.
|
||||
//
|
||||
// See LICENSE for license information
|
||||
// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
import protocol Hummingbird.RequestContext
|
||||
|
||||
import struct Hummingbird.HTTPResponse
|
||||
import struct Hummingbird.Request
|
||||
import struct Logging.Logger
|
||||
|
||||
extension Logger.Metadata {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
/// Generates a dictionary to use as metadata for events to log into the logging system.
|
||||
/// - Parameters:
|
||||
/// - context: A type that contains all the parameters associated with a given request, and that conforms to the `RequestContext` protocol.
|
||||
/// - request: A type that contains all the parameters to process as a request.
|
||||
/// - statusCode: A representation of a response status to provide as a response.
|
||||
/// - redirect: A URI path to use in a redirection event, if any.
|
||||
/// - Returns: A generated metadata dictionary for an event to log into the logging system.
|
||||
static func metadata(
|
||||
context: any RequestContext,
|
||||
request: Request,
|
||||
statusCode: HTTPResponse.Status,
|
||||
redirect: String? = nil
|
||||
) -> Self {
|
||||
var metadata: Logger.Metadata = [
|
||||
"hb.request.id": "\(context.id)",
|
||||
"hb.request.method": "\(request.method.rawValue)",
|
||||
"hb.request.path": "\(request.uri.path)",
|
||||
"hb.request.status": "\(statusCode.code)"
|
||||
]
|
||||
|
||||
if let redirect {
|
||||
metadata["hb.request.redirect"] = "\(redirect)"
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
import Foundation
|
||||
import RegexBuilder
|
||||
|
||||
/// A use case that obtains some necessary data from a given URI path, that are essential for routing the documentation contents.
|
||||
/// A use case that extracts data from a given URI path, essential for routing the documentation contents.
|
||||
struct PrepareURIPathUseCase {
|
||||
|
||||
// MARK: Type aliases
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
// ===----------------------------------------------------------------------===
|
||||
//
|
||||
// This source file is part of the Hummingbird DocC Middleware open source project
|
||||
//
|
||||
// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors
|
||||
// Licensed under the EUPL 1.2 or later.
|
||||
//
|
||||
// See LICENSE for license information
|
||||
// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
import Testing
|
||||
|
||||
import struct Hummingbird.HTTPRequest
|
||||
import struct Hummingbird.HTTPResponse
|
||||
import struct Hummingbird.Request
|
||||
import struct Logging.Logger
|
||||
|
||||
@testable import DocCMiddleware
|
||||
|
||||
@Suite("Logger Metadata Helpers", .tags(.extension))
|
||||
struct LoggerMetadata_HelpersTests {
|
||||
|
||||
// MARK: Functions tests
|
||||
|
||||
#if swift(>=6.2)
|
||||
@Test
|
||||
func `metadata with HTTP method and status code`() throws {
|
||||
assertMetadata(
|
||||
method: try randomMethod,
|
||||
statusCode: try randomStatusCode
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `metadata with HTTP method, status code and redirection URI path`() throws {
|
||||
assertMetadata(
|
||||
method: try randomMethod,
|
||||
statusCode: try randomStatusCode,
|
||||
redirect: .uriRedirection
|
||||
)
|
||||
}
|
||||
#else
|
||||
@Test("metadata with HTTP method and status code")
|
||||
func metadataWithMethodAndStatusCode() throws {
|
||||
assertMetadata(
|
||||
method: try randomMethod,
|
||||
statusCode: try randomStatusCode
|
||||
)
|
||||
}
|
||||
|
||||
@Test("metadata with HTTP method, status code and redirection URI path")
|
||||
func metadataWithMethodStatusCodeAndRedirection() throws {
|
||||
assertMetadata(
|
||||
method: try randomMethod,
|
||||
statusCode: try randomStatusCode,
|
||||
redirect: .uriRedirection
|
||||
)
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Assertions
|
||||
|
||||
private extension LoggerMetadata_HelpersTests {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
/// Asserts the generated metadata dictionary based on provided parameters.
|
||||
/// - Parameters:
|
||||
/// - method: A HTTP method of the request.
|
||||
/// - statusCode: A status code of the response.
|
||||
/// - redirect: A redirection URI path, if any.
|
||||
func assertMetadata(
|
||||
method: HTTPRequest.Method,
|
||||
statusCode: HTTPResponse.Status,
|
||||
redirect: String? = nil
|
||||
) {
|
||||
// GIVEN
|
||||
let logger: Logger = .test
|
||||
let context: MockRequestContext = .init(logger: logger)
|
||||
let request: Request = .test(method: method)
|
||||
|
||||
// WHEN
|
||||
let metadata: Logger.Metadata = .metadata(
|
||||
context: context,
|
||||
request: request,
|
||||
statusCode: statusCode,
|
||||
redirect: redirect
|
||||
)
|
||||
|
||||
// THEN
|
||||
#expect(metadata.keys.count == (redirect == nil ? 4 : 5))
|
||||
#expect(metadata["hb.request.id"] == logger[metadataKey: "hb.request.id"])
|
||||
#expect(metadata["hb.request.method"] == "\(method.rawValue)")
|
||||
#expect(metadata["hb.request.path"] == "/")
|
||||
#expect(metadata["hb.request.status"] == "\(statusCode.code)")
|
||||
|
||||
if let redirect {
|
||||
#expect(metadata["hb.request.redirect"] == "\(redirect)")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension LoggerMetadata_HelpersTests {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
/// Extracts a random HTTP method of the request from a list of pre-defined values.
|
||||
var randomMethod: HTTPRequest.Method {
|
||||
get throws {
|
||||
try #require([.connect, .delete, .get, .head, .options, .patch, .post, .put, .trace].randomElement())
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts a random status code of the response from a list of pre-defined values.
|
||||
var randomStatusCode: HTTPResponse.Status {
|
||||
get throws {
|
||||
try #require([.`continue`, .earlyHints, .ok, .accepted, .multipleChoices, .seeOther, .badRequest, .notFound, .internalServerError, .serviceUnavailable].randomElement())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private extension String {
|
||||
/// A URI path to use as a redirection sample.
|
||||
static let uriRedirection = "/some/redirect/path"
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
import Foundation
|
||||
import Logging
|
||||
import Testing
|
||||
|
||||
@@ -22,7 +23,9 @@ extension Logger {
|
||||
var logger = Logger(label: "test.hummingbird-docc-middleware.logger")
|
||||
|
||||
logger.logLevel = try! #require(Logger.Level.allCases.randomElement())
|
||||
|
||||
|
||||
logger[metadataKey: "hb.request.id"] = "\(UUID().uuidString)"
|
||||
|
||||
return logger
|
||||
}()
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// ===----------------------------------------------------------------------===
|
||||
//
|
||||
// This source file is part of the Hummingbird DocC Middleware open source project
|
||||
//
|
||||
// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors
|
||||
// Licensed under the EUPL 1.2 or later.
|
||||
//
|
||||
// See LICENSE for license information
|
||||
// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
import struct Hummingbird.HTTPRequest
|
||||
import struct Hummingbird.Request
|
||||
import struct Hummingbird.RequestBody
|
||||
|
||||
extension Request {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
/// Generates a request that is ready to use in test case.
|
||||
/// - Parameters:
|
||||
/// - method: A HTTP method.
|
||||
/// - path: A URI path, if any.
|
||||
/// - Returns: A generated request instance to use in test cases.
|
||||
static func test(
|
||||
method: HTTPRequest.Method,
|
||||
path: String? = nil
|
||||
) -> Self {
|
||||
.init(
|
||||
head: .init(
|
||||
method: method,
|
||||
scheme: nil,
|
||||
authority: nil,
|
||||
path: path
|
||||
),
|
||||
body: .init(buffer: .init())
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,8 @@ extension Tag {
|
||||
|
||||
/// Tag that indicate a test case for an enumeration type.
|
||||
@Tag static var enumeration: Self
|
||||
/// Tag that indicate a test case for an extended type.
|
||||
@Tag static var `extension`: Self
|
||||
/// Tag that indicate a test case for a middleware type.
|
||||
@Tag static var middleware: Self
|
||||
/// Tag that indicate a test case for a use case type.
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// ===----------------------------------------------------------------------===
|
||||
//
|
||||
// This source file is part of the Hummingbird DocC Middleware open source project
|
||||
//
|
||||
// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors
|
||||
// Licensed under the EUPL 1.2 or later.
|
||||
//
|
||||
// See LICENSE for license information
|
||||
// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
import class NIOEmbedded.NIOAsyncTestingChannel
|
||||
|
||||
import protocol Hummingbird.RequestContext
|
||||
|
||||
import struct Hummingbird.ApplicationRequestContextSource
|
||||
import struct Hummingbird.CoreRequestContextStorage
|
||||
import struct Logging.Logger
|
||||
|
||||
/// A mock that conforms to the `RequestContext` protocol.
|
||||
struct MockRequestContext {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
var coreContext: CoreRequestContextStorage
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Initializes this mock.
|
||||
/// - Parameter logger: A type that interacts with the logging system.
|
||||
init(logger: Logger) {
|
||||
self.coreContext = .init(source: ApplicationRequestContextSource(
|
||||
channel: NIOAsyncTestingChannel(),
|
||||
logger: logger
|
||||
))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - RequestContext
|
||||
|
||||
extension MockRequestContext: RequestContext {
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
init(source: ApplicationRequestContextSource) {
|
||||
self.coreContext = .init(source: source)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,5 +10,5 @@
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
/// A namespace assigned for test arguments that would be input into test cases.
|
||||
/// A namespace assigned for test arguments
|
||||
enum Input {}
|
||||
|
||||
Reference in New Issue
Block a user