orville-postgresql-1.1.0.0: test/Test/Expr/Aggregate.hs
module Test.Expr.Aggregate
( aggregateTests
)
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 qualified Hedgehog as HH
import qualified Orville.PostgreSQL as Orville
import qualified Orville.PostgreSQL.Execution as Execution
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
import qualified Test.Property as Property
data FooBar = FooBar
{ foo :: Int.Int32
, bar :: String
}
newtype DoubleRes = DoubleRes
{doubleRes :: Double}
aggregateTests :: Orville.ConnectionPool -> Property.Group
aggregateTests pool =
Property.group
"Expr - Aggregate"
[ prop_avgAggregate pool
, prop_maxAggregate pool
, prop_minAggregate pool
, prop_sumAggregate pool
]
prop_avgAggregate :: Property.NamedDBProperty
prop_avgAggregate =
aggregateFunctionTest "avgAggregateFunction computes simple average" $
AggregateTest
{ valuesToInsert = NE.fromList [FooBar 1 "dog", FooBar 2 "dingo", FooBar 3 "dog"]
, groupByExpectedQueryResults =
[ DoubleRes 2.00000
]
, aggregateFunctionExpr =
Expr.avgAggregateFunction
Nothing
(Expr.columnReference fooColumn)
Nothing
Nothing
}
prop_maxAggregate :: Property.NamedDBProperty
prop_maxAggregate =
aggregateFunctionTest "maxAggregateFunction computes maximum" $
AggregateTest
{ valuesToInsert = NE.fromList [FooBar 1 "dog", FooBar 2 "dingo", FooBar 3 "dog"]
, groupByExpectedQueryResults =
[ DoubleRes 3.0
]
, aggregateFunctionExpr =
Expr.maxAggregateFunction
Nothing
(Expr.columnReference fooColumn)
Nothing
Nothing
}
prop_minAggregate :: Property.NamedDBProperty
prop_minAggregate =
aggregateFunctionTest "minAggregateFunction computes minimum" $
AggregateTest
{ valuesToInsert = NE.fromList [FooBar 1 "dog", FooBar 2 "dingo", FooBar 3 "dog"]
, groupByExpectedQueryResults =
[ DoubleRes 1.0
]
, aggregateFunctionExpr =
Expr.minAggregateFunction
Nothing
(Expr.columnReference fooColumn)
Nothing
Nothing
}
prop_sumAggregate :: Property.NamedDBProperty
prop_sumAggregate =
aggregateFunctionTest "sumAggregateFunction computes simple sum" $
AggregateTest
{ valuesToInsert = NE.fromList [FooBar 1 "dog", FooBar 2 "dingo", FooBar 3 "dog"]
, groupByExpectedQueryResults =
[ DoubleRes 6.0
]
, aggregateFunctionExpr =
Expr.sumAggregateFunction
Nothing
(Expr.columnReference fooColumn)
Nothing
Nothing
}
data AggregateTest = AggregateTest
{ valuesToInsert :: NE.NonEmpty FooBar
, aggregateFunctionExpr :: Expr.ValueExpression
, groupByExpectedQueryResults :: [DoubleRes]
}
aggregateFunctionTest :: String -> AggregateTest -> Property.NamedDBProperty
aggregateFunctionTest testName test =
Property.singletonNamedDBProperty testName $ \pool -> do
rows <- MIO.liftIO . Conn.withPoolConnection pool $ \connection -> do
dropAndRecreateTestTable connection
RawSql.executeVoid connection $
Expr.insertExpr testTable Nothing (mkAggregateTestInsertSource test) Nothing Nothing
result <-
RawSql.execute connection $
Expr.queryExpr
(Expr.selectClause $ Expr.selectExpr Nothing)
(Expr.selectDerivedColumns . pure $ Expr.deriveColumnAs (aggregateFunctionExpr test) (RawSql.unsafeFromRawSql $ RawSql.fromString "agg"))
(Just $ Expr.tableExpr (Expr.singleTableReferenceList testTable) Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing)
Execution.readRows result
((fmap . fmap . fmap) SqlValue.toDouble rows) HH.=== ((fmap . fmap . fmap) SqlValue.toDouble $ mkAggregateTestExpectedRows test)
mkAggregateTestInsertSource :: AggregateTest -> Expr.InsertSource
mkAggregateTestInsertSource test =
let
mkRow foobar =
NE.fromList
[ Expr.valueExpression $ SqlValue.fromInt32 (foo foobar)
, Expr.valueExpression $ SqlValue.fromText (T.pack $ bar foobar)
]
in
Expr.valuesExprInsertSource
. Expr.valuesExprFromValueExpressions
$ fmap mkRow (valuesToInsert test)
mkAggregateTestExpectedRows :: AggregateTest -> [[(Maybe B8.ByteString, SqlValue.SqlValue)]]
mkAggregateTestExpectedRows test =
let
mkRow res =
[ (Just (B8.pack "agg"), SqlValue.fromDouble (doubleRes res))
]
in
fmap mkRow (groupByExpectedQueryResults test)
testTable :: Expr.QualifiedOrUnqualified Expr.TableName
testTable =
Expr.unqualified (Expr.tableName "expr_test")
fooColumn :: Expr.QualifiedOrUnqualified Expr.ColumnName
fooColumn =
Expr.unqualified (Expr.columnName "foo")
dropAndRecreateTestTable :: Orville.Connection -> IO ()
dropAndRecreateTestTable connection = do
RawSql.executeVoid connection (RawSql.fromString "DROP TABLE IF EXISTS " <> RawSql.toRawSql testTable)
RawSql.executeVoid connection (RawSql.fromString "CREATE TABLE " <> RawSql.toRawSql testTable <> RawSql.fromString "(foo INTEGER, bar TEXT)")