From 4000843f521801f28ba10f0bbc270a0845dc8e18 Mon Sep 17 00:00:00 2001 From: Mykola <61082418+gulivero1773@users.noreply.github.com> Date: Tue, 6 Feb 2024 18:31:31 +0200 Subject: [PATCH] feat: add function for check psql connection (#10) * feat: add function for check psql connection * fix: fix swift lint * tests: try fix tests. Bug with date format * test: try fix test for date format * refactor: refactor test and add extension * refactor: delete print * style: delete space * docs: add documentation * refactor: change date format * tests: refactor mock data * refactor: refactor functions, rename and add tls parameter * refactor: change function checkConnection for result * refactor: change struct response * docs: change documentation * refactor: rename file * docs: add license documentation * refactor: refactor method for get psql connection * refactor: add test for func * refactor: refactotr test * refactor: rename func * refactor: add field to struct * refactor: rename func * refactor: delete not use init * refactor: refactor service * refactor: refactor funcs * docs: change documentation * tests: fix tests * tests: refactor tests * refactor: change funcs * tests: refactor tests * refactor: change function --- Package.resolved | 54 ++++++++++ Package.swift | 10 +- .../AppHealthChecks.swift | 0 Sources/AppHealthChecks/ChecksProtocol.swift | 33 ++++++ .../Extensions/Application+Extensions.swift | 38 ++++++- .../Models/ComponentName.swift | 49 +++++++++ .../{Response => Models}/ComponentType.swift | 3 + .../{Response => Models}/HealthCheck.swift | 0 .../Models/HealthCheckItem+Equatable.swift | 59 ++++++++++ .../HealthCheckItem.swift | 0 .../HealthCheckStatus.swift | 0 .../MeasurementType.swift | 4 + .../PostgresChecksProtocol.swift | 40 +++++++ .../PostgresHealthChecks.swift | 102 ++++++++++++++++++ .../PostgresHealthChecksProtocol.swift | 28 +++++ .../Constants.swift} | 21 +--- .../HealthCheckItemTests.swift | 59 ++++++++++ .../Mocks/PostgresHealthChecksMock.swift | 64 +++++++++++ .../PostgresHealthChecksTests.swift | 68 ++++++++++++ 19 files changed, 613 insertions(+), 19 deletions(-) rename Sources/AppHealthChecks/{HealthChecks.ApplicationStatus => ApplicationHealthChecks}/AppHealthChecks.swift (100%) create mode 100644 Sources/AppHealthChecks/ChecksProtocol.swift create mode 100644 Sources/AppHealthChecks/Models/ComponentName.swift rename Sources/AppHealthChecks/{Response => Models}/ComponentType.swift (81%) rename Sources/AppHealthChecks/{Response => Models}/HealthCheck.swift (100%) create mode 100644 Sources/AppHealthChecks/Models/HealthCheckItem+Equatable.swift rename Sources/AppHealthChecks/{Response => Models}/HealthCheckItem.swift (100%) rename Sources/AppHealthChecks/{Response => Models}/HealthCheckStatus.swift (100%) rename Sources/AppHealthChecks/{Response => Models}/MeasurementType.swift (77%) create mode 100644 Sources/AppHealthChecks/PostgresHealthChecks/PostgresChecksProtocol.swift create mode 100644 Sources/AppHealthChecks/PostgresHealthChecks/PostgresHealthChecks.swift create mode 100644 Sources/AppHealthChecks/PostgresHealthChecks/PostgresHealthChecksProtocol.swift rename Sources/AppHealthChecks/{Response/ComponentName.swift => Utilities/Constants.swift} (68%) create mode 100644 Tests/AppHealthChecksTests/HealthCheckItemTests.swift create mode 100644 Tests/AppHealthChecksTests/Mocks/PostgresHealthChecksMock.swift create mode 100644 Tests/AppHealthChecksTests/PostgresHealthChecksTests.swift diff --git a/Package.resolved b/Package.resolved index 8a3152b..0289bc7 100644 --- a/Package.resolved +++ b/Package.resolved @@ -27,6 +27,33 @@ "version" : "4.14.1" } }, + { + "identity" : "fluent", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/fluent.git", + "state" : { + "revision" : "a586a5d4164f23a0ee4e02e1f467b9bbef0c9f1c", + "version" : "4.9.0" + } + }, + { + "identity" : "fluent-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/fluent-kit.git", + "state" : { + "revision" : "6cef8533c9ab87865de58fa3c6e6317e3e09857a", + "version" : "1.45.1" + } + }, + { + "identity" : "fluent-postgres-driver", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/fluent-postgres-driver.git", + "state" : { + "revision" : "a538fc647f82d915eb84e0a12ca9b08c513e57c4", + "version" : "2.8.0" + } + }, { "identity" : "multipart-kit", "kind" : "remoteSourceControl", @@ -36,6 +63,24 @@ "version" : "4.6.0" } }, + { + "identity" : "postgres-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/postgres-kit.git", + "state" : { + "revision" : "80ab7737dac4fccd4a8ad38743828dcb71ba7ac8", + "version" : "2.12.2" + } + }, + { + "identity" : "postgres-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/postgres-nio.git", + "state" : { + "revision" : "fa3137d39bca84843739db1c5a3db2d7f4ae65e6", + "version" : "1.20.0" + } + }, { "identity" : "routing-kit", "kind" : "remoteSourceControl", @@ -45,6 +90,15 @@ "version" : "4.9.0" } }, + { + "identity" : "sql-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/sql-kit.git", + "state" : { + "revision" : "b2f128cb62a3abfbb1e3b2893ff3ee69e70f4f0f", + "version" : "3.28.0" + } + }, { "identity" : "swift-algorithms", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 7479a9e..ce7999f 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,11 @@ let package = Package( ], dependencies: [ // 💧 A server-side Swift web framework. - .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0") + .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"), + // 🖋 Swift ORM (queries, models, and relations) for NoSQL and SQL databases. + .package(url: "https://github.com/vapor/fluent.git", from: "4.1.0"), + // 🐘 Swift ORM (queries, models, relations, etc) built on PostgreSQL. + .package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.1.1") ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. @@ -24,7 +28,9 @@ let package = Package( .target( name: "AppHealthChecks", dependencies: [ - .product(name: "Vapor", package: "vapor") + .product(name: "Vapor", package: "vapor"), + .product(name: "Fluent", package: "fluent"), + .product(name: "FluentPostgresDriver", package: "fluent-postgres-driver") ] ), .testTarget( diff --git a/Sources/AppHealthChecks/HealthChecks.ApplicationStatus/AppHealthChecks.swift b/Sources/AppHealthChecks/ApplicationHealthChecks/AppHealthChecks.swift similarity index 100% rename from Sources/AppHealthChecks/HealthChecks.ApplicationStatus/AppHealthChecks.swift rename to Sources/AppHealthChecks/ApplicationHealthChecks/AppHealthChecks.swift diff --git a/Sources/AppHealthChecks/ChecksProtocol.swift b/Sources/AppHealthChecks/ChecksProtocol.swift new file mode 100644 index 0000000..0324c0a --- /dev/null +++ b/Sources/AppHealthChecks/ChecksProtocol.swift @@ -0,0 +1,33 @@ +// FS App Health Checks +// Copyright (C) 2024 FREEDOM SPACE, LLC + +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// +// ChecksProtocol.swift +// +// +// Created by Mykola Buhaiov on 06.02.2024. +// + +import Vapor + +/// Groups func for get health check +public protocol ChecksProtocol { + /// Check with setup options + /// - Parameter options: array of `MeasurementType` + /// - Returns: dictionary `[String: HealthCheckItem]` + func checkHealth(for options: [MeasurementType]) async -> [String: HealthCheckItem] +} diff --git a/Sources/AppHealthChecks/Extensions/Application+Extensions.swift b/Sources/AppHealthChecks/Extensions/Application+Extensions.swift index 3d3bda8..c386c15 100644 --- a/Sources/AppHealthChecks/Extensions/Application+Extensions.swift +++ b/Sources/AppHealthChecks/Extensions/Application+Extensions.swift @@ -31,7 +31,7 @@ extension Application { /// Less verbose typealias for `UUID`. typealias Value = UUID } - + /// Setup `serviceId` in application storage public var serviceId: UUID? { get { storage[ServiceIdKey.self] } @@ -49,4 +49,40 @@ extension Application { get { storage[ReleaseIdKey.self] } set { storage[ReleaseIdKey.self] = newValue } } + + /// A `psqlIdKey` conform to StorageKey protocol + private struct PsqlIdKey: StorageKey { + /// Less verbose typealias for `String`. + typealias Value = String + } + + /// Setup `psqlIdKey` in application storage + public var psqlId: String? { + get { storage[PsqlIdKey.self] } + set { storage[PsqlIdKey.self] = newValue } + } +} + +extension Application { + /// A `PostgresHealthChecksKey` conform to StorageKey protocol + public struct PostgresHealthChecksKey: StorageKey { + /// Less verbose typealias for `PostgresHealthChecksProtocol`. + public typealias Value = PostgresHealthChecksProtocol + } + + /// Setup `psqlHealthChecks` in application storage + public var psqlHealthChecks: PostgresHealthChecksProtocol? { + get { storage[PostgresHealthChecksKey.self] } + set { storage[PostgresHealthChecksKey.self] = newValue } + } +} + +extension Application { + /// Variable of date conform to DateFormatter protocol. ISO 8601 with date time format + /// Example: `2024-02-01T11:11:59.364` + public var dateTimeISOFormat: DateFormatter { + let formatter = DateFormatter() + formatter.dateFormat = Constants.dateFormat + return formatter + } } diff --git a/Sources/AppHealthChecks/Models/ComponentName.swift b/Sources/AppHealthChecks/Models/ComponentName.swift new file mode 100644 index 0000000..0319ecc --- /dev/null +++ b/Sources/AppHealthChecks/Models/ComponentName.swift @@ -0,0 +1,49 @@ +// FS App Health Checks +// Copyright (C) 2024 FREEDOM SPACE, LLC + +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// +// ComponentName.swift +// +// +// Created by Mykola Buhaiov on 29.01.2024. +// + +import Vapor + +/// Human-readable name for the component +public enum ComponentName: String { + /// The Central Processing Unit (CPU) is the primary component of a computer that acts as its "control center." + case cpu + /// Memory, also known as random access memory (RAM), is a PC component that stores data while the computer runs + case memory + /// Redis is an open-source in-memory storage, used as a distributed, in-memory key–value database + case redis + /// PostgreSQL also known as Postgres, is a free and open-source relational database management system (RDBMS) emphasizing extensibility + case postgresql + /// MongoDB is a source-available, cross-platform, document-oriented database program. + case mongo + /// Distributed messaging system between server applications in real time + case kafka + /// Consul is a service networking solution to automate network configurations, discover services, and enable secure connectivity across any cloud or runtime. + case consul + /// gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. + case grpc +} + +extension ComponentName: Content {} + +extension ComponentName: CaseIterable {} diff --git a/Sources/AppHealthChecks/Response/ComponentType.swift b/Sources/AppHealthChecks/Models/ComponentType.swift similarity index 81% rename from Sources/AppHealthChecks/Response/ComponentType.swift rename to Sources/AppHealthChecks/Models/ComponentType.swift index d2486c8..3debadd 100644 --- a/Sources/AppHealthChecks/Response/ComponentType.swift +++ b/Sources/AppHealthChecks/Models/ComponentType.swift @@ -26,8 +26,11 @@ import Vapor /// Human-readable type for the component. public enum ComponentType: String { + /// A part that combines with other parts to form something bigger case component + /// A datastore is a repository for storing, managing and distributing data sets on an enterprise level case datastore + /// A set of connected things or devices that operate together case system } diff --git a/Sources/AppHealthChecks/Response/HealthCheck.swift b/Sources/AppHealthChecks/Models/HealthCheck.swift similarity index 100% rename from Sources/AppHealthChecks/Response/HealthCheck.swift rename to Sources/AppHealthChecks/Models/HealthCheck.swift diff --git a/Sources/AppHealthChecks/Models/HealthCheckItem+Equatable.swift b/Sources/AppHealthChecks/Models/HealthCheckItem+Equatable.swift new file mode 100644 index 0000000..5f197f0 --- /dev/null +++ b/Sources/AppHealthChecks/Models/HealthCheckItem+Equatable.swift @@ -0,0 +1,59 @@ +// FS App Health Checks +// Copyright (C) 2024 FREEDOM SPACE, LLC + +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// +// HealthCheckItem+Equatable.swift +// +// +// Created by Mykola Buhaiov on 06.02.2024. +// + +import Vapor + +/// Equatable protocol implementation for `HealthCheckItem` struct. +/// +/// Two `HealthCheckItem` structs are considered equal if they have the same: +/// +/// - componentId +/// - componentType +/// - observedValue +/// - observedUnit +/// - status +/// - affectedEndpoints +/// - time +/// - output +/// - links +/// - node +extension HealthCheckItem: Equatable { + /// Conform `HealthCheckItem` to `Equatable` protocol + /// - Parameters: + /// - lhs: `HealthCheckItem` + /// - rhs: `HealthCheckItem` + /// - Returns: `Bool` + public static func == (lhs: HealthCheckItem, rhs: HealthCheckItem) -> Bool { + return lhs.componentId == rhs.componentId && + lhs.componentType == rhs.componentType && + lhs.observedValue == rhs.observedValue && + lhs.observedUnit == rhs.observedUnit && + lhs.status == rhs.status && + lhs.affectedEndpoints == rhs.affectedEndpoints && + lhs.time == rhs.time && + lhs.output == rhs.output && + lhs.links == rhs.links && + lhs.node == rhs.node + } +} diff --git a/Sources/AppHealthChecks/Response/HealthCheckItem.swift b/Sources/AppHealthChecks/Models/HealthCheckItem.swift similarity index 100% rename from Sources/AppHealthChecks/Response/HealthCheckItem.swift rename to Sources/AppHealthChecks/Models/HealthCheckItem.swift diff --git a/Sources/AppHealthChecks/Response/HealthCheckStatus.swift b/Sources/AppHealthChecks/Models/HealthCheckStatus.swift similarity index 100% rename from Sources/AppHealthChecks/Response/HealthCheckStatus.swift rename to Sources/AppHealthChecks/Models/HealthCheckStatus.swift diff --git a/Sources/AppHealthChecks/Response/MeasurementType.swift b/Sources/AppHealthChecks/Models/MeasurementType.swift similarity index 77% rename from Sources/AppHealthChecks/Response/MeasurementType.swift rename to Sources/AppHealthChecks/Models/MeasurementType.swift index f4336c9..af0923e 100644 --- a/Sources/AppHealthChecks/Response/MeasurementType.swift +++ b/Sources/AppHealthChecks/Models/MeasurementType.swift @@ -26,9 +26,13 @@ import Vapor /// Name of the measurement type (a data point type) that the status is reported for public enum MeasurementType: String { + /// An act or instance of making practical or profitable use of something case utilization + /// The time lag between an electronic input and the output signal which depends upon the value of passive components used case responseTime + /// The state of being connected case connections + /// Uptime is a measure of system reliability, expressed as the percentage of time a machine case uptime } diff --git a/Sources/AppHealthChecks/PostgresHealthChecks/PostgresChecksProtocol.swift b/Sources/AppHealthChecks/PostgresHealthChecks/PostgresChecksProtocol.swift new file mode 100644 index 0000000..4410c70 --- /dev/null +++ b/Sources/AppHealthChecks/PostgresHealthChecks/PostgresChecksProtocol.swift @@ -0,0 +1,40 @@ +// FS App Health Checks +// Copyright (C) 2024 FREEDOM SPACE, LLC + +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// +// PostgresChecksProtocol.swift +// +// +// Created by Mykola Buhaiov on 31.01.2024. +// + +import Vapor + +/// Groups func for get psql health check +public protocol PostgresChecksProtocol { + /// Get Postgresql version + /// - Returns: `HealthCheckItem` + func connection() async -> HealthCheckItem + + /// Get response time from postgresql + /// - Returns: `HealthCheckItem` + func getResponseTime() async -> HealthCheckItem + + /// Get version from postgresql + /// - Returns: `String` + func getVersion() async -> String +} diff --git a/Sources/AppHealthChecks/PostgresHealthChecks/PostgresHealthChecks.swift b/Sources/AppHealthChecks/PostgresHealthChecks/PostgresHealthChecks.swift new file mode 100644 index 0000000..6bcdf8d --- /dev/null +++ b/Sources/AppHealthChecks/PostgresHealthChecks/PostgresHealthChecks.swift @@ -0,0 +1,102 @@ +// FS App Health Checks +// Copyright (C) 2024 FREEDOM SPACE, LLC + +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// +// PostgresHealthChecks.swift +// +// +// Created by Mykola Buhaiov on 31.01.2024. +// + +import Vapor +import Fluent +import FluentPostgresDriver + +/// Service that provides psql health check functionality +public struct PostgresHealthChecks: PostgresHealthChecksProtocol { + /// Instance of app as `Application` + public let app: Application + + /// Get postgresql version + /// - Returns: `HealthCheckItem` + public func connection() async -> HealthCheckItem { + let dateNow = Date().timeIntervalSinceReferenceDate + let versionDescription = await getVersion() + let result = HealthCheckItem( + componentId: app.psqlId, + componentType: .datastore, + observedValue: Date().timeIntervalSinceReferenceDate - dateNow, + observedUnit: "s", + status: versionDescription.contains("PostgreSQL") ? .pass : .fail, + time: app.dateTimeISOFormat.string(from: Date()), + output: !versionDescription.contains("PostgreSQL") ? versionDescription : nil, + links: nil, + node: nil + ) + return result + } + + /// Get response time from postgresql + /// - Returns: `HealthCheckItem` + public func getResponseTime() async -> HealthCheckItem { + let dateNow = Date().timeIntervalSinceReferenceDate + let versionDescription = await getVersion() + let result = HealthCheckItem( + componentId: app.psqlId, + componentType: .datastore, + observedValue: Date().timeIntervalSinceReferenceDate - dateNow, + observedUnit: "s", + status: versionDescription.contains("PostgreSQL") ? .pass : .fail, + time: app.dateTimeISOFormat.string(from: Date()), + output: !versionDescription.contains("PostgreSQL") ? versionDescription : nil, + links: nil, + node: nil + ) + return result + } + + /// Get version from postgresql + /// - Returns: `String` + public func getVersion() async -> String { + let rows = try? await (app.db(.psql) as? PostgresDatabase)?.simpleQuery("SELECT version()").get() + let row = rows?.first?.makeRandomAccess() + var connectionDescription = "ERROR: No connect to Postgres database" + if let result = (row?[data: "version"].string) { + connectionDescription = result + } + return connectionDescription + } + + /// Check with setup options + /// - Parameter options: array of `MeasurementType` + /// - Returns: dictionary `[String: HealthCheckItem]` + public func checkHealth(for options: [MeasurementType]) async -> [String: HealthCheckItem] { + var result = ["": HealthCheckItem()] + let measurementTypes = Array(Set(options)) + for type in measurementTypes { + switch type { + case .responseTime: + result["\(ComponentName.postgresql):\(MeasurementType.responseTime)"] = await getResponseTime() + case .connections: + result["\(ComponentName.postgresql):\(MeasurementType.connections)"] = await connection() + default: + break + } + } + return result + } +} diff --git a/Sources/AppHealthChecks/PostgresHealthChecks/PostgresHealthChecksProtocol.swift b/Sources/AppHealthChecks/PostgresHealthChecks/PostgresHealthChecksProtocol.swift new file mode 100644 index 0000000..ab22293 --- /dev/null +++ b/Sources/AppHealthChecks/PostgresHealthChecks/PostgresHealthChecksProtocol.swift @@ -0,0 +1,28 @@ +// FS App Health Checks +// Copyright (C) 2024 FREEDOM SPACE, LLC + +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// +// PostgresHealthChecksProtocol.swift +// +// +// Created by Mykola Buhaiov on 06.02.2024. +// + +import Vapor + +/// Groups func for get psql health check +public protocol PostgresHealthChecksProtocol: PostgresChecksProtocol, ChecksProtocol {} diff --git a/Sources/AppHealthChecks/Response/ComponentName.swift b/Sources/AppHealthChecks/Utilities/Constants.swift similarity index 68% rename from Sources/AppHealthChecks/Response/ComponentName.swift rename to Sources/AppHealthChecks/Utilities/Constants.swift index 1cf6b02..f7b9357 100644 --- a/Sources/AppHealthChecks/Response/ComponentName.swift +++ b/Sources/AppHealthChecks/Utilities/Constants.swift @@ -16,26 +16,15 @@ // along with this program. If not, see . // -// ComponentName.swift +// Constants.swift // // -// Created by Mykola Buhaiov on 29.01.2024. +// Created by Mykola Buhaiov on 06.02.2024. // import Vapor -/// Human-readable name for the component -public enum ComponentName: String { - case cpu - case memory - case redis - case postgresql - case mongo - case kafka - case consul - case grpc +/// Constants +public enum Constants { + static let dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" } - -extension ComponentName: Content {} - -extension ComponentName: CaseIterable {} diff --git a/Tests/AppHealthChecksTests/HealthCheckItemTests.swift b/Tests/AppHealthChecksTests/HealthCheckItemTests.swift new file mode 100644 index 0000000..ba8ce3a --- /dev/null +++ b/Tests/AppHealthChecksTests/HealthCheckItemTests.swift @@ -0,0 +1,59 @@ +// FS App Health Checks +// Copyright (C) 2024 FREEDOM SPACE, LLC + +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// +// HealthCheckItemTests.swift +// +// +// Created by Mykola Buhaiov on 06.02.2024. +// + +import Vapor +import XCTest +@testable import AppHealthChecks + +final class HealthCheckItemTests: XCTestCase { + func testPublicKeyEquatable() { + let firstHealthCheckItem = HealthCheckItem( + componentId: "adca7c3d-55f4-4ab3-a842-18b35f50cb0f", + componentType: .datastore, + observedValue: 1, + observedUnit: "s", + status: .pass, + affectedEndpoints: nil, + time: "2024-02-01T11:11:59.364", + output: "Ok", + links: nil, + node: nil + ) + var secondHealthCheckItem = HealthCheckItem( + componentId: "adca7c3d-55f4-4ab3-a842-18b35f50cb0f", + componentType: .datastore, + observedValue: 1, + observedUnit: "s", + status: .pass, + affectedEndpoints: nil, + time: "2024-02-01T11:11:59.364", + output: "Ok", + links: nil, + node: nil + ) + XCTAssertEqual(firstHealthCheckItem, secondHealthCheckItem) + secondHealthCheckItem.observedValue = 4 + XCTAssertNotEqual(firstHealthCheckItem, secondHealthCheckItem) + } +} diff --git a/Tests/AppHealthChecksTests/Mocks/PostgresHealthChecksMock.swift b/Tests/AppHealthChecksTests/Mocks/PostgresHealthChecksMock.swift new file mode 100644 index 0000000..920e56f --- /dev/null +++ b/Tests/AppHealthChecksTests/Mocks/PostgresHealthChecksMock.swift @@ -0,0 +1,64 @@ +// FS App Health Checks +// Copyright (C) 2024 FREEDOM SPACE, LLC + +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// +// PostgresHealthChecksMock.swift +// +// +// Created by Mykola Buhaiov on 31.01.2024. +// + +import Vapor +import FluentPostgresDriver +@testable import AppHealthChecks + +public struct PostgresHealthChecksMock: PostgresHealthChecksProtocol { + static let psqlId = "adca7c3d-55f4-4ab3-a842-18b35f50cb0f" + static let version = "database version - 15" + static let healthCheckItem = HealthCheckItem( + componentId: psqlId, + componentType: .datastore, + observedValue: 1, + observedUnit: "s", + status: .pass, + affectedEndpoints: nil, + time: "2024-02-01T11:11:59.364", + output: "Ok", + links: nil, + node: nil + ) + + public func connection() async -> HealthCheckItem { + PostgresHealthChecksMock.healthCheckItem + } + + public func getResponseTime() async -> HealthCheckItem { + PostgresHealthChecksMock.healthCheckItem + } + + public func getVersion() async -> String { + PostgresHealthChecksMock.version + } + + public func checkHealth(for options: [MeasurementType]) async -> [String: HealthCheckItem] { + let result = [ + "\(ComponentName.postgresql):\(MeasurementType.responseTime)": PostgresHealthChecksMock.healthCheckItem, + "\(ComponentName.postgresql):\(MeasurementType.connections)": PostgresHealthChecksMock.healthCheckItem + ] + return result + } +} diff --git a/Tests/AppHealthChecksTests/PostgresHealthChecksTests.swift b/Tests/AppHealthChecksTests/PostgresHealthChecksTests.swift new file mode 100644 index 0000000..5302ede --- /dev/null +++ b/Tests/AppHealthChecksTests/PostgresHealthChecksTests.swift @@ -0,0 +1,68 @@ +// FS App Health Checks +// Copyright (C) 2024 FREEDOM SPACE, LLC + +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// +// PostgresHealthChecksTests.swift +// +// +// Created by Mykola Buhaiov on 09.03.2023. +// + +import Vapor +import XCTest +@testable import AppHealthChecks + +final class PostgresHealthChecksTests: XCTestCase { + func testConnection() async throws { + let app = Application(.testing) + defer { app.shutdown() } + app.psqlId = UUID().uuidString + app.psqlHealthChecks = PostgresHealthChecksMock() + let result = await app.psqlHealthChecks?.connection() + XCTAssertEqual(result, PostgresHealthChecksMock.healthCheckItem) + } + + func testGetResponseTime() async throws { + let app = Application(.testing) + defer { app.shutdown() } + let dateFormat = app.dateTimeISOFormat + app.psqlHealthChecks = PostgresHealthChecksMock() + let result = await app.psqlHealthChecks?.getResponseTime() + XCTAssertEqual(result, PostgresHealthChecksMock.healthCheckItem) + } + + func testCheckHealth() async throws { + let app = Application(.testing) + defer { app.shutdown() } + app.psqlId = UUID().uuidString + app.psqlHealthChecks = PostgresHealthChecksMock() + let result = await app.psqlHealthChecks?.checkHealth(for: [MeasurementType.responseTime, MeasurementType.connections]) + let psqlConnections = result?["\(ComponentName.postgresql):\(MeasurementType.connections)"] + XCTAssertEqual(psqlConnections, PostgresHealthChecksMock.healthCheckItem) + let psqlResponseTimes = result?["\(ComponentName.postgresql):\(MeasurementType.responseTime)"] + XCTAssertEqual(psqlResponseTimes, PostgresHealthChecksMock.healthCheckItem) + } + + func testGetVersion() async throws { + let app = Application(.testing) + defer { app.shutdown() } + app.psqlId = UUID().uuidString + app.psqlHealthChecks = PostgresHealthChecksMock() + let result = await app.psqlHealthChecks?.getVersion() + XCTAssertEqual(result, PostgresHealthChecksMock.version) + } +}