on dev help sign in
tests!
commit8a3e90afb982d2ef5b0622d05043390fd83a8fb7 authorEitan Chatav <eitan@EEM109LMBP-1.local> dateTue 16 Jul 2019, 13:15 (7 years ago) parent9a2cd8d
7 files changed+134−170
squeal-postgresql/squeal-postgresql.cabal+6−14 squeal-postgresql/test/DocTest.hs → squeal-postgresql/test/Doc.hs+3−3 squeal-postgresql/test/Main.hs+14 squeal-postgresql/test/Specs/Binary.hs → squeal-postgresql/test/Property.hs+3−3 squeal-postgresql/test/Spec.hs+108 squeal-postgresql/test/Specs/ExceptionHandling.hs−140 squeal-postgresql/test/Specs/Specs.hs−10
squeal-postgresql/squeal-postgresql.cabal+6−14
@@ -80,29 +80,21 @@ library
8080 , uuid-types >= 1.0.3
8181 , vector >= 0.12.0.1
8282
83−test-suite squeal-postgresql-doctest
83+test-suite squeal-postgresql-test
8484 default-language: Haskell2010
8585 type: exitcode-stdio-1.0
8686 hs-source-dirs: test
8787 ghc-options: -Wall
88− main-is: DocTest.hs
89− build-depends:
90− base >= 4.10.0.0
91− , doctest >= 0.11.4
92−
93−test-suite squeal-postgresql-specs
94− default-language: Haskell2010
95− type: exitcode-stdio-1.0
96− hs-source-dirs: test/Specs
97− ghc-options: -Wall
98− main-is: Specs.hs
88+ main-is: Main.hs
9989 other-modules:
100− Binary
101− , ExceptionHandling
90+ Doc
91+ , Property
92+ , Spec
10293 build-depends:
10394 base >= 4.10.0.0
10495 , async >= 2.2.2
10596 , bytestring >= 0.10.8.2
97+ , doctest >= 0.11.4
10698 , generics-sop >= 0.3.1.0
10799 , hspec >= 2.4.8
108100 , hedgehog >= 1.0
squeal-postgresql/test/DocTest.hs → squeal-postgresql/test/Doc.hsrenamed+3−3
@@ -1,9 +1,9 @@
1−module Main (main) where
1+module Doc (docTests) where
22
33import Test.DocTest
44
5−main :: IO ()
6−main = doctest
5+docTests :: IO ()
6+docTests = doctest
77 [ "-isrc"
88 , "src/Squeal/PostgreSQL.hs"
99 , "src/Squeal/PostgreSQL/Binary.hs"
squeal-postgresql/test/Main.hsnew file+14
@@ -0,0 +1,14 @@
1+module Main (main) where
2+
3+import Doc (docTests)
4+import Spec (specTests)
5+import Property (propertyTests)
6+
7+main :: IO ()
8+main = do
9+ putStrLn "\n\nRUN DOC TESTS\n\n"
10+ docTests
11+ putStrLn "\n\nRUN SPEC TESTS\n\n"
12+ specTests
13+ putStrLn "\n\nRUN PROP TESTS\n\n"
14+ propertyTests
squeal-postgresql/test/Specs/Binary.hs → squeal-postgresql/test/Property.hsrenamed+3−3
@@ -6,7 +6,7 @@
66 , TypeOperators
77#-}
88
9−module Binary (roundtrips) where
9+module Property (propertyTests) where
1010
1111import Control.Monad.Trans
1212import Data.ByteString (ByteString)
@@ -17,8 +17,8 @@ import qualified Hedgehog.Gen as Gen
1717import qualified Hedgehog.Main as Main
1818import qualified Hedgehog.Range as Range
1919
20−roundtrips :: IO ()
21−roundtrips = Main.defaultMain
20+propertyTests :: IO ()
21+propertyTests = Main.defaultMain
2222 [ roundtrip int2 (Gen.int16 Range.exponentialBounded)
2323 , roundtrip int4 (Gen.int32 Range.exponentialBounded)
2424 , roundtrip int8 (Gen.int64 Range.exponentialBounded)
squeal-postgresql/test/Spec.hsnew file+108
@@ -0,0 +1,108 @@
1+{-# LANGUAGE
2+ DataKinds
3+ , DeriveAnyClass
4+ , DeriveGeneric
5+ , DerivingStrategies
6+ , DuplicateRecordFields
7+ , FlexibleContexts
8+ , OverloadedLabels
9+ , OverloadedLists
10+ , OverloadedStrings
11+ , TypeApplications
12+ , TypeFamilies
13+ , TypeInType
14+ , TypeOperators
15+#-}
16+
17+module Spec (specTests) where
18+
19+import Control.Concurrent.Async (replicateConcurrently)
20+import Data.ByteString (ByteString)
21+import Data.Text (Text)
22+import Test.Hspec
23+
24+import qualified Generics.SOP as SOP
25+import qualified GHC.Generics as GHC
26+
27+import Squeal.PostgreSQL
28+
29+type Schemas = Public
30+ '[ "users" ::: 'Table (
31+ '[ "pk_users" ::: 'PrimaryKey '["id"]
32+ , "unique_names" ::: 'Unique '["name"]
33+ ] :=>
34+ '[ "id" ::: 'Def :=> 'NotNull 'PGint4
35+ , "name" ::: 'NoDef :=> 'NotNull 'PGtext ] ) ]
36+
37+data User = User
38+ { userName :: Text
39+ } deriving stock (Eq, Show, GHC.Generic)
40+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
41+
42+insertUser :: Manipulation_ Schemas User ()
43+insertUser = insertInto_ #users
44+ (Values_ (Default `as` #id :* Set (param @1) `as` #name))
45+
46+setup :: Definition (Public '[]) Schemas
47+setup =
48+ createTable #users
49+ ( serial `as` #id :*
50+ notNullable text `as` #name )
51+ ( primaryKey #id `as` #pk_users :*
52+ unique #name `as` #unique_names )
53+
54+teardown :: Definition Schemas (Public '[])
55+teardown = dropTable #users
56+
57+silence :: MonadPQ schemas pq => pq ()
58+silence = manipulate_ $
59+ UnsafeManipulation "SET client_min_messages TO WARNING;"
60+
61+setupDB :: IO ()
62+setupDB = withConnection connectionString $
63+ silence & pqThen (define setup)
64+
65+dropDB :: IO ()
66+dropDB = withConnection connectionString $
67+ silence & pqThen (define teardown)
68+
69+connectionString :: ByteString
70+connectionString = "host=localhost port=5432 dbname=exampledb"
71+
72+spec :: Spec
73+spec = before_ setupDB . after_ dropDB $ do
74+
75+ describe "Exceptions" $ do
76+
77+ let
78+ testUser = User "TestUser"
79+ newUser = manipulateParams_ insertUser
80+ insertUserTwice = newUser testUser >> newUser testUser
81+ err23505 = PQException $ PQState FatalError (Just "23505")
82+ (Just "ERROR: duplicate key value violates unique constraint \"unique_names\"\nDETAIL: Key (name)=(TestUser) already exists.\n")
83+
84+ it "should be thrown for constraint violation" $
85+ withConnection connectionString insertUserTwice
86+ `shouldThrow` (== err23505)
87+
88+ it "should be rethrown for constraint violation in a transaction" $
89+ withConnection connectionString (transactionally_ insertUserTwice)
90+ `shouldThrow` (== err23505)
91+
92+ describe "Pools" $ do
93+
94+ it "should manage concurrent transactions" $ do
95+ pool <- createConnectionPool
96+ "host=localhost port=5432 dbname=exampledb" 1 0.5 10
97+ let
98+ query :: Query_ (Public '[]) () (Only Char)
99+ query = values_ (literal 'a' `as` #fromOnly)
100+ session = usingConnectionPool pool . transactionally_ $ do
101+ result <- runQuery query
102+ Just (Only chr) <- firstRow result
103+ return chr
104+ chrs <- replicateConcurrently 10 session
105+ chrs `shouldSatisfy` (all (== 'a'))
106+
107+specTests :: IO ()
108+specTests = hspec spec
squeal-postgresql/test/Specs/ExceptionHandling.hsdeleted−140
@@ -1,140 +0,0 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE DeriveGeneric #-}
3−{-# LANGUAGE DuplicateRecordFields #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE OverloadedLabels #-}
6−{-# LANGUAGE OverloadedLists #-}
7−{-# LANGUAGE OverloadedStrings #-}
8−{-# LANGUAGE TypeApplications #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeInType #-}
11−{-# LANGUAGE TypeOperators #-}
12−module ExceptionHandling
13− ( specs
14− , User (..)
15− ) where
16−
17−import Control.Concurrent.Async (replicateConcurrently)
18−import Control.Monad (void)
19−import Control.Monad.IO.Class (MonadIO (..))
20−import Data.Int (Int16)
21−import Data.Text (Text)
22−import Data.Vector (Vector)
23−import Test.Hspec
24−
25−import qualified Data.ByteString.Char8 as Char8
26−import qualified Generics.SOP as SOP
27−import qualified GHC.Generics as GHC
28−
29−import Squeal.PostgreSQL
30−
31−type Schema =
32− '[ "users" ::: 'Table (
33− '[ "pk_users" ::: 'PrimaryKey '["id"]
34− , "unique_names" ::: 'Unique '["name"]
35− ] :=>
36− '[ "id" ::: 'Def :=> 'NotNull 'PGint4
37− , "name" ::: 'NoDef :=> 'NotNull 'PGtext
38− , "vec" ::: 'NoDef :=> 'NotNull ('PGvararray ('Null 'PGint2))
39− ])
40− , "emails" ::: 'Table (
41− '[ "pk_emails" ::: 'PrimaryKey '["id"]
42− , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
43− ] :=>
44− '[ "id" ::: 'Def :=> 'NotNull 'PGint4
45− , "user_id" ::: 'NoDef :=> 'NotNull 'PGint4
46− , "email" ::: 'NoDef :=> 'Null 'PGtext
47− ])
48− ]
49−
50−type Schemas = Public Schema
51−
52−data User =
53− User { userName :: Text
54− , userEmail :: Maybe Text
55− , userVec :: VarArray (Vector (Maybe Int16)) }
56− deriving (Show, GHC.Generic)
57−instance SOP.Generic User
58−instance SOP.HasDatatypeInfo User
59−
60−insertUser :: Manipulation '[] Schemas '[ 'NotNull 'PGtext, 'NotNull ('PGvararray ('Null 'PGint2))]
61− '[ "fromOnly" ::: 'NotNull 'PGint4 ]
62−insertUser = insertInto #users
63− (Values_ (Default `as` #id :* Set (param @1) `as` #name :* Set (param @2) `as` #vec))
64− OnConflictDoRaise (Returning (#id `as` #fromOnly))
65−
66−setup :: Definition (Public '[]) Schemas
67−setup =
68− createTable #users
69− ( serial `as` #id :*
70− (text & notNullable) `as` #name :*
71− (vararray int2 & notNullable) `as` #vec )
72− ( primaryKey #id `as` #pk_users
73− :* unique #name `as` #unique_names )
74− >>>
75− createTable #emails
76− ( serial `as` #id :*
77− (int & notNullable) `as` #user_id :*
78− (text & nullable) `as` #email )
79− ( primaryKey #id `as` #pk_emails :*
80− foreignKey #user_id #users #id
81− OnDeleteCascade OnUpdateCascade `as` #fk_user_id )
82−
83−teardown :: Definition Schemas (Public '[])
84−teardown = dropTable #emails >>> dropTable #users
85−
86−migration :: Migration Definition (Public '[]) Schemas
87−migration = Migration { name = "test"
88− , up = setup
89− , down = teardown }
90−
91−setupDB :: IO ()
92−setupDB = withConnection connectionString $
93− manipulate (UnsafeManipulation "SET client_min_messages TO WARNING;")
94− & pqThen (migrateUp (single migration))
95−
96−dropDB :: IO ()
97−dropDB = withConnection connectionString $
98− manipulate (UnsafeManipulation "SET client_min_messages TO WARNING;")
99− & pqThen (migrateDown (single migration))
100−
101−connectionString :: Char8.ByteString
102−connectionString = "host=localhost port=5432 dbname=exampledb"
103−
104−testUser :: User
105−testUser = User "TestUser" Nothing (VarArray [])
106−
107−newUser :: (MonadIO m, MonadPQ Schemas m) => User -> m ()
108−newUser u = void $ manipulateParams insertUser (userName u, userVec u)
109−
110−insertUserTwice :: (MonadIO m, MonadPQ Schemas m) => m ()
111−insertUserTwice = newUser testUser >> newUser testUser
112−
113−specs :: SpecWith ()
114−specs = before_ setupDB $ after_ dropDB $
115− describe "Exceptions" $ do
116−
117− let
118− dupKeyErr = PQException $ PQState FatalError (Just "23505")
119− (Just "ERROR: duplicate key value violates unique constraint \"unique_names\"\nDETAIL: Key (name)=(TestUser) already exists.\n")
120−
121− it "should be thrown for unique constraint violation in a manipulation" $
122− withConnection connectionString insertUserTwice
123− `shouldThrow` (== dupKeyErr)
124−
125− it "should be rethrown for unique constraint violation in a manipulation by a transaction" $
126− withConnection connectionString (transactionally_ insertUserTwice)
127− `shouldThrow` (== dupKeyErr)
128−
129− it "should handle concurrent transactions using pooled connections" $ do
130− pool <- createConnectionPool
131− "host=localhost port=5432 dbname=exampledb" 1 0.5 10
132− let
133− query :: Query_ (Public '[]) () (Only Char)
134− query = values_ (literal 'a' `as` #fromOnly)
135− session = usingConnectionPool pool . transactionally_ $ do
136− result <- runQuery query
137− Just (Only chr) <- firstRow result
138− return chr
139− chrs <- replicateConcurrently 10 session
140− chrs `shouldSatisfy` (all (== 'a'))
squeal-postgresql/test/Specs/Specs.hsdeleted−10
@@ -1,10 +0,0 @@
1−module Main where
2−
3−import Test.Hspec
4−import qualified ExceptionHandling
5−import qualified Binary
6−
7−main :: IO ()
8−main = do
9− hspec ExceptionHandling.specs
10− Binary.roundtrips