packages feed

cqrs-postgresql-0.10.0: src-test/Data/CQRS/PostgreSQL/Internal/MigrationSpec.hs

{-# LANGUAGE OverloadedStrings #-}
module Data.CQRS.PostgreSQL.Internal.MigrationSpec
    ( mkApplyMigrationsSpec
    ) where

import           Control.Exception (bracket)
import           Data.ByteString (ByteString)
import           Data.CQRS.PostgreSQL.Internal.Utils (runQuery)
import           Data.CQRS.PostgreSQL.Internal.Migration (applyMigrations, uuid)
import           Data.Pool (Pool, withResource, destroyAllResources)
import           Data.UUID.Types (UUID)
import           Database.PostgreSQL.LibPQ (Connection)
import           Test.Hspec

-- Tests for Data.CQRS.PostgreSQL.Internal.Migration
mkApplyMigrationsSpec :: IO (Pool Connection) -> Spec
mkApplyMigrationsSpec mkConnectionPool = do
  describe "applyMigrations" $ do
    it "can apply a single migration" $ withConnectionPool $ \connectionPool -> do
      -- Apply the migration
      applyMigrations' connectionPool
        [ (uuid0, createXSql)
        ]
      -- Do a query which would fail without the migration
      assertValidQuery connectionPool selectFromX

    it "ignores migrations that have already been applied (single call)" $ withConnectionPool $ \connectionPool -> do
      -- Apply the migrations
      applyMigrations' connectionPool
        [ (uuid0, createXSql)
        , (uuid0, createXSql) -- Would fail if applied
        ]
      -- Do a query which would fail without at least one of the migrations being applied
      assertValidQuery connectionPool selectFromX

    it "ignores migrations that have already been applied (multiple calls)" $ withConnectionPool $ \connectionPool -> do
      -- Apply first migration
      applyMigrations' connectionPool
        [ (uuid0, createXSql)
        ]
      -- Apply second migration
      applyMigrations' connectionPool
        [ (uuid0, createXSql)
        ]
      -- Do a query which would fail without at least one of the migrations being applied
      assertValidQuery connectionPool selectFromX

    it "throws an error if SQL is changed for a given change set ID" $ withConnectionPool $ \connectionPool -> do
      -- Apply first migration
      applyMigrations' connectionPool
        [ (uuid0, createXSql)
        ]
      -- Apply second migration, which SHOULD fail because the SQL is different
      applyMigrations' connectionPool
        [ (uuid0, createXSqlBad)
        ] `shouldThrow` anyException

    it "can apply multiple distinct migrations in a single call" $ withConnectionPool $ \connectionPool -> do
      -- Apply both migrations
      applyMigrations' connectionPool
        [ (uuid0, createXSql)
        , (uuid1, createYSql)
        ]
      -- Do a query which would fail without at least one of the migrations being applied
      assertValidQuery connectionPool joinXY

  where
    withConnectionPool spec =
      bracket mkConnectionPool destroyAllResources spec

    assertValidQuery connectionPool sql = do
      rows <- runQuery connectionPool sql []
      rows `shouldSatisfy` (\rs -> length rs >= 0) -- Don't care about size of result, just that query succeeded

    createXSql = "CREATE TABLE X (A INT)"
    createYSql = "CREATE TABLE Y (B INT)"
    createXSqlBad = "CREATE TABLE X (Y CHAR(1))"

    selectFromX = "SELECT * FROM X"
    joinXY = "SELECT * FROM X, Y where X.A = Y.B"

    uuid0 = uuid "a328156d-9875-4471-8192-0c86959badb3"
    uuid1 = uuid "00c6159c-c7f6-4cec-b63f-f70c1c4c7bb1"

applyMigrations' :: Pool Connection -> [(UUID, ByteString)] -> IO ()
applyMigrations' connectionPool migrations = do
  withResource connectionPool $ \connection ->
      applyMigrations connection migrations