Files
ccn/Services/Website/Tests/Library/Cases/Public/Controllers/HealthControllerTests.swift
T
javier ea00b94841 Tweaks and fixes throughout the project (#27)
This PR contains the work done to do a little bit of housekeeping pass across all packages and the Website service.

To provide further details about the work:
* Refreshed the READMEs and source documentation to match the current code;
* Tagged every test case consistently across the Infrastructure, Localization, Persistence, and Website test targets;
* Removed Website middleware tests now covered by Infrastructure's own suite;
* Conformed the `PrepareDB` method to Sendable;
* Relaxes the production Compose DATABASE_TLS default from require to prefer;
* Added Persistence test verifying the prefer posture falls back to plaintext connections.

Reviewed-on: rock-n-code/loud-amsterdam#27
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
2026-07-30 06:33:57 +00:00

146 lines
4.1 KiB
Swift

import Hummingbird
import HummingbirdTesting
import Logging
import NIOCore
import Persistence
import Testing
@testable import WebsiteLibrary
@Suite(
"HealthController controller",
.tags(.controller)
)
struct HealthControllerTests {
// MARK: Functional tests
@Test
func `serves the status payload at the health path`() async throws {
try await app(
probe: nil
).test(.router) { client in
try await client.execute(
uri: "/health",
method: .get
) { response in
let body = String(buffer: response.body)
#expect(response.status == .ok)
#expect(response.headers[.contentType] == "application/json")
#expect(body == #"{"status":"ok"}"#)
}
}
}
@Test
func `serves ready at the readiness path when the database is reachable`() async throws {
let service = Service(
driver: .inMemory,
logger: Logger(label: "test")
)
let fluent = service()
do {
try await app(
probe: Probe(fluent: fluent)
).test(.router) { client in
try await client.execute(
uri: "/health/ready",
method: .get
) { response in
let body = String(buffer: response.body)
#expect(response.status == .ok)
#expect(response.headers[.contentType] == "application/json")
#expect(body == #"{"status":"ready"}"#)
}
}
} catch {
try? await fluent.shutdown()
throw error
}
try await fluent.shutdown()
}
@Test
func `serves unavailable at the readiness path when the database is unreachable`() async throws {
// Port 1 on the loopback interface has nothing listening, so the probe's connection is refused
// immediately instead of timing out.
let service = Service(
driver: .mysql(
.init(
host: "127.0.0.1",
port: 1,
name: "unreachable",
username: "nobody",
password: "nothing",
tls: .off,
maxConnectionsPerEventLoop: 1
)
),
logger: Logger(label: "test")
)
let fluent = service()
do {
try await app(
probe: Probe(fluent: fluent)
).test(.router) { client in
try await client.execute(
uri: "/health/ready",
method: .get
) { response in
let body = String(buffer: response.body)
#expect(response.status == .serviceUnavailable)
#expect(response.headers[.contentType] == "application/json")
#expect(body == #"{"status":"unavailable"}"#)
}
}
} catch {
try? await fluent.shutdown()
throw error
}
try await fluent.shutdown()
}
@Test
func `does not serve the readiness path without a probe`() async throws {
try await app(probe: nil).test(.router) { client in
try await client.execute(
uri: "/health/ready",
method: .get
) { response in
#expect(response.status == .notFound)
}
}
}
}
// MARK: - Helpers
private extension HealthControllerTests {
/// Builds a test application serving the ``HealthController`` routes for the given probe.
/// - Parameter probe: the probe supplied to the controller, or `nil` for liveness only.
/// - Returns: the configured test application.
func app(
probe: Probe?
) -> some ApplicationProtocol {
Application(router: {
let router = Router()
router.addRoutes(HealthController<BasicRequestContext>(probe: probe).routes)
return router
}())
}
}