packages feed

orville-postgresql-1.1.0.0: test/Test/Expr/GroupByOrderBy.hs

module Test.Expr.GroupByOrderBy
  ( groupByOrderByTests
  )
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 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 Test.Expr.TestSchema (assertEqualSqlRows)
import qualified Test.Property as Property

data FooBar = FooBar
  { foo :: Int.Int32
  , bar :: String
  }

groupByOrderByTests :: Orville.ConnectionPool -> Property.Group
groupByOrderByTests pool =
  Property.group
    "Expr - GroupBy and OrderBy"
    [ prop_groupByOrderByExpr pool
    ]

prop_groupByOrderByExpr :: Property.NamedDBProperty
prop_groupByOrderByExpr =
  groupByOrderByTest "GroupBy and OrderBy clauses can be used together" $
    GroupByOrderByTest
      { valuesToInsert = NE.fromList [FooBar 1 "shiba", FooBar 2 "dingo", FooBar 1 "dog", FooBar 2 "dingo", FooBar 1 "shiba"]
      , expectedQueryResults = [FooBar 2 "dingo", FooBar 1 "dog", FooBar 1 "shiba"]
      , groupByClause =
          Just . Expr.groupByClause $
            Expr.appendGroupByExpr
              (Expr.groupByColumnsExpr . pure $ barColumn)
              (Expr.groupByColumnsExpr . pure $ fooColumn)
      , orderByClause =
          Just . Expr.orderByClause $
            Expr.orderByColumnName barColumn Expr.ascendingOrder
      }

data GroupByOrderByTest = GroupByOrderByTest
  { valuesToInsert :: NE.NonEmpty FooBar
  , groupByClause :: Maybe Expr.GroupByClause
  , orderByClause :: Maybe Expr.OrderByClause
  , expectedQueryResults :: [FooBar]
  }

mkGroupByOrderByTestInsertSource :: GroupByOrderByTest -> Expr.InsertSource
mkGroupByOrderByTestInsertSource 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

mkGroupByOrderByTestExpectedRows :: GroupByOrderByTest -> [[(Maybe B8.ByteString, SqlValue.SqlValue)]]
mkGroupByOrderByTestExpectedRows test =
  let
    mkRow foobar =
      [ (Just (B8.pack "foo"), SqlValue.fromInt32 (foo foobar))
      , (Just (B8.pack "bar"), SqlValue.fromText (T.pack $ bar foobar))
      ]
  in
    fmap mkRow (expectedQueryResults test)

groupByOrderByTest :: String -> GroupByOrderByTest -> Property.NamedDBProperty
groupByOrderByTest testName test =
  Property.singletonNamedDBProperty testName $ \pool -> do
    rows <- MIO.liftIO . Conn.withPoolConnection pool $ \connection -> do
      dropAndRecreateTestTable connection

      RawSql.executeVoid connection $
        Expr.insertExpr testTable Nothing (mkGroupByOrderByTestInsertSource test) Nothing Nothing

      result <-
        RawSql.execute connection $
          Expr.queryExpr
            (Expr.selectClause $ Expr.selectExpr Nothing)
            (Expr.selectColumns [fooColumn, barColumn])
            (Just $ Expr.tableExpr (Expr.singleTableReferenceList testTable) Nothing (groupByClause test) (orderByClause test) Nothing Nothing Nothing Nothing Nothing)

      Execution.readRows result

    rows `assertEqualSqlRows` mkGroupByOrderByTestExpectedRows test

testTable :: Expr.QualifiedOrUnqualified Expr.TableName
testTable =
  Expr.unqualified (Expr.tableName "expr_test")

fooColumn :: Expr.QualifiedOrUnqualified Expr.ColumnName
fooColumn =
  Expr.unqualified (Expr.columnName "foo")

barColumn :: Expr.QualifiedOrUnqualified Expr.ColumnName
barColumn =
  Expr.unqualified (Expr.columnName "bar")

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)")