import Foundation import Logging import SQLKit import Testing @testable import Persistence @Suite( "Service method", .tags(.method) ) struct ServiceTests { // MARK: Methods tests @Test func `registers an SQLite database as the default for the in-memory driver`() async throws { let service = Service( driver: .inMemory, logger: Logger(label: "test") ) let fluent = try service() let database = fluent.db() as? any SQLDatabase try await fluent.shutdown() let dialect = try #require(database?.dialect) #expect(dialect.name == "sqlite") } @Test func `registers a PostgreSQL database as the default for the postgres driver`() async throws { // Resolving the default database opens no connection — pooling is lazy — so no server // needs to be listening on the configured host and port. let service = Service( driver: .postgres( .init( host: "127.0.0.1", port: 5432, name: "loud", username: "loud", password: "loud", tls: .off, maxConnectionsPerEventLoop: 1 ) ), logger: Logger(label: "test") ) let fluent = try service() let database = fluent.db() as? any SQLDatabase try await fluent.shutdown() let dialect = try #require(database?.dialect) #expect(dialect.name == "postgresql") } @Test func `builds a usable in-memory database`() async throws { let service = Service( driver: .inMemory, logger: Logger(label: "test") ) let fluent = try service() do { let database = try #require(fluent.db() as? any SQLDatabase) try await database.raw("SELECT 1").run() } catch { try? await fluent.shutdown() throw error } try await fluent.shutdown() } @Test("in-memory: migrate, insert, read back") func inMemoryRoundTrip() async throws { try await roundTrip(driver: .inMemory) } @Test( "postgres: migrate, insert, read back", .enabled(if: postgresDriver != nil) ) func postgresRoundTrip() async throws { try await roundTrip( driver: postgresDriver!, revertAfter: true ) } } // MARK: - Helpers private extension ServiceTests { /// Migrates, inserts, and reads back a record against the given driver, shutting the pool down after. /// /// The `Fluent` service normally owns pool shutdown via its `run()` in the service group; outside that, /// the test must shut it down explicitly — even on failure — or the pool asserts on `deinit`. /// - Parameters: /// - driver: the persistence backend to exercise. /// - revertAfter: whether to revert the migrations afterwards; set for a shared database (the /// in-memory database is discarded on shutdown, so it needs no revert). func roundTrip( driver: Persistence.Driver, revertAfter: Bool = false ) async throws { let prepareDB = PrepareDB() let service = Service( driver: driver, logger: Logger(label: "test") ) let fluent = try service() do { await prepareDB(for: fluent) try await fluent.migrate() let repository = ExampleRepository(fluent: fluent) let created = try await repository.create(name: "loud") #expect(try await repository.all().contains(created)) if revertAfter { try await fluent.revert() } } catch { try? await fluent.shutdown() throw error } try await fluent.shutdown() } } /// The PostgreSQL driver built from the `POSTGRES_TEST_*` environment variables, or `nil` when the gate /// variable `POSTGRES_TEST_HOST` is unset — in which case the PostgreSQL integration test is skipped, so /// the suite stays runnable with no database available. private let postgresDriver: Persistence.Driver? = { let environment = ProcessInfo.processInfo.environment guard let host = environment["POSTGRES_TEST_HOST"] else { return nil } return .postgres( .init( host: host, port: environment["POSTGRES_TEST_PORT"].flatMap(Int.init) ?? 5432, name: environment["POSTGRES_TEST_NAME"] ?? "loud", username: environment["POSTGRES_TEST_USERNAME"] ?? "loud", password: environment["POSTGRES_TEST_PASSWORD"] ?? "loud", tls: .off, maxConnectionsPerEventLoop: 2 ) ) }()