postgres-wire/tests/Driver.hs

219 lines
7.6 KiB
Haskell
Raw Normal View History

2017-01-29 03:48:14 +03:00
module Driver where
import Data.Monoid ((<>))
import Data.Foldable
import Control.Monad
import Data.Maybe
2017-01-29 04:22:55 +03:00
import Data.Either
2017-01-29 03:48:14 +03:00
import qualified Data.ByteString as B
2017-02-13 17:30:39 +03:00
import qualified Data.ByteString.Lazy as BL
2017-01-29 03:48:14 +03:00
import qualified Data.ByteString.Char8 as BS
2017-01-29 05:21:46 +03:00
import qualified Data.Vector as V
2017-01-29 03:48:14 +03:00
import Test.Tasty
import Test.Tasty.HUnit
import Database.PostgreSQL.Driver.Connection
2017-02-02 03:09:07 +03:00
import Database.PostgreSQL.Driver.StatementStorage
2017-02-01 06:53:56 +03:00
import Database.PostgreSQL.Driver.Query
2017-01-29 03:48:14 +03:00
import Database.PostgreSQL.Protocol.Types
import Connection
2017-01-29 04:22:55 +03:00
testDriver :: TestTree
testDriver = testGroup "Driver"
[ testCase "Single batch" testBatch
, testCase "Two batches" testTwoBatches
, testCase "Multiple batches" testMultipleBatches
2017-01-29 04:22:55 +03:00
, testCase "Empty query" testEmptyQuery
, testCase "Query without result" testQueryWithoutResult
, testCase "Invalid queries" testInvalidBatch
2017-01-29 05:21:46 +03:00
, testCase "Describe statement" testDescribeStatement
, testCase "Describe statement with no data" testDescribeStatementNoData
, testCase "Describe empty statement" testDescribeStatementEmpty
, testCase "SimpleQuery" testSimpleQuery
2017-02-02 03:45:37 +03:00
, testCase "PreparedStatementCache" testPreparedStatementCache
2017-02-09 12:37:52 +03:00
, testCase "Query with large response" testLargeQuery
2017-01-29 04:22:55 +03:00
]
2017-01-29 03:48:14 +03:00
makeQuery1 :: B.ByteString -> Query
makeQuery1 n = Query "SELECT $1" (V.fromList [(Oid 23, Just n)])
2017-02-02 03:09:07 +03:00
Text Text AlwaysCache
2017-01-29 03:48:14 +03:00
makeQuery2 :: B.ByteString -> B.ByteString -> Query
2017-02-02 03:09:07 +03:00
makeQuery2 n1 n2 = Query "SELECT $1 + $2"
(V.fromList [(Oid 23, Just n1), (Oid 23, Just n2)]) Text Text AlwaysCache
2017-01-29 03:48:14 +03:00
2017-01-29 04:22:55 +03:00
fromRight :: Either e a -> a
2017-01-29 03:48:14 +03:00
fromRight (Right v) = v
fromRight _ = error "fromRight"
2017-02-13 17:30:39 +03:00
fromMessage :: Either e DataRows -> B.ByteString
-- TODO
2017-02-13 18:27:50 +03:00
-- 5 bytes -header, 2 bytes -count, 4 bytes - length
fromMessage (Right (DataRows bs)) = B.drop 11 $ BL.toStrict bs
2017-02-02 00:18:06 +03:00
fromMessage _ = error "from message"
2017-01-29 04:22:55 +03:00
2017-01-29 05:45:44 +03:00
-- | Single batch.
2017-01-29 03:48:14 +03:00
testBatch :: IO ()
testBatch = withConnection $ \c -> do
let a = "5"
b = "3"
sendBatchAndSync c [makeQuery1 a, makeQuery1 b]
r1 <- readNextData c
r2 <- readNextData c
2017-02-13 18:27:50 +03:00
waitReadyForQuery c
2017-02-02 00:18:06 +03:00
a @=? fromMessage r1
b @=? fromMessage r2
2017-01-29 03:48:14 +03:00
2017-01-29 05:45:44 +03:00
-- | Two batches in single transaction.
2017-01-29 03:48:14 +03:00
testTwoBatches :: IO ()
testTwoBatches = withConnection $ \c -> do
let a = 7
b = 2
sendBatchAndFlush c [ makeQuery1 (BS.pack (show a))
, makeQuery1 (BS.pack (show b))]
2017-02-02 00:18:06 +03:00
r1 <- fromMessage <$> readNextData c
r2 <- fromMessage <$> readNextData c
2017-01-29 03:48:14 +03:00
sendBatchAndSync c [makeQuery2 r1 r2]
r <- readNextData c
2017-02-09 20:05:49 +03:00
waitReadyForQuery c
2017-01-29 03:48:14 +03:00
2017-02-02 00:18:06 +03:00
BS.pack (show $ a + b) @=? fromMessage r
2017-01-29 05:45:44 +03:00
-- | Multiple batches with individual transactions in single connection.
testMultipleBatches :: IO ()
testMultipleBatches = withConnection $ replicateM_ 10 . assertSingleBatch
2017-01-29 03:48:14 +03:00
where
assertSingleBatch c = do
let a = "5"
b = "6"
sendBatchAndSync c [ makeQuery1 a, makeQuery1 b]
r1 <- readNextData c
2017-02-02 00:18:06 +03:00
a @=? fromMessage r1
2017-01-29 05:45:44 +03:00
r2 <- readNextData c
2017-02-02 00:18:06 +03:00
b @=? fromMessage r2
2017-02-09 20:05:49 +03:00
waitReadyForQuery c
2017-01-29 03:48:14 +03:00
2017-01-29 05:45:44 +03:00
-- | Query is empty string.
2017-01-29 04:22:55 +03:00
testEmptyQuery :: IO ()
testEmptyQuery = assertQueryNoData $
2017-02-02 03:09:07 +03:00
Query "" V.empty Text Text NeverCache
2017-01-29 04:22:55 +03:00
2017-01-29 05:45:44 +03:00
-- | Query than returns no datarows.
2017-01-29 04:22:55 +03:00
testQueryWithoutResult :: IO ()
testQueryWithoutResult = assertQueryNoData $
2017-02-02 03:09:07 +03:00
Query "SET client_encoding TO UTF8" V.empty Text Text NeverCache
2017-01-29 04:22:55 +03:00
2017-01-29 05:45:44 +03:00
-- | Asserts that query returns no data rows.
2017-01-29 04:22:55 +03:00
assertQueryNoData :: Query -> IO ()
assertQueryNoData q = withConnection $ \c -> do
sendBatchAndSync c [q]
r <- fromRight <$> readNextData c
2017-02-09 20:05:49 +03:00
waitReadyForQuery c
2017-02-13 17:30:39 +03:00
DataRows "" @=? r
2017-01-29 04:22:55 +03:00
2017-02-13 17:30:39 +03:00
-- | Asserts that all the received data messages are in form (Right _)
2017-01-29 04:22:55 +03:00
checkRightResult :: Connection -> Int -> Assertion
checkRightResult conn 0 = pure ()
checkRightResult conn n = readNextData conn >>=
either (const $ assertFailure "Result is invalid")
(const $ checkRightResult conn (n - 1))
2017-02-13 17:30:39 +03:00
-- | Asserts that (Left _) as result exists in the received data messages.
2017-01-29 04:22:55 +03:00
checkInvalidResult :: Connection -> Int -> Assertion
checkInvalidResult conn 0 = assertFailure "Result is right"
checkInvalidResult conn n = readNextData conn >>=
either (const $ pure ())
(const $ checkInvalidResult conn (n -1))
2017-01-29 05:45:44 +03:00
-- | Diffirent invalid queries in batches.
2017-01-29 04:22:55 +03:00
testInvalidBatch :: IO ()
testInvalidBatch = do
let rightQuery = makeQuery1 "5"
q1 = Query "SEL $1" (V.fromList [(Oid 23, Just "5")]) Text Text NeverCache
q2 = Query "SELECT $1" (V.fromList [(Oid 23, Just "a")]) Text Text NeverCache
2017-02-02 03:09:07 +03:00
q4 = Query "SELECT $1" (V.fromList []) Text Text NeverCache
2017-01-29 04:22:55 +03:00
assertInvalidBatch "Parse error" [q1]
assertInvalidBatch "Invalid param" [ q2]
assertInvalidBatch "Missed oid of param" [ q4]
assertInvalidBatch "Parse error" [rightQuery, q1]
assertInvalidBatch "Invalid param" [rightQuery, q2]
assertInvalidBatch "Missed oid of param" [rightQuery, q4]
where
assertInvalidBatch desc qs = withConnection $ \c -> do
sendBatchAndSync c qs
checkInvalidResult c $ length qs
2017-01-29 05:45:44 +03:00
-- | Describes usual statement.
2017-01-29 05:21:46 +03:00
testDescribeStatement :: IO ()
2017-02-13 17:30:39 +03:00
testDescribeStatement = withConnectionCommon $ \c -> do
2017-01-29 05:21:46 +03:00
r <- describeStatement c $
"select typname, typnamespace, typowner, typlen, typbyval,"
<> "typcategory, typispreferred, typisdefined, typdelim, typrelid,"
<> "typelem, typarray from pg_type where typtypmod = $1 "
<> "and typisdefined = $2"
assertBool "Should be Right" $ isRight r
2017-01-29 05:45:44 +03:00
-- | Describes statement that returns no data.
2017-01-29 05:21:46 +03:00
testDescribeStatementNoData :: IO ()
2017-02-13 17:30:39 +03:00
testDescribeStatementNoData = withConnectionCommon $ \c -> do
2017-01-29 05:21:46 +03:00
r <- fromRight <$> describeStatement c "SET client_encoding TO UTF8"
assertBool "Should be empty" $ V.null (fst r)
assertBool "Should be empty" $ V.null (snd r)
2017-01-29 05:45:44 +03:00
-- | Describes statement that is empty string.
2017-01-29 05:21:46 +03:00
testDescribeStatementEmpty :: IO ()
2017-02-13 17:30:39 +03:00
testDescribeStatementEmpty = withConnectionCommon $ \c -> do
2017-01-29 05:21:46 +03:00
r <- fromRight <$> describeStatement c ""
assertBool "Should be empty" $ V.null (fst r)
assertBool "Should be empty" $ V.null (snd r)
2017-01-29 05:45:44 +03:00
-- | Query using simple query protocol.
testSimpleQuery :: IO ()
2017-02-13 17:30:39 +03:00
testSimpleQuery = withConnectionCommon $ \c -> do
r <- sendSimpleQuery c $
"DROP TABLE IF EXISTS a;"
<> "CREATE TABLE a(v int);"
<> "INSERT INTO a VALUES (1), (2), (3);"
<> "SELECT * FROM a;"
<> "DROP TABLE a;"
assertBool "Should be Right" $ isRight r
2017-02-02 03:45:37 +03:00
-- | Test that cache of statements works.
testPreparedStatementCache :: IO ()
testPreparedStatementCache = withConnection $ \c -> do
let a = 7
b = 2
sendBatchAndSync c [ makeQuery1 (BS.pack (show a))
, makeQuery1 (BS.pack (show b))
, makeQuery2 (BS.pack (show a)) (BS.pack (show b))]
r1 <- fromMessage <$> readNextData c
r2 <- fromMessage <$> readNextData c
r3 <- fromMessage <$> readNextData c
2017-02-13 18:27:50 +03:00
waitReadyForQuery c
2017-02-02 03:45:37 +03:00
BS.pack (show a) @=? r1
BS.pack (show b) @=? r2
BS.pack (show $ a + b) @=? r3
size <- getCacheSize $ connStatementStorage c
-- 2 different statements were send
2 @=? size
2017-02-09 12:37:52 +03:00
-- | Test that large responses are properly handled
testLargeQuery :: IO ()
testLargeQuery = withConnection $ \c -> do
sendBatchAndSync c [Query largeStmt V.empty Text Text NeverCache ]
r <- readNextData c
2017-02-13 18:27:50 +03:00
waitReadyForQuery c
2017-02-09 12:37:52 +03:00
assertBool "Should be Right" $ isRight r
where
largeStmt = "select typname, typnamespace, typowner, typlen, typbyval,"
<> "typcategory, typispreferred, typisdefined, typdelim,"
<> "typrelid, typelem, typarray from pg_type "