orville-postgresql-1.1.0.0: test/Test/Expr/TestSchema.hs
module Test.Expr.TestSchema
( FooBar (..)
, mkFooBar
, findAllFooBars
, findAllFooBarsInTable
, fooBarTable
, fooColumn
, fooColumnRef
, barColumn
, barColumnRef
, encodeFooBar
, orderByFoo
, insertFooBarSource
, withFooBarData
, dropAndRecreateTestTable
, assertEqualFooBarRows
, assertEqualSqlRows
, sqlRowsToText
)
where
import qualified Control.Monad.IO.Class as MIO
import qualified Data.ByteString.Char8 as B8
import qualified Data.Int as Int
import qualified Data.List.NonEmpty as NE
import qualified Data.Text as T
import GHC.Stack (HasCallStack, withFrozenCallStack)
import Hedgehog ((===))
import qualified Hedgehog as HH
import qualified Orville.PostgreSQL as Orville
import qualified Orville.PostgreSQL.Expr as Expr
import qualified Orville.PostgreSQL.Raw.Connection as Conn
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
import qualified Orville.PostgreSQL.Raw.SqlValue as SqlValue
data FooBar = FooBar
{ foo :: Maybe Int.Int32
, bar :: Maybe String
}
-- Smart constructor for the common case when both fields are not null
mkFooBar :: Int.Int32 -> String -> FooBar
mkFooBar f b = FooBar (Just f) (Just b)
fooBarTable :: Expr.QualifiedOrUnqualified Expr.TableName
fooBarTable =
Expr.unqualified (Expr.tableName "foobar")
fooColumn :: Expr.QualifiedOrUnqualified Expr.ColumnName
fooColumn =
Expr.unqualified $ Expr.columnName "foo"
fooColumnRef :: Expr.ValueExpression
fooColumnRef =
Expr.columnReference fooColumn
barColumn :: Expr.QualifiedOrUnqualified Expr.ColumnName
barColumn =
Expr.unqualified $ Expr.columnName "bar"
barColumnAliased :: Expr.Qualified Expr.ColumnName
barColumnAliased =
Expr.aliasQualifyColumn (Expr.stringToAliasExpr "b") $ Expr.columnName "bar"
barColumnRef :: Expr.ValueExpression
barColumnRef =
Expr.columnReference barColumn
orderByFoo :: Expr.OrderByClause
orderByFoo =
Expr.orderByClause $
Expr.orderByColumnName fooColumn Expr.ascendingOrder
findAllFooBars :: Expr.QueryExpr
findAllFooBars =
findAllFooBarsInTable fooBarTable
findAllFooBarsInTable :: Expr.QualifiedOrUnqualified Expr.TableName -> Expr.QueryExpr
findAllFooBarsInTable tableName =
let
tableRefList =
Expr.tableReferenceList
[ Expr.tableNameReference tableName (Just (Expr.stringToAliasExpr "b"))
]
in
Expr.queryExpr
(Expr.selectClause $ Expr.selectExpr Nothing)
(Expr.selectColumns [fooColumn, Expr.untrackQualified barColumnAliased])
(Just $ Expr.tableExpr tableRefList Nothing Nothing (Just orderByFoo) Nothing Nothing Nothing Nothing Nothing)
encodeFooBar :: FooBar -> [(Maybe B8.ByteString, SqlValue.SqlValue)]
encodeFooBar fooBar =
[ (Just (B8.pack "foo"), nullOr SqlValue.fromInt32 (foo fooBar))
, (Just (B8.pack "bar"), nullOr SqlValue.fromText (T.pack <$> bar fooBar))
]
insertFooBarSource :: NE.NonEmpty FooBar -> Expr.InsertSource
insertFooBarSource fooBars =
let
mkRow fooBar =
NE.fromList
[ Expr.valueExpression $ nullOr SqlValue.fromInt32 (foo fooBar)
, Expr.valueExpression $ nullOr SqlValue.fromText (T.pack <$> bar fooBar)
]
in
Expr.valuesExprInsertSource
. Expr.valuesExprFromValueExpressions
$ fmap mkRow fooBars
nullOr :: (a -> SqlValue.SqlValue) -> Maybe a -> SqlValue.SqlValue
nullOr = maybe SqlValue.sqlNull
withFooBarData ::
Orville.ConnectionPool ->
NE.NonEmpty FooBar ->
(Orville.Connection -> IO a) ->
HH.PropertyT IO a
withFooBarData pool fooBars action =
MIO.liftIO $
Conn.withPoolConnection pool $ \connection -> do
dropAndRecreateTestTable connection
RawSql.executeVoid connection $
Expr.insertExpr fooBarTable Nothing (insertFooBarSource fooBars) Nothing Nothing
action connection
dropAndRecreateTestTable :: Orville.Connection -> IO ()
dropAndRecreateTestTable connection = do
RawSql.executeVoid connection (RawSql.fromString "DROP TABLE IF EXISTS " <> RawSql.toRawSql fooBarTable)
RawSql.executeVoid connection (RawSql.fromString "CREATE TABLE " <> RawSql.toRawSql fooBarTable <> RawSql.fromString "(foo INTEGER, bar TEXT)")
assertEqualFooBarRows ::
(HH.MonadTest m, HasCallStack) =>
[[(Maybe B8.ByteString, SqlValue.SqlValue)]] ->
[FooBar] ->
m ()
assertEqualFooBarRows rows fooBars =
withFrozenCallStack $
assertEqualSqlRows rows (map encodeFooBar fooBars)
-- SqlValue doesn't have Show or Eq, so use this to compare them in tests
assertEqualSqlRows ::
(Show a, Eq a, HH.MonadTest m, HasCallStack) =>
[[(a, SqlValue.SqlValue)]] ->
[[(a, SqlValue.SqlValue)]] ->
m ()
assertEqualSqlRows l r =
withFrozenCallStack $
sqlRowsToText l === sqlRowsToText r
sqlRowsToText :: [[(a, SqlValue.SqlValue)]] -> [[(a, Either String T.Text)]]
sqlRowsToText = fmap (fmap (\(a, b) -> (a, SqlValue.toText b)))