From e959bcdbf56eee5f6d8e54ac140ee2896358de2a Mon Sep 17 00:00:00 2001 From: Mykhailo Bondarenko Date: Thu, 22 Feb 2024 18:45:18 +0200 Subject: [PATCH 1/5] refactor: add tests to consul health check --- .../ConsulHealthChecks.swift | 1 + .../ConsulHealthChecksTests.swift | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift index d025ec9..41067b1 100644 --- a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift +++ b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift @@ -50,6 +50,7 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { break } } + dict[""] = nil return dict } diff --git a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift index 8eed2a5..7d5b6db 100644 --- a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift +++ b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift @@ -46,4 +46,80 @@ final class ConsulHealthChecksTests: XCTestCase { XCTAssertEqual(app.consulConfig?.username, consulConfig.username) XCTAssertEqual(app.consulConfig?.password, consulConfig.password) } + + func testCheckForBothSuccess() async { + let app = Application(.testing) + defer { app.shutdown() } + let clientResponse = ClientResponse(status: .ok) + app.clients.use { app in + MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) + } + let healthChecks = ConsulHealthChecks(app: app) + let check = await healthChecks.check(for: [.responseTime, .connections]) + print(check) + XCTAssertEqual(check.count, 2) + + // Response time check + let responseTimeCheck = check["\(ComponentName.consul):\(MeasurementType.responseTime)"]! + XCTAssertEqual(responseTimeCheck.status, .pass) + guard let observedValue = responseTimeCheck.observedValue else { + return XCTFail("no have observed value") + } + XCTAssertGreaterThan(observedValue, 0) + XCTAssertNil(responseTimeCheck.output) + + // Connections check + let connectionsCheck = check["\(ComponentName.consul):\(MeasurementType.connections)"]! + XCTAssertEqual(connectionsCheck.status, .pass) + XCTAssertNil(connectionsCheck.observedValue) + XCTAssertNil(connectionsCheck.output) + } + + func testCheckStatusSuccess() async { + let app = Application(.testing) + defer { app.shutdown() } + let clientResponse = ClientResponse(status: .ok) + app.clients.use { app in + MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) + } + let healthChecks = ConsulHealthChecks(app: app) + let response = await healthChecks.getStatus() + let result = healthChecks.status(response) + + XCTAssertEqual(result.status, .pass) + XCTAssertNil(result.observedValue) + XCTAssertNil(result.output) + } + + func testCheckResponseTimeSuccess() async { + let app = Application(.testing) + defer { app.shutdown() } + let clientResponse = ClientResponse(status: .ok) + app.clients.use { app in + MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) + } + let healthChecks = ConsulHealthChecks(app: app) + let response = await healthChecks.getStatus() + + let result = healthChecks.responseTime(from: response, Date().timeIntervalSinceReferenceDate) + XCTAssertEqual(result.status, .pass) + guard let observedValue = result.observedValue else { + return XCTFail("no have observed value") + } + XCTAssertGreaterThan(observedValue, 0) + XCTAssertNil(result.output) + } +} + +struct MockClient: Client { + var eventLoop: EventLoop + var clientResponse: ClientResponse + + func send(_ request: ClientRequest) -> EventLoopFuture { + self.eventLoop.makeSucceededFuture(self.clientResponse) + } + + func delegating(to eventLoop: EventLoop) -> Client { + self + } } From a256c6003efadf68e0e33876d4a7c492258d4a1d Mon Sep 17 00:00:00 2001 From: Mykhailo Bondarenko Date: Fri, 23 Feb 2024 11:50:41 +0200 Subject: [PATCH 2/5] test: add test for consul health checks --- .../ApplicationHealthChecks.swift | 3 +- .../ConsulHealthChecks.swift | 27 +++---- .../ApplicationHealthChecksTests.swift | 18 +---- .../ConsulHealthChecksTests.swift | 72 ++++++++++++++++--- .../HealthChecksTests/Mocks/MockClient.swift | 23 ++++++ 5 files changed, 103 insertions(+), 40 deletions(-) create mode 100644 Tests/HealthChecksTests/Mocks/MockClient.swift diff --git a/Sources/HealthChecks/ApplicationHealthChecks/ApplicationHealthChecks.swift b/Sources/HealthChecks/ApplicationHealthChecks/ApplicationHealthChecks.swift index 1f0683f..4c56b53 100644 --- a/Sources/HealthChecks/ApplicationHealthChecks/ApplicationHealthChecks.swift +++ b/Sources/HealthChecks/ApplicationHealthChecks/ApplicationHealthChecks.swift @@ -34,7 +34,7 @@ public struct ApplicationHealthChecks: ApplicationHealthChecksProtocol { /// Get uptime of the system. /// - Returns: A `HealthCheckItem` representing the application's uptime. public func uptime() -> HealthCheckItem { - let uptime = Date().timeIntervalSinceReferenceDate - app.launchTime + let uptime = Date().timeIntervalSinceReferenceDate - app.launchTime return HealthCheckItem( componentType: .system, observedValue: uptime, @@ -58,6 +58,7 @@ public struct ApplicationHealthChecks: ApplicationHealthChecksProtocol { break } } + result[""] = nil return result } } diff --git a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift index 41067b1..db4a29f 100644 --- a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift +++ b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift @@ -28,32 +28,30 @@ import Vapor public struct ConsulHealthChecks: ConsulHealthChecksProtocol { /// Instance of app as `Application` public let app: Application - + /// Check with setup options /// - Parameters: /// - options: array of `MeasurementType` /// - Returns: dictionary `[String: HealthCheckItem]` public func check(for options: [MeasurementType]) async -> [String: HealthCheckItem] { - var dict = ["": HealthCheckItem()] + var result = ["": HealthCheckItem()] let measurementTypes = Array(Set(options)) let dateNow = Date().timeIntervalSinceReferenceDate let response = await getStatus() for type in measurementTypes { switch type { case .responseTime: - let result = responseTime(from: response, dateNow) - dict["\(ComponentName.consul):\(MeasurementType.responseTime)"] = result + result["\(ComponentName.consul):\(MeasurementType.responseTime)"] = responseTime(from: response, dateNow) case .connections: - let result = status(response) - dict["\(ComponentName.consul):\(MeasurementType.connections)"] = result + result["\(ComponentName.consul):\(MeasurementType.connections)"] = status(response) default: break } } - dict[""] = nil - return dict + result[""] = nil + return result } - + /// Get response for consul /// - Returns: `ClientResponse` func getStatus() async -> ClientResponse { @@ -61,7 +59,10 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { let path = Constants.consulStatusPath let uri = URI(string: url + path) var headers = HTTPHeaders() - if let username = app.consulConfig?.username, !username.isEmpty, let password = app.consulConfig?.password, !password.isEmpty { + if let username = app.consulConfig?.username, + !username.isEmpty, + let password = app.consulConfig?.password, + !password.isEmpty { headers.basicAuthorization = BasicAuthorization(username: username, password: password) } do { @@ -82,7 +83,7 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { componentId: app.consulConfig?.id, componentType: .component, status: response.status == .ok ? .pass : .fail, - time: app.dateTimeISOFormat.string(from: Date()), + time: response.status == .ok ? app.dateTimeISOFormat.string(from: Date()) : nil, output: response.status != .ok ? "Error response from uri - \(url + path), with http status - \(response.status)" : nil, links: nil, node: nil @@ -100,10 +101,10 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { return HealthCheckItem( componentId: app.consulConfig?.id, componentType: .component, - observedValue: Date().timeIntervalSinceReferenceDate - start, + observedValue: response.status == .ok ? Date().timeIntervalSinceReferenceDate - start : 0, observedUnit: "s", status: response.status == .ok ? .pass : .fail, - time: app.dateTimeISOFormat.string(from: Date()), + time: response.status == .ok ? app.dateTimeISOFormat.string(from: Date()) : nil, output: response.status != .ok ? "Error response from uri - \(url + path), with http status - \(response.status)" : nil, links: nil, node: nil diff --git a/Tests/HealthChecksTests/ApplicationHealthChecksTests.swift b/Tests/HealthChecksTests/ApplicationHealthChecksTests.swift index 02346df..651c64b 100644 --- a/Tests/HealthChecksTests/ApplicationHealthChecksTests.swift +++ b/Tests/HealthChecksTests/ApplicationHealthChecksTests.swift @@ -71,7 +71,7 @@ final class ApplicationHealthChecksTests: XCTestCase { let healthChecks = ApplicationHealthChecks(app: app) let checks = await healthChecks.check(for: [.uptime]) - + XCTAssertEqual(checks.count, 1) XCTAssertNotNil(checks) XCTAssertTrue(checks.keys.contains("uptime")) guard let uptimeItem = checks["uptime"] else { @@ -93,20 +93,6 @@ final class ApplicationHealthChecksTests: XCTestCase { let healthChecks = ApplicationHealthChecks(app: app) let checks = await healthChecks.check(for: [.connections]) - let expectedResult = [ - "": HealthCheckItem( - componentId: nil, - componentType: nil, - observedValue: nil, - observedUnit: nil, - status: nil, - affectedEndpoints: nil, - time: nil, - output: nil, - links: nil, - node: nil - ) - ] - XCTAssertEqual(checks, expectedResult) // Expect empty result, as .memory is not supported + XCTAssertEqual(checks.count, 0) // Expect empty result, as .memory is not supported } } diff --git a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift index 7d5b6db..4b42e27 100644 --- a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift +++ b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift @@ -56,7 +56,6 @@ final class ConsulHealthChecksTests: XCTestCase { } let healthChecks = ConsulHealthChecks(app: app) let check = await healthChecks.check(for: [.responseTime, .connections]) - print(check) XCTAssertEqual(check.count, 2) // Response time check @@ -91,6 +90,22 @@ final class ConsulHealthChecksTests: XCTestCase { XCTAssertNil(result.output) } + func testCheckStatusFail() async { + let app = Application(.testing) + defer { app.shutdown() } + let clientResponse = ClientResponse(status: .badRequest) + app.clients.use { app in + MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) + } + let healthChecks = ConsulHealthChecks(app: app) + let response = await healthChecks.getStatus() + let result = healthChecks.status(response) + + XCTAssertEqual(result.status, .fail) + XCTAssertNil(result.observedValue) + XCTAssertNotNil(result.output) + } + func testCheckResponseTimeSuccess() async { let app = Application(.testing) defer { app.shutdown() } @@ -109,17 +124,54 @@ final class ConsulHealthChecksTests: XCTestCase { XCTAssertGreaterThan(observedValue, 0) XCTAssertNil(result.output) } -} - -struct MockClient: Client { - var eventLoop: EventLoop - var clientResponse: ClientResponse - func send(_ request: ClientRequest) -> EventLoopFuture { - self.eventLoop.makeSucceededFuture(self.clientResponse) + func testCheckResponseTimeFail() async { + let app = Application(.testing) + defer { app.shutdown() } + let clientResponse = ClientResponse(status: .badRequest) + app.clients.use { app in + MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) + } + let healthChecks = ConsulHealthChecks(app: app) + let response = await healthChecks.getStatus() + + let result = healthChecks.responseTime(from: response, Date().timeIntervalSinceReferenceDate) + XCTAssertEqual(result.status, .fail) + guard let observedValue = result.observedValue else { + return XCTFail("no have observed value") + } + XCTAssertEqual(observedValue, 0) + XCTAssertNotNil(result.output) + } + + func testGetStatusSuccessWithAuth() async { + let expectedUrl = "https://example.com/status" + let expectedUsername = "user" + let expectedPassword = "password" + let app = Application(.testing) + defer { app.shutdown() } + app.consulConfig = ConsulConfig( + id: String(UUID()), + url: expectedUrl, + username: expectedUsername, + password: expectedPassword + ) + let clientResponse = ClientResponse(status: .ok) + app.clients.use { app in + MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) + } + let healthChecks = ConsulHealthChecks(app: app) + let response = await healthChecks.getStatus() + + XCTAssertEqual(response.status, .ok) } - func delegating(to eventLoop: EventLoop) -> Client { - self + func testCheckHandlesUnsupportedTypes() async { + let app = Application(.testing) + defer { app.shutdown() } + + let healthChecks = ConsulHealthChecks(app: app) + let checks = await healthChecks.check(for: [.uptime]) + XCTAssertEqual(checks.count, 0) // Expect empty result, as .memory is not supported } } diff --git a/Tests/HealthChecksTests/Mocks/MockClient.swift b/Tests/HealthChecksTests/Mocks/MockClient.swift new file mode 100644 index 0000000..46400f1 --- /dev/null +++ b/Tests/HealthChecksTests/Mocks/MockClient.swift @@ -0,0 +1,23 @@ +// +// MockClient.swift +// +// +// Created by Mykhailo Bondarenko on 23.02.2024. +// + +import Vapor + +struct MockClient: Client { + var eventLoop: EventLoop + var clientResponse: ClientResponse + + func send(_ request: ClientRequest) -> EventLoopFuture { + self.eventLoop.makeSucceededFuture(self.clientResponse) + } + + func delegating(to eventLoop: EventLoop) -> Client { + self + } +} + +extension ClientResponse: @unchecked Sendable {} From f782da48b925c2e9cdc4c47fb21fa0b1deefa036 Mon Sep 17 00:00:00 2001 From: Mykhailo Bondarenko Date: Fri, 23 Feb 2024 12:09:23 +0200 Subject: [PATCH 3/5] docs: improve documentation --- Sources/HealthChecks/ChecksProtocol.swift | 11 +++-- .../ConsulHealthChecks/ConsulConfig.swift | 44 ++++++++++++++----- .../ConsulHealthChecks.swift | 27 +++++++++--- .../ConsulHealthChecksProtocol.swift | 2 +- .../ConsulHealthChecksTests.swift | 29 ++++++++---- 5 files changed, 82 insertions(+), 31 deletions(-) diff --git a/Sources/HealthChecks/ChecksProtocol.swift b/Sources/HealthChecks/ChecksProtocol.swift index e943f0e..c2fda5c 100644 --- a/Sources/HealthChecks/ChecksProtocol.swift +++ b/Sources/HealthChecks/ChecksProtocol.swift @@ -24,10 +24,13 @@ import Vapor -/// Groups func for get health check +/// The protocol defines a basic interface for performing health checks against different systems or services. public protocol ChecksProtocol { - /// Check with setup options - /// - Parameter options: array of `MeasurementType` - /// - Returns: dictionary `[String: HealthCheckItem]` + /// Performs health checks against Consul, returning a dictionary + /// of `HealthCheckItem`s for the specified components. + /// + /// - Parameters: + /// - options: An array of `MeasurementType`s indicating which checks to perform. + /// - Returns: A dictionary of `HealthCheckItem`s, keyed by component ID and measurement type. func check(for options: [MeasurementType]) async -> [String: HealthCheckItem] } diff --git a/Sources/HealthChecks/ConsulHealthChecks/ConsulConfig.swift b/Sources/HealthChecks/ConsulHealthChecks/ConsulConfig.swift index e637b4d..aa7f975 100644 --- a/Sources/HealthChecks/ConsulHealthChecks/ConsulConfig.swift +++ b/Sources/HealthChecks/ConsulHealthChecks/ConsulConfig.swift @@ -24,21 +24,41 @@ import Vapor -/// A generic `ConsulConfig` data that can be save in storage. +/// Represents configuration details for connecting to a Consul server. public struct ConsulConfig { - /// Is a unique identifier of the consul, in the application scope - /// Example: `43119325-63f5-4e14-9175-84e0e296c527` + /// A unique identifier for this Consul configuration within your application. + /// This ID is not related to Consul itself and can be used for internal reference. + /// Example: "43119325-63f5-4e14-9175-84e0e296c527" public let id: String - - /// Consul url - /// Example: `http://127.0.0.1:8500`, `https://xmpl-consul.example.com` + + /// The URL of the Consul server to connect to. + /// Example: "http://127.0.0.1:8500", "https://xmpl-consul.example.com" public let url: String - - /// Consul username - /// Example: `username` + + /// The username for authenticating with Consul (optional). + /// Example: "username" public let username: String? - - /// Consul password - /// Example: `password` + + /// The password for authenticating with Consul (optional). + /// Example: "password" public let password: String? + + /// Initializes a `ConsulConfig` with the specified details. + /// + /// - Parameters: + /// - id: The unique identifier for this configuration. + /// - url: The URL of the Consul server. + /// - username: The username for authentication (optional). + /// - password: The password for authentication (optional). + public init( + id: String, + url: String, + username: String? = nil, + password: String? = nil + ) { + self.id = id + self.url = url + self.username = username + self.password = password + } } diff --git a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift index db4a29f..4dc9d4e 100644 --- a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift +++ b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift @@ -24,9 +24,9 @@ import Vapor -/// Service that provides consul health check functionality +/// Service that provides Consul health check functionality public struct ConsulHealthChecks: ConsulHealthChecksProtocol { - /// Instance of app as `Application` + /// Instance of the application as `Application` public let app: Application /// Check with setup options @@ -55,7 +55,10 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { /// Get response for consul /// - Returns: `ClientResponse` func getStatus() async -> ClientResponse { - let url = app.consulConfig?.url ?? Constants.consulUrl + guard let url = app.consulConfig?.url else { + app.logger.error("ERROR: Consul URL is not configured.") + return ClientResponse() + } let path = Constants.consulStatusPath let uri = URI(string: url + path) var headers = HTTPHeaders() @@ -77,7 +80,14 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { /// - Parameter response: `ClientResponse` /// - Returns: `HealthCheckItem` func status(_ response: ClientResponse) -> HealthCheckItem { - let url = app.consulConfig?.url ?? Constants.consulUrl + guard let url = app.consulConfig?.url else { + return HealthCheckItem( + componentId: app.consulConfig?.id, + componentType: .component, + status: .fail, + output: "Consul URL is not configured." + ) + } let path = Constants.consulStatusPath return HealthCheckItem( componentId: app.consulConfig?.id, @@ -96,7 +106,14 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { /// - start: `TimeInterval` /// - Returns: `HealthCheckItem` func responseTime(from response: ClientResponse, _ start: TimeInterval) -> HealthCheckItem { - let url = app.consulConfig?.url ?? Constants.consulUrl + guard let url = app.consulConfig?.url else { + return HealthCheckItem( + componentId: app.consulConfig?.id, + componentType: .component, + status: .fail, + output: "Consul URL is not configured." + ) + } let path = Constants.consulStatusPath return HealthCheckItem( componentId: app.consulConfig?.id, diff --git a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecksProtocol.swift b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecksProtocol.swift index 2c1441e..43af7ce 100644 --- a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecksProtocol.swift +++ b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecksProtocol.swift @@ -24,5 +24,5 @@ import Vapor -/// Groups func for get consul health check +/// Protocol defining an interface for performing Consul health checks. public protocol ConsulHealthChecksProtocol: ChecksProtocol {} diff --git a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift index 4b42e27..301e2e6 100644 --- a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift +++ b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift @@ -33,9 +33,7 @@ final class ConsulHealthChecksTests: XCTestCase { app.consulHealthChecks = ConsulHealthChecksMock() let consulConfig = ConsulConfig( id: UUID().uuidString, - url: Constants.consulUrl, - username: "username", - password: "password" + url: Constants.consulUrl ) app.consulConfig = consulConfig let result = await app.consulHealthChecks?.check(for: [MeasurementType.responseTime, MeasurementType.connections]) @@ -50,6 +48,10 @@ final class ConsulHealthChecksTests: XCTestCase { func testCheckForBothSuccess() async { let app = Application(.testing) defer { app.shutdown() } + app.consulConfig = ConsulConfig( + id: String(UUID()), + url: "consul-url" + ) let clientResponse = ClientResponse(status: .ok) app.clients.use { app in MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) @@ -77,6 +79,10 @@ final class ConsulHealthChecksTests: XCTestCase { func testCheckStatusSuccess() async { let app = Application(.testing) defer { app.shutdown() } + app.consulConfig = ConsulConfig( + id: String(UUID()), + url: "consul-url" + ) let clientResponse = ClientResponse(status: .ok) app.clients.use { app in MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) @@ -109,6 +115,10 @@ final class ConsulHealthChecksTests: XCTestCase { func testCheckResponseTimeSuccess() async { let app = Application(.testing) defer { app.shutdown() } + app.consulConfig = ConsulConfig( + id: String(UUID()), + url: "consul-url" + ) let clientResponse = ClientResponse(status: .ok) app.clients.use { app in MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) @@ -128,6 +138,10 @@ final class ConsulHealthChecksTests: XCTestCase { func testCheckResponseTimeFail() async { let app = Application(.testing) defer { app.shutdown() } + app.consulConfig = ConsulConfig( + id: String(UUID()), + url: "consul-url" + ) let clientResponse = ClientResponse(status: .badRequest) app.clients.use { app in MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) @@ -145,16 +159,13 @@ final class ConsulHealthChecksTests: XCTestCase { } func testGetStatusSuccessWithAuth() async { - let expectedUrl = "https://example.com/status" - let expectedUsername = "user" - let expectedPassword = "password" let app = Application(.testing) defer { app.shutdown() } app.consulConfig = ConsulConfig( id: String(UUID()), - url: expectedUrl, - username: expectedUsername, - password: expectedPassword + url: "https://example.com/status", + username: "user", + password: "password" ) let clientResponse = ClientResponse(status: .ok) app.clients.use { app in From c2bf8858e03966829614a5f7db1c6a9d5d04d467 Mon Sep 17 00:00:00 2001 From: Mykhailo Bondarenko Date: Fri, 23 Feb 2024 12:14:15 +0200 Subject: [PATCH 4/5] style: resolve lint warnings --- Tests/HealthChecksTests/ConsulHealthChecksTests.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift index 301e2e6..c3bebc4 100644 --- a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift +++ b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift @@ -61,7 +61,9 @@ final class ConsulHealthChecksTests: XCTestCase { XCTAssertEqual(check.count, 2) // Response time check - let responseTimeCheck = check["\(ComponentName.consul):\(MeasurementType.responseTime)"]! + guard let responseTimeCheck = check["\(ComponentName.consul):\(MeasurementType.responseTime)"] else { + return XCTFail("no have key for response time") + } XCTAssertEqual(responseTimeCheck.status, .pass) guard let observedValue = responseTimeCheck.observedValue else { return XCTFail("no have observed value") @@ -70,7 +72,9 @@ final class ConsulHealthChecksTests: XCTestCase { XCTAssertNil(responseTimeCheck.output) // Connections check - let connectionsCheck = check["\(ComponentName.consul):\(MeasurementType.connections)"]! + guard let connectionsCheck = check["\(ComponentName.consul):\(MeasurementType.connections)"] else { + return XCTFail("no have key for connections") + } XCTAssertEqual(connectionsCheck.status, .pass) XCTAssertNil(connectionsCheck.observedValue) XCTAssertNil(connectionsCheck.output) From 3902c556a6def4ba7135f8f454c79880e028af93 Mon Sep 17 00:00:00 2001 From: Mykhailo Bondarenko Date: Fri, 23 Feb 2024 12:40:00 +0200 Subject: [PATCH 5/5] test: improve test when fail --- .../ConsulHealthChecks.swift | 24 +++---------------- .../ConsulHealthChecksTests.swift | 15 ++---------- 2 files changed, 5 insertions(+), 34 deletions(-) diff --git a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift index 4dc9d4e..3614909 100644 --- a/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift +++ b/Sources/HealthChecks/ConsulHealthChecks/ConsulHealthChecks.swift @@ -80,21 +80,12 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { /// - Parameter response: `ClientResponse` /// - Returns: `HealthCheckItem` func status(_ response: ClientResponse) -> HealthCheckItem { - guard let url = app.consulConfig?.url else { - return HealthCheckItem( - componentId: app.consulConfig?.id, - componentType: .component, - status: .fail, - output: "Consul URL is not configured." - ) - } - let path = Constants.consulStatusPath return HealthCheckItem( componentId: app.consulConfig?.id, componentType: .component, status: response.status == .ok ? .pass : .fail, - time: response.status == .ok ? app.dateTimeISOFormat.string(from: Date()) : nil, - output: response.status != .ok ? "Error response from uri - \(url + path), with http status - \(response.status)" : nil, + time: response.status == .ok ? app.dateTimeISOFormat.string(from: Date()) : nil, + output: response.status != .ok ? "Error response from consul, with http status - \(response.status)" : nil, links: nil, node: nil ) @@ -106,15 +97,6 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { /// - start: `TimeInterval` /// - Returns: `HealthCheckItem` func responseTime(from response: ClientResponse, _ start: TimeInterval) -> HealthCheckItem { - guard let url = app.consulConfig?.url else { - return HealthCheckItem( - componentId: app.consulConfig?.id, - componentType: .component, - status: .fail, - output: "Consul URL is not configured." - ) - } - let path = Constants.consulStatusPath return HealthCheckItem( componentId: app.consulConfig?.id, componentType: .component, @@ -122,7 +104,7 @@ public struct ConsulHealthChecks: ConsulHealthChecksProtocol { observedUnit: "s", status: response.status == .ok ? .pass : .fail, time: response.status == .ok ? app.dateTimeISOFormat.string(from: Date()) : nil, - output: response.status != .ok ? "Error response from uri - \(url + path), with http status - \(response.status)" : nil, + output: response.status != .ok ? "Error response from consul, with http status - \(response.status)" : nil, links: nil, node: nil ) diff --git a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift index c3bebc4..114bf06 100644 --- a/Tests/HealthChecksTests/ConsulHealthChecksTests.swift +++ b/Tests/HealthChecksTests/ConsulHealthChecksTests.swift @@ -104,12 +104,9 @@ final class ConsulHealthChecksTests: XCTestCase { let app = Application(.testing) defer { app.shutdown() } let clientResponse = ClientResponse(status: .badRequest) - app.clients.use { app in - MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) - } let healthChecks = ConsulHealthChecks(app: app) let response = await healthChecks.getStatus() - let result = healthChecks.status(response) + let result = healthChecks.status(clientResponse) XCTAssertEqual(result.status, .fail) XCTAssertNil(result.observedValue) @@ -142,18 +139,10 @@ final class ConsulHealthChecksTests: XCTestCase { func testCheckResponseTimeFail() async { let app = Application(.testing) defer { app.shutdown() } - app.consulConfig = ConsulConfig( - id: String(UUID()), - url: "consul-url" - ) let clientResponse = ClientResponse(status: .badRequest) - app.clients.use { app in - MockClient(eventLoop: app.eventLoopGroup.next(), clientResponse: clientResponse) - } let healthChecks = ConsulHealthChecks(app: app) - let response = await healthChecks.getStatus() - let result = healthChecks.responseTime(from: response, Date().timeIntervalSinceReferenceDate) + let result = healthChecks.responseTime(from: clientResponse, Date().timeIntervalSinceReferenceDate) XCTAssertEqual(result.status, .fail) guard let observedValue = result.observedValue else { return XCTFail("no have observed value")