yeshql-postgresql-simple-4.1.0.2: tests/Database/YeshQL/PostgreSQL/Tests.hs
{-#LANGUAGE QuasiQuotes #-}
{-#LANGUAGE RankNTypes #-}
{-#LANGUAGE LambdaCase #-}
{-#LANGUAGE TemplateHaskell #-}
module Database.YeshQL.PostgreSQL.Tests
( tests
)
where
import Test.Tasty
import Test.Tasty.HUnit
import Database.PostgreSQL.Simple
import Database.PostgreSQL.Simple.FromRow
import Database.YeshQL.PostgreSQL
import System.IO
import Data.Char
import Data.List (dropWhile, dropWhileEnd)
data User = User
{ userID :: Int
, userName :: String
} deriving (Show, Eq)
instance FromRow User where
fromRow = User <$> field <*> field
data Person = Person
{ personName :: String
, personEmail :: String
} deriving (Show, Eq)
data UserData = UserData
{ user :: User
, person :: Person
} deriving (Show, Eq)
instance FromRow UserData where
fromRow =
UserData
<$> (User <$> field <*> field)
<*> (Person <$> field <*> field)
tests conn =
map
($ conn)
[ testSimpleSelect
, testSimpleSelectStr
, testParametrizedSelect
, testSingleInsert
, testRecordReturn
, testRecordReturnComplex
, testRecordReturnExcessive
, testTupleReturnMany
, testRecordReturnMany
, testRecordParams
, testUpdateReturnRowCount
, testMultiQuery
, testQueryFromFile
, testQueryFromFileAutoName
, testDDL
]
testSimpleSelect :: Connection -> TestTree
testSimpleSelect conn = testCase "Simple SELECT" $ do
results <- [yesh|
-- name:getUserByName :: (String)
SELECT username FROM users|] conn
return ()
testEmptyReturn :: Connection -> TestTree
testEmptyReturn conn = testCase "Simple SELECT (empty return)" $ do
[yesh|
-- name:getUserByName :: ()
SELECT username FROM users WHERE 0|] conn
testSimpleSelectStr :: Connection -> TestTree
testSimpleSelectStr conn = testCase "Simple SELECT (expr by string)" $ do
results <- $(yesh $ unlines
[ "-- name:getUserByName :: (String)"
, "SELECT username FROM users"
]) conn
return ()
testParametrizedSelect :: Connection -> TestTree
testParametrizedSelect conn = testCase "Parametrized SELECT" $ do
actual <- [yesh|
-- name:getUserByName :: (Integer, String)
-- :username :: String
SELECT id, username FROM users WHERE username = :username LIMIT 1|] conn "billy"
let expected :: Maybe (Integer, String)
expected = Just (1, "billy")
assertEqual "" expected actual
testRecordReturn :: Connection -> TestTree
testRecordReturn conn = testCase "Return record from SELECT" $ do
actual <- [yesh|
-- name:getUserByName :: User
-- :username :: String
SELECT id, username FROM users WHERE username = :username LIMIT 1|]
conn
"billy"
let expected :: Maybe User
expected = Just $ User 1 "billy"
assertEqual "" expected actual
testRecordReturnComplex :: Connection -> TestTree
testRecordReturnComplex conn = testCase "Return record from SELECT" $ do
actual <- [yesh|
-- name:getUserByName :: UserData
-- :username :: String
SELECT id, username, person_name, email FROM users WHERE username = :username and id = 1|]
conn
"billy"
let expected :: Maybe UserData
expected = Just $ UserData (User 1 "billy") (Person "Billy" "billy@example.org")
assertEqual "" expected actual
testRecordReturnExcessive :: Connection -> TestTree
testRecordReturnExcessive conn = testCase "Return record from SELECT (extra values ignored)" $ do
actual <- [yesh|
-- name:getUserByName :: User
-- :username :: String
SELECT id, username FROM users WHERE username = :username LIMIT 1|]
conn
"billy"
let expected :: Maybe User
expected = Just $ User 1 "billy"
assertEqual "" expected actual
testTupleReturnMany :: Connection -> TestTree
testTupleReturnMany conn = testCase "Return a list of single-element tuples" $ do
actual <- [yesh|
-- name:getUsers :: [(String)]
select username from Users|] conn
let expected = ["billy", "billy"]
assertEqual "" expected actual
testRecordReturnMany :: Connection -> TestTree
testRecordReturnMany conn = testCase "Return a list of records from SELECT" $ do
actual <- [yesh|
-- name:getUsers :: [User]
select id, username from Users|] conn
let expected = [User 1 "billy", User 2 "billy"]
assertEqual "" expected actual
testRecordParams :: Connection -> TestTree
testRecordParams conn = testCase "Pass records as params" $ do
actual <- [yesh|
-- name:getUserByName :: User
-- :user :: User
SELECT id, username FROM users WHERE username = :user.userName.reverse LIMIT 1|]
conn
(User 10 "yllib")
let expected :: Maybe User
expected = Just $ User 1 "billy"
assertEqual "" expected actual
testSingleInsert :: Connection -> TestTree
testSingleInsert conn = testCase "Single INSERT" $ do
actual <- [yesh|
-- name:createUser :: (Integer)
-- :username :: String
INSERT INTO users (username) VALUES (:username) RETURNING id|] conn "billy"
let expected :: Maybe Integer
expected = Just 2
assertEqual "" expected actual
testUpdateReturnRowCount :: Connection -> TestTree
testUpdateReturnRowCount conn = testCase "UPDATE, get row count" $ do
actual <- [yesh|
-- name:renameUser :: rowcount Integer
-- :oldName :: String
-- :newName :: String
UPDATE users SET username = :oldName WHERE username = :newName AND username <> :oldName|]
conn "tony" "billy"
let expected :: Integer
expected = 2
assertEqual "" expected actual
[yesh|
-- name:findUser :: (Int)
-- :username :: String
SELECT id FROM users WHERE username = :username
;;;
-- name:setUserName :: rowcount Integer
-- :userID :: Int
-- :username :: String
UPDATE users SET username = :username WHERE id = :userID
|]
testMultiQuery :: Connection -> TestTree
testMultiQuery conn = testCase "Define multiple queries in one QQ inside a where" $ do
userID <- maybe (fail "User Not Found") return =<< findUser conn "tony"
rowCount <- setUserName conn userID "billy"
assertEqual "" rowCount 1
testQueryFromFile :: Connection -> TestTree
testQueryFromFile conn = testCase "Query loaded from fixture file" $ do
[(userID, username)] <- [yesh1File|tests/fixtures/getUserNamed.sql|] conn 1
assertEqual "" userID 1
assertEqual "" username "billy"
[yesh1File|tests/fixtures/getUser.sql|]
testQueryFromFileAutoName :: Connection -> TestTree
testQueryFromFileAutoName conn = testCase "Query loaded from fixture file, deriving query name from filename" $ do
[(userID, username)] <- getUser conn 1
assertEqual "" userID 1
assertEqual "" username "billy"
[yeshFile|tests/fixtures/multiQueries.sql|]
testManyQueriesFromFileAutoName :: Connection -> TestTree
testManyQueriesFromFileAutoName conn = testCase "Many queries from fixture file, auto-naming some" $ do
count0 <- multiQueries_0 conn
count1 <- multiQueriesNamed conn
count2 <- multiQueries_2 conn
assertEqual "" count0 1
assertEqual "" count1 2
assertEqual "" count2 3
testDDL :: Connection -> TestTree
testDDL conn = testCase "typical DDL statement sequence" $ do
[yesh1|
-- @ddl
CREATE TABLE users_tmp
( id INTEGER
, username TEXT
);
CREATE TABLE pages
( id INTEGER
, owner_id INTEGER
, title TEXT
, body TEXT
, FOREIGN KEY (owner_id) REFERENCES users (id)
);
|] conn