module Test.Expr.Where ( whereTests ) where import qualified Control.Monad.IO.Class as MIO import Data.Int (Int32) import Data.List.NonEmpty (NonEmpty ((:|))) 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 (FooBar (..), assertEqualFooBarRows, barColumn, barColumnRef, dropAndRecreateTestTable, fooBarTable, fooColumn, fooColumnRef, insertFooBarSource, mkFooBar) import qualified Test.Property as Property whereTests :: Orville.ConnectionPool -> Property.Group whereTests pool = Property.group "Expr - WhereClause" $ [ prop_noWhereClauseSpecified pool , prop_equalsOp pool , prop_greaterThanOp pool , prop_greaterThanOrEqualsOp pool , prop_lessThanOp pool , prop_lessThanOrEqualsToOp pool , prop_andExpr pool , prop_orExpr pool , prop_valueIn pool , prop_valueNotIn pool , prop_tupleIn pool , prop_tupleNotIn pool ] prop_noWhereClauseSpecified :: Property.NamedDBProperty prop_noWhereClauseSpecified = whereConditionTest "Returns all rows when where clause is specified" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "ant", mkFooBar 2 "bee", mkFooBar 3 "chihuahua"] , whereExpectedQueryResults = [mkFooBar 1 "ant", mkFooBar 2 "bee", mkFooBar 3 "chihuahua"] , whereClause = Nothing } prop_equalsOp :: Property.NamedDBProperty prop_equalsOp = whereConditionTest "equalsOp matches exact value" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "ant", mkFooBar 2 "bee", mkFooBar 3 "chihuahua"] , whereExpectedQueryResults = [mkFooBar 2 "bee"] , whereClause = Just . Expr.whereClause $ Expr.equals fooColumnRef (int32ValueExpr 2) } prop_greaterThanOp :: Property.NamedDBProperty prop_greaterThanOp = whereConditionTest "greaterThanOp matches greater values" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "ant", mkFooBar 2 "bee", mkFooBar 3 "chihuahua"] , whereExpectedQueryResults = [mkFooBar 3 "chihuahua"] , whereClause = Just . Expr.whereClause $ Expr.greaterThan fooColumnRef (int32ValueExpr 2) } prop_greaterThanOrEqualsOp :: Property.NamedDBProperty prop_greaterThanOrEqualsOp = whereConditionTest "greaterThanOrEqualsOp matches greater or equal values" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "ant", mkFooBar 2 "bee", mkFooBar 3 "chihuahua"] , whereExpectedQueryResults = [mkFooBar 2 "bee", mkFooBar 3 "chihuahua"] , whereClause = Just . Expr.whereClause $ Expr.greaterThanOrEqualTo fooColumnRef (int32ValueExpr 2) } prop_lessThanOp :: Property.NamedDBProperty prop_lessThanOp = whereConditionTest "lessThanOp matches lesser values" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "ant", mkFooBar 2 "bee", mkFooBar 3 "chihuahua"] , whereExpectedQueryResults = [mkFooBar 1 "ant"] , whereClause = Just . Expr.whereClause $ Expr.lessThan fooColumnRef (int32ValueExpr 2) } prop_lessThanOrEqualsToOp :: Property.NamedDBProperty prop_lessThanOrEqualsToOp = whereConditionTest "lessThanOrEqualsOp matches lesser or equal values" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "ant", mkFooBar 2 "bee", mkFooBar 3 "chihuahua"] , whereExpectedQueryResults = [mkFooBar 1 "ant", mkFooBar 2 "bee"] , whereClause = Just . Expr.whereClause $ Expr.lessThanOrEqualTo fooColumnRef (int32ValueExpr 2) } prop_andExpr :: Property.NamedDBProperty prop_andExpr = whereConditionTest "andExpr requires both conditions to be true" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "dog", mkFooBar 2 "dingo", mkFooBar 3 "dog"] , whereExpectedQueryResults = [mkFooBar 3 "dog"] , whereClause = Just . Expr.whereClause $ Expr.andExpr (Expr.equals fooColumnRef (int32ValueExpr 3)) (Expr.equals barColumnRef (textValueExpr "dog")) } prop_orExpr :: Property.NamedDBProperty prop_orExpr = whereConditionTest "orExpr requires either conditions to be true" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "dog", mkFooBar 2 "dingo", mkFooBar 3 "dog"] , whereExpectedQueryResults = [mkFooBar 2 "dingo", mkFooBar 3 "dog"] , whereClause = Just . Expr.whereClause $ Expr.orExpr (Expr.equals fooColumnRef (int32ValueExpr 3)) (Expr.equals barColumnRef (textValueExpr "dingo")) } prop_valueIn :: Property.NamedDBProperty prop_valueIn = whereConditionTest "valueIn requires the column's value to be in the list" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "dog", mkFooBar 2 "dingo", mkFooBar 3 "dog"] , whereExpectedQueryResults = [mkFooBar 1 "dog", mkFooBar 3 "dog"] , whereClause = Just . Expr.whereClause $ Expr.valueIn barColumnRef (textValueExpr "dog" :| [textValueExpr "cat"]) } prop_valueNotIn :: Property.NamedDBProperty prop_valueNotIn = whereConditionTest "valueNotIn requires the column's value to not be in the list" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "dog", mkFooBar 2 "dingo", mkFooBar 3 "dog"] , whereExpectedQueryResults = [mkFooBar 2 "dingo"] , whereClause = Just . Expr.whereClause $ Expr.valueNotIn barColumnRef (textValueExpr "dog" :| [textValueExpr "cat"]) } prop_tupleIn :: Property.NamedDBProperty prop_tupleIn = whereConditionTest "tupleIn requires the column value combination to be in the list" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "dog", mkFooBar 2 "dingo", mkFooBar 3 "dog"] , whereExpectedQueryResults = [mkFooBar 1 "dog", mkFooBar 2 "dingo"] , whereClause = Just . Expr.whereClause $ Expr.tupleIn (fooColumnRef :| [barColumnRef]) ( (int32ValueExpr 1 :| [textValueExpr "dog"]) :| [int32ValueExpr 2 :| [textValueExpr "dingo"]] ) } prop_tupleNotIn :: Property.NamedDBProperty prop_tupleNotIn = whereConditionTest "tupleNotIn requires the column value combination to not be in the list" $ WhereConditionTest { whereValuesToInsert = [mkFooBar 1 "dog", mkFooBar 2 "dingo", mkFooBar 3 "dog"] , whereExpectedQueryResults = [mkFooBar 3 "dog"] , whereClause = Just . Expr.whereClause $ Expr.tupleNotIn (fooColumnRef :| [barColumnRef]) ( (int32ValueExpr 1 :| [textValueExpr "dog"]) :| [int32ValueExpr 2 :| [textValueExpr "dingo"]] ) } int32ValueExpr :: Int32 -> Expr.ValueExpression int32ValueExpr = Expr.valueExpression . SqlValue.fromInt32 textValueExpr :: String -> Expr.ValueExpression textValueExpr = Expr.valueExpression . SqlValue.fromText . T.pack data WhereConditionTest = WhereConditionTest { whereValuesToInsert :: [FooBar] , whereClause :: Maybe Expr.WhereClause , whereExpectedQueryResults :: [FooBar] } whereConditionTest :: String -> WhereConditionTest -> Property.NamedDBProperty whereConditionTest testName test = Property.singletonNamedDBProperty testName $ \pool -> do rows <- MIO.liftIO $ Conn.withPoolConnection pool $ \connection -> do dropAndRecreateTestTable connection RawSql.executeVoid connection $ Expr.insertExpr fooBarTable Nothing (insertFooBarSource $ whereValuesToInsert test) Nothing result <- RawSql.execute connection $ Expr.queryExpr (Expr.selectClause $ Expr.selectExpr Nothing) (Expr.selectColumns [fooColumn, barColumn]) (Just $ Expr.tableExpr (Expr.referencesTable fooBarTable) (whereClause test) Nothing Nothing Nothing Nothing) Execution.readRows result assertEqualFooBarRows rows (whereExpectedQueryResults test)