packages feed

keiro-test-support 0.19.0.1 → 0.20.0.0

raw patch · 3 files changed

+100/−16 lines, 3 filesdep ~keiro-migrationsdep ~kiroku-storedep ~kiroku-store-migrationsPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: keiro-migrations, kiroku-store, kiroku-store-migrations

API changes (from Hackage documentation)

+ Keiro.Test.Postgres: withFreshResourceStoresSameDatabase :: Fixture -> (((KirokuStore, StoreRunner), (KirokuStore, StoreRunner)) -> IO ()) -> IO ()
+ Keiro.Test.Postgres: withFreshResourceStoresSameDatabaseWithConnection :: Fixture -> (Text -> ((KirokuStore, StoreRunner), (KirokuStore, StoreRunner)) -> IO ()) -> IO ()
+ Keiro.Test.Postgres: withMigratedBenchmarkSuite :: (Fixture -> IO a) -> IO a
- Keiro.Test.Postgres: withFreshResourceStore :: Fixture -> ((KirokuStore, StoreRunner) -> IO ()) -> IO ()
+ Keiro.Test.Postgres: withFreshResourceStore :: Fixture -> ((KirokuStore, StoreRunner) -> IO a) -> IO a
- Keiro.Test.Postgres: withFreshResourceStoreWith :: Fixture -> (ConnectionSettings -> ConnectionSettings) -> ((KirokuStore, StoreRunner) -> IO ()) -> IO ()
+ Keiro.Test.Postgres: withFreshResourceStoreWith :: Fixture -> (ConnectionSettings -> ConnectionSettings) -> ((KirokuStore, StoreRunner) -> IO a) -> IO a

Files

CHANGELOG.md view
@@ -6,6 +6,29 @@  ## [Unreleased] +## 0.20.0.0 — 2026-10-12++### Breaking Changes++- Require the Kiroku 0.11 cohort from+  mori://shinzui/kiroku/packages/kiroku-store; see the root changelog and+  upgrade blueprint for subscription constructors, decoding, and migration+  guidance.+- Require `kiroku-store-migrations ^>=0.7.1.0` from+  mori://shinzui/kiroku/packages/kiroku-store-migrations. Stop workers for+  migrations 0013 and 0014; plan the index build in 0015. The composed plan+  now contains 47 migrations.++### New Features++- Add `withFreshResourceStoresSameDatabase` and+  `withFreshResourceStoresSameDatabaseWithConnection` for independent resource+  stores sharing one isolated database.+- Add `withMigratedBenchmarkSuite` for durable benchmark databases. It enables+  fsync, synchronous commit, full-page writes and autovacuum locally, or uses an+  external server supplied through `KEIRO_GUARDED_CATALOG_PG_ADMIN` while+  creating and dropping only its uniquely named template and cloned databases.+ ## 0.19.0.1 — 2026-10-05  ### Other Changes
keiro-test-support.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: keiro-test-support-version: 0.19.0.1+version: 0.20.0.0 synopsis: Shared PostgreSQL test fixtures for Keiro test suites description:   Suite-level ephemeral-PostgreSQL fixtures shared by the Keiro test@@ -58,9 +58,9 @@     ephemeral-pg >=0.3.1 && <0.4,     hasql >=1.10 && <1.11,     hasql-pool >=1.2 && <1.5,-    keiro-migrations ^>=0.19.0.1,-    kiroku-store >=0.9.0.1 && <0.10,-    kiroku-store-migrations ^>=0.6.0.0,+    keiro-migrations ^>=0.20.0.0,+    kiroku-store ^>=0.11.0.0,+    kiroku-store-migrations ^>=0.7.1.0,     pg-migrate ^>=1.2.0.0,     stm >=2.5 && <2.6,     text >=2.1 && <2.2,
src/Keiro/Test/Postgres.hs view
@@ -25,6 +25,7 @@   ( Fixture,     withMigratedSuite,     withMigratedSuiteWith,+    withMigratedBenchmarkSuite,     withFreshDatabase,     withFreshStore,     withFreshStoreWith,@@ -33,6 +34,8 @@     withFreshResourceStoreWith,     withFreshResourceStorePrepared,     withFreshStores2,+    withFreshResourceStoresSameDatabase,+    withFreshResourceStoresSameDatabaseWithConnection,   ) where @@ -46,6 +49,7 @@ import Effectful (Eff, IOE, Limit (..), Persistence (..), UnliftStrategy (..), runEff, withEffToIO) import Effectful.Error.Static (Error, runErrorNoCallStack) import EphemeralPg qualified as Pg+import GHC.Clock (getMonotonicTimeNSec) import Hasql.Connection.Settings qualified as Conn import Hasql.Pool qualified as Pool import Hasql.Pool.Config qualified as Pool.Config@@ -57,12 +61,14 @@ import Kiroku.Store.Error (StoreError) import Kiroku.Store.Migrations qualified as Kiroku import System.Directory (createDirectoryIfMissing)+import System.Environment (lookupEnv) import System.Posix.User (getEffectiveUserID)  -- | A running, migrated suite fixture: one cached PostgreSQL server owning a -- single migrated template database, plus a counter for unique clone names. data Fixture = Fixture-  { server :: Pg.Database,+  { administrativeConnection :: Text,+    databaseConnection :: Text -> Text,     templateName :: Text,     nextId :: TVar Int   }@@ -111,13 +117,47 @@ withMigratedSuiteWith :: [MigrationComponent] -> (Fixture -> IO a) -> IO a withMigratedSuiteWith extraComponents action = do   config <- ephemeralPgConfig+  withConfiguredSuite config extraComponents action++-- | Durable benchmark databases. An exclusively leased remote server may be+-- supplied through KEIRO_GUARDED_CATALOG_PG_ADMIN, a libpq keyword connection+-- string. Only uniquely named databases created here are dropped. Ordinary+-- correctness fixtures retain their fast local test configuration.+withMigratedBenchmarkSuite :: (Fixture -> IO a) -> IO a+withMigratedBenchmarkSuite action =+  lookupEnv "KEIRO_GUARDED_CATALOG_PG_ADMIN" >>= \case+    Nothing -> do+      config <- ephemeralPgConfig+      let durable = config {Pg.postgresSettings = filter (\(key, _) -> key `notElem` ["fsync", "synchronous_commit", "full_page_writes", "wal_level"]) config.postgresSettings <> [("fsync", "on"), ("synchronous_commit", "on"), ("full_page_writes", "on"), ("wal_level", "replica")]}+      withConfiguredSuite durable [] action+    Just prefix -> do+      let connection = Text.pack prefix+      if Text.null connection || "postgres://" `Text.isPrefixOf` connection || "postgresql://" `Text.isPrefixOf` connection+        then fail "KEIRO_GUARDED_CATALOG_PG_ADMIN requires a libpq keyword connection string"+        else do+          unique <- getMonotonicTimeNSec+          let template = "keiro_bench_" <> Text.pack (show unique)+              connect name = connection <> " dbname=" <> name+          bracket+            ( do+                runSqlOn connection ("CREATE DATABASE " <> quoteIdentifier template)+                migrateTemplate [] (connect template)+                  `onException` runSqlOn connection ("DROP DATABASE " <> quoteIdentifier template <> " WITH (FORCE)")+                counter <- newTVarIO 0+                pure (Fixture connection connect template counter)+            )+            (\_ -> runSqlOn connection ("DROP DATABASE " <> quoteIdentifier template <> " WITH (FORCE)"))+            action++withConfiguredSuite :: Pg.Config -> [MigrationComponent] -> (Fixture -> IO a) -> IO a+withConfiguredSuite config extraComponents action = do   started <- Pg.startCached config Pg.defaultCacheConfig   case started of     Left err -> fail (Text.unpack (Pg.renderStartError err))     Right server ->       bracket         (setup server `onException` Pg.stop server)-        (\fixture -> Pg.stop fixture.server)+        (const (Pg.stop server))         action   where     setup server = do@@ -128,7 +168,7 @@       -- released before any clone, so the template has no active sessions when       -- PostgreSQL copies it.       migrateTemplate extraComponents templateConnStr-      pure (Fixture server templateDbName counter)+      pure (Fixture (Pg.connectionString server) (connectionStringFor server) templateDbName counter)  -- | Clone a fresh, empty, migrated database from the template, open a -- 'Store.KirokuStore' against it, run @action@, then drop the clone. The store@@ -159,7 +199,7 @@  -- | Like 'withFreshStore', but also supplies the resource-aware interpreter -- required by Keiro's transactional command runners.-withFreshResourceStore :: Fixture -> ((Store.KirokuStore, StoreRunner) -> IO ()) -> IO ()+withFreshResourceStore :: Fixture -> ((Store.KirokuStore, StoreRunner) -> IO a) -> IO a withFreshResourceStore fixture = withFreshResourceStoreWith fixture id  -- | Like 'withFreshResourceStore', with a connection-settings modifier for@@ -167,8 +207,8 @@ withFreshResourceStoreWith ::   Fixture ->   (Store.ConnectionSettings -> Store.ConnectionSettings) ->-  ((Store.KirokuStore, StoreRunner) -> IO ()) ->-  IO ()+  ((Store.KirokuStore, StoreRunner) -> IO a) ->+  IO a withFreshResourceStoreWith fixture modify action =   withFreshDatabase fixture \connStr ->     runResourceStore (modify (Store.defaultConnectionSettings connStr)) action@@ -189,8 +229,8 @@  runResourceStore ::   Store.ConnectionSettings ->-  ((Store.KirokuStore, StoreRunner) -> IO ()) ->-  IO ()+  ((Store.KirokuStore, StoreRunner) -> IO a) ->+  IO a runResourceStore settings action =   runEff $     withKirokuStore settings $ do@@ -201,6 +241,27 @@             StoreRunner (unlift . runErrorNoCallStack . runStoreResource)           ) +-- | Independent resource-aware pools sharing one migrated database. Unlike+-- 'withFreshStores2', this fixture exercises contention in the same event store.+withFreshResourceStoresSameDatabase ::+  Fixture ->+  (((Store.KirokuStore, StoreRunner), (Store.KirokuStore, StoreRunner)) -> IO ()) ->+  IO ()+withFreshResourceStoresSameDatabase fixture action =+  withFreshResourceStoresSameDatabaseWithConnection fixture (\_ -> action)++-- | Also expose the clone's connection string for an independent SQL observer+-- or controller. All supplied stores are closed before the clone is dropped.+withFreshResourceStoresSameDatabaseWithConnection ::+  Fixture ->+  (Text -> ((Store.KirokuStore, StoreRunner), (Store.KirokuStore, StoreRunner)) -> IO ()) ->+  IO ()+withFreshResourceStoresSameDatabaseWithConnection fixture action =+  withFreshDatabase fixture \connStr ->+    runResourceStore (Store.defaultConnectionSettings connStr) \first ->+      runResourceStore (Store.defaultConnectionSettings connStr) \second ->+        action connStr (first, second)+ -- | Like 'withFreshStore' but provides two independent migrated databases (and -- two stores) cloned from the same template — used by cross-context -- integration tests that need two isolated PostgreSQL databases.@@ -217,12 +278,12 @@ withFreshDatabase :: Fixture -> (Text -> IO a) -> IO a withFreshDatabase fixture action =   bracket create dropDb \dbName ->-    action (connectionStringFor fixture.server dbName)+    action (fixture.databaseConnection dbName)   where     create = do       n <- atomically $ stateTVar fixture.nextId \i -> (i + 1, i + 1)-      let dbName = "keiro_test_" <> Text.pack (show n)-      runSql fixture.server $+      let dbName = fixture.templateName <> "_" <> Text.pack (show n)+      runSqlOn fixture.administrativeConnection $         "CREATE DATABASE "           <> quoteIdentifier dbName           <> " TEMPLATE "@@ -230,7 +291,7 @@       pure dbName      dropDb dbName =-      runSql fixture.server $+      runSqlOn fixture.administrativeConnection $         "DROP DATABASE IF EXISTS " <> quoteIdentifier dbName <> " WITH (FORCE)"  migrateTemplate :: [MigrationComponent] -> Text -> IO ()