packages feed

preql-0.4: test/Test.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE OverloadedStrings        #-}

module Main where

import Test.Syntax.RoundTrip
import Test.Syntax.Parser
import Test.Syntax.Printer
import Test.Wire (badConnection, connectionString, wire)

import Preql
import Preql.Imports
import Preql.QuasiQuoter.Raw.TH as Raw
import Preql.QuasiQuoter.Syntax.Params as Syntax (AntiquoteState(..), numberAntiquotes)
import Preql.QuasiQuoter.Syntax.Syntax hiding (query, select)
import qualified Preql.QuasiQuoter.Syntax.Syntax as Syntax
import qualified Preql.Wire.Query as W

import Control.Exception (throwIO)
import Control.Monad.Trans.Reader (runReaderT)
import Data.Int
import GHC.TypeNats
import Prelude hiding (Ordering(..), lex)
import Test.Tasty
import Test.Tasty.HUnit

import qualified Data.Text as T
import qualified Database.PostgreSQL.LibPQ as PQ
import qualified Preql.QuasiQuoter.Raw.Lex as Raw

main :: IO ()
main = defaultMain $ testGroup "preql"
    [ antiquotes
    , wire
    , printer
    , lexer
    , parser
    , roundtrip
    , integration
    ]

integration :: TestTree
integration = withResource initDB PQ.finish $ \db ->
  let
    query' :: (ToSql p, FromSql r, KnownNat (Width r)) =>
      (Preql.Query (Width r), p) -> IO (Vector r)
    query' qp = runReaderT (query qp) =<< db
  in testGroup "integration"
    [ testCase "SELECT foo, bar FROM baz" $ do
        result <- query' [sql|SELECT foo, bar FROM baz |]
        assertEqual "" [(1, "one"), (2, "two")] (result :: Vector (Int32, T.Text))
    , testCase "with params" $ do
        result <- query' $ [select| SELECT foo, bar FROM baz WHERE foo = $1|] (1 :: Int32)
        assertEqual "" [(1, "one")] (result :: Vector (Int32, T.Text))
    , testCase "antiquote, 2 params" $ do
        let
            foo0 = 1 :: Int32
            bar0 = "one" :: T.Text
        result <- query' [select| SELECT foo, bar FROM baz WHERE foo = ${foo0} AND bar = ${bar0}|]
        assertEqual "" [(1, "one")] (result :: Vector (Int32, T.Text))
    , testCase "antiquote, 1 params" $ do
        let foo0 = 1 :: Int32
        result <- query' [select| SELECT foo, bar FROM baz WHERE foo = ${foo0}|]
        assertEqual "" [(1, "one")] (result :: Vector (Int32, T.Text))
    ]

initDB :: HasCallStack => IO PQ.Connection
initDB = do
    conn <- PQ.connectdb =<< connectionString
    status <- PQ.status conn
    unless (status == PQ.ConnectionOk) (throwIO =<< badConnection conn)
    let query' q = either throwIO return =<< W.query_ conn q ()
    query' "DROP TABLE IF EXISTS baz"
    query' "CREATE TABLE baz (foo int4, bar text)"
    query' "INSERT INTO baz (foo, bar) values (1, 'one'), (2, 'two')"
    return conn

antiquotes :: TestTree
antiquotes = testGroup "antiquotes"
    [ testCase "numberAntiquotes, Syntax" $
        assertEqual ""
            (QS (Simple Syntax.select
                 { from =  [ J (Table "baz") ]
                 , targetList = [ Column (CRef  "foo") Nothing, Column (CRef "bar") Nothing ]
                 , whereClause = Just (BinOp Eq (CRef "foo") (NumberedParam 1))
                 }), AntiquoteState 1 ["foo0"])
            (Syntax.numberAntiquotes 0 (QS (Simple Syntax.select
                 { from =  [ J (Table "baz") ]
                 , targetList = [ Column (CRef  "foo") Nothing, Column (CRef "bar") Nothing ]
                 , whereClause = Just (BinOp Eq (CRef "foo") (HaskellParam "foo0"))
                 })))
    , testCase "numberAntiquotes, Raw" $
        assertEqual ""
            ("SELECT foo, bar FROM baz WHERE foo = $1", ["foo0"])
            (Raw.numberAntiquotes 0 [ Raw.Sql "SELECT foo, bar FROM baz WHERE foo = ", Raw.HaskellParam "foo0" ])
    ]