packages feed

kioku-migrations-0.1.0.0: test-support/Kioku/Migrations/TestSupport.hs

{-# LANGUAGE MultilineStrings #-}

module Kioku.Migrations.TestSupport
  ( withKiokuMigratedDatabase,
    withBareDatabase,
  )
where

import Data.Text (Text)
import Database.PostgreSQL.Migrate.Test qualified as PgMigrate
import EphemeralPg qualified
import Hasql.Connection qualified as Connection
import Hasql.Decoders qualified as D
import Hasql.Encoders qualified as E
import Hasql.Session qualified as Session
import Hasql.Statement (Statement, preparable)
import Kioku.Migrations (kiokuMigrationPlan)

withKiokuMigratedDatabase :: (Text -> IO a) -> IO a
withKiokuMigratedDatabase use = do
  plan <- either (fail . show) pure kiokuMigrationPlan
  result <-
    PgMigrate.withMigratedDatabase plan \connection -> do
      connectionStringResult <- Connection.use connection connectionStringSession
      connectionString <- either (fail . show) pure connectionStringResult
      use connectionString
  either (fail . show) pure result

-- | An ephemeral database with no migrations applied at all — not even keiro's
-- bootstrap. Tests that need to build a schema layout by hand (for instance, to
-- exercise a migration against a keiro cohort this package is not compiled
-- against) start from here.
withBareDatabase :: (Text -> IO a) -> IO a
withBareDatabase use = do
  result <- EphemeralPg.with (use . EphemeralPg.connectionString)
  either (fail . show) pure result

-- pg-migrate-test-support deliberately supplies a live Hasql connection so
-- assertions cannot inherit runner session state. Kioku's established shim
-- supplies a connection string instead, so recover the ephemeral server's
-- connection coordinates from that fresh callback session.
connectionStringSession :: Session.Session Text
connectionStringSession = Session.statement () connectionStringStatement

connectionStringStatement :: Statement () Text
connectionStringStatement =
  preparable
    """
    SELECT 'host=' || split_part(current_setting('unix_socket_directories'), ',', 1)
        || ' port=' || current_setting('port')
        || ' dbname=' || current_database()
        || ' user=' || current_user
    """
    E.noParams
    (D.singleRow (D.column (D.nonNullable D.text)))