orville-postgresql-1.0.0.0: test/Test/PostgreSQLAxioms.hs
module Test.PostgreSQLAxioms
( postgreSQLAxiomTests
)
where
import qualified Control.Exception.Safe as ExSafe
import qualified Control.Monad.IO.Class as MIO
import qualified Data.ByteString.Char8 as B8
import Hedgehog ((===))
import qualified Hedgehog as HH
import qualified Orville.PostgreSQL as Orville
import qualified Orville.PostgreSQL.Marshall.SqlType as SqlType
import qualified Orville.PostgreSQL.Raw.Connection as Conn
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
import qualified Test.Property as Property
postgreSQLAxiomTests :: Orville.ConnectionPool -> Property.Group
postgreSQLAxiomTests pool =
Property.group
"PostgreSQL Axioms"
[ prop_smallIntegerBounds pool
, prop_integerBounds pool
, prop_bigIntegerBounds pool
]
prop_smallIntegerBounds :: Property.NamedDBProperty
prop_smallIntegerBounds =
Property.singletonNamedDBProperty "smallinteger bounds match Haskell Int16" $ \pool -> do
assertPostgreSQLMinValueMatchesHaskell pool SqlType.smallInteger
assertPostgreSQLMaxValueMatchesHaskell pool SqlType.smallInteger
prop_integerBounds :: Property.NamedDBProperty
prop_integerBounds =
Property.singletonNamedDBProperty "integer bounds match Haskell Int32" $ \pool -> do
assertPostgreSQLMinValueMatchesHaskell pool SqlType.integer
assertPostgreSQLMaxValueMatchesHaskell pool SqlType.integer
prop_bigIntegerBounds :: Property.NamedDBProperty
prop_bigIntegerBounds =
Property.singletonNamedDBProperty "bigInteger bounds match Haskell Int64" $ \pool -> do
assertPostgreSQLMinValueMatchesHaskell pool SqlType.bigInteger
assertPostgreSQLMaxValueMatchesHaskell pool SqlType.bigInteger
assertPostgreSQLMinValueMatchesHaskell ::
(Eq a, Show a, Bounded a, HH.MonadTest m, MIO.MonadIO m, ExSafe.MonadCatch m) =>
Orville.ConnectionPool ->
SqlType.SqlType a ->
m ()
assertPostgreSQLMinValueMatchesHaskell pool sqlType = do
-- First check that the min value can be selected via PostgreSQL
result <- HH.evalEitherM $ selectValue pool minBound sqlType
result === minBound
-- Then check that minvalue - 1 gives a numeric overflow error, verifying that
-- the min value haskell is, in fact, the minimum value in PostgreSQL
err <- evalEitherMLeft $ selectValueWithModifier pool minBound (RawSql.fromString "-1") sqlType
Conn.sqlExecutionErrorSqlState err === Just (B8.pack "22003")
assertPostgreSQLMaxValueMatchesHaskell ::
(Eq a, Show a, Bounded a, HH.MonadTest m, MIO.MonadIO m, ExSafe.MonadCatch m) =>
Orville.ConnectionPool ->
SqlType.SqlType a ->
m ()
assertPostgreSQLMaxValueMatchesHaskell pool sqlType = do
-- First check that the max value can be selected via PostgreSQL
result <- HH.evalEitherM $ selectValue pool maxBound sqlType
result === maxBound
-- Then check that maxvalue - 1 gives a numeric overflow error, verifying that
-- the max value haskell is, in fact, the maximum value in PostgreSQL
err <- evalEitherMLeft $ selectValueWithModifier pool maxBound (RawSql.fromString "+1") sqlType
Conn.sqlExecutionErrorSqlState err === Just (B8.pack "22003")
evalEitherMLeft :: (HH.MonadTest m, ExSafe.MonadCatch m, Show b) => m (Either a b) -> m a
evalEitherMLeft =
let
swap :: Either a b -> Either b a
swap (Left a) = Right a
swap (Right b) = Left b
in
HH.evalEitherM . fmap swap
selectValue ::
(HH.MonadTest m, MIO.MonadIO m) =>
Orville.ConnectionPool ->
a ->
SqlType.SqlType a ->
m (Either Conn.SqlExecutionError a)
selectValue pool inputValue sqlType =
selectValueWithModifier pool inputValue mempty sqlType
selectValueWithModifier ::
(HH.MonadTest m, MIO.MonadIO m) =>
Orville.ConnectionPool ->
a ->
RawSql.RawSql ->
SqlType.SqlType a ->
m (Either Conn.SqlExecutionError a)
selectValueWithModifier pool inputValue modifier sqlType = do
let
selectOne =
RawSql.fromString "SELECT (("
<> RawSql.parameter (SqlType.sqlTypeToSql sqlType inputValue)
<> modifier
<> RawSql.fromString ")"
<> RawSql.fromString "::"
<> RawSql.toRawSql (SqlType.sqlTypeExpr sqlType)
<> RawSql.fromString ") as result"
marshaller =
Orville.annotateSqlMarshallerEmptyAnnotation $
Orville.marshallField id (Orville.fieldOfType sqlType "result")
errOrResults <-
HH.evalIO . Orville.runOrville pool . ExSafe.try $
Orville.executeAndDecode Orville.SelectQuery selectOne marshaller
case errOrResults of
Left err ->
pure (Left err)
Right [outputValue] ->
pure (Right outputValue)
Right results -> do
HH.annotate $ "Expected exactly one result row from query, but got " <> show (length results)
HH.failure