11module Test.Main where
22
33import Prelude
4- import Control.Monad.Eff.Console as C
5- import Control.Monad.Aff (Aff , apathize , attempt , runAff )
6- import Control.Monad.Aff.Console (log , logShow )
4+ import Control.Monad.Aff (Aff , apathize , attempt )
75import Control.Monad.Eff (Eff )
86import Control.Monad.Eff.Class (liftEff )
97import Control.Monad.Eff.Console (CONSOLE )
108import Control.Monad.Eff.Exception (error )
119import Control.Monad.Error.Class (throwError )
10+ import Data.Array (length )
11+ import Data.Date (canonicalDate )
12+ import Data.DateTime (DateTime (..))
13+ import Data.Date.Component (Month (..))
14+ import Data.Enum (toEnum )
15+ import Data.Time (Time (..))
1216import Data.Either (either )
13- import Data.Foldable (foldMap )
17+ import Data.Foreign (Foreign )
18+ import Data.JSDate (toDateTime )
19+ import Data.Maybe (Maybe (Nothing, Just), maybe )
1420import Data.Foreign.Class (class IsForeign , readProp )
15- import Data.Maybe ( Maybe )
16- import Database.Postgres (DB , Query (Query), queryOne_ , execute_ , withConnection , query , withClient , end , query_ , connect , queryValue_ , disconnect , mkConnectionString )
21+ import Data.Generic ( class Generic , gEq )
22+ import Database.Postgres (DB , Query (Query), queryOne_ , execute , execute_ , withConnection , query , withClient , end , query_ , connect , queryValue_ , mkConnectionString )
1723import Database.Postgres.SqlValue (toSql )
1824import Database.Postgres.Transaction (withTransaction )
25+ import Node.Process (PROCESS )
1926
20- main :: forall eff . Eff ( console :: CONSOLE , db :: DB | eff ) Unit
21- main = runAff C .logShow (const $ C .log " All ok" ) $ do
22- logShow $ " connecting to " <> mkConnectionString connectionInfo <> " ..."
23- exampleUsingWithConnection
24- exampleLowLevel
27+ import Unsafe.Coerce (unsafeCoerce )
2528
26- res <- attempt exampleError
27- either (const $ log " got an error, like we should" ) (const $ log " FAIL" ) res
28-
29- exampleQueries
30-
31- exampleTransaction
32-
33- liftEff $ disconnect
29+ import Test.Spec (describe , it )
30+ import Test.Spec.Runner (run )
31+ import Test.Spec.Assertions (fail , shouldEqual )
32+ import Test.Spec.Reporter.Console (consoleReporter )
3433
3534data Artist = Artist
3635 { name :: String
3736 , year :: Int
3837 }
3938
39+ connectionInfo :: { host :: String , db :: String , port :: Int , user :: String , password :: String }
4040connectionInfo =
4141 { host: " localhost"
4242 , db: " test"
@@ -45,57 +45,99 @@ connectionInfo =
4545 , password: " test"
4646 }
4747
48- exampleUsingWithConnection :: forall eff . Aff (console :: C.CONSOLE , db :: DB | eff ) Unit
49- exampleUsingWithConnection = withConnection connectionInfo $ \c -> do
50- execute_ (Query " delete from artist" ) c
51- execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) c
52- execute_ (Query " insert into artist values ('Deep Purple', 1968)" ) c
53- year <- queryValue_ (Query " insert into artist values ('Fairport Convention', 1967) returning year" :: Query Number ) c
54- logShow (show year)
55- artists <- query_ (Query " select * from artist" :: Query Artist ) c
56- printRows artists
57-
58- exampleLowLevel :: forall eff . Aff (console :: C.CONSOLE , db :: DB | eff ) Unit
59- exampleLowLevel = do
60- client <- connect connectionInfo
61- artists <- query_ (Query " select * from artist order by name desc" :: Query Artist ) client
62- printRows artists
63- liftEff $ end client
48+ main :: Eff (process :: PROCESS , console :: CONSOLE , db :: DB ) Unit
49+ main = run [consoleReporter] do
50+ describe " connection string" do
51+ it " should build one from the connection record" do
52+ mkConnectionString connectionInfo `shouldEqual` " postgres://testuser:test@localhost:5432/test"
53+
54+ describe " withConnection" do
55+ it " Returns a connection" do
56+ withConnection connectionInfo $ \c -> do
57+ execute_ (Query " delete from artist" ) c
58+ execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) c
59+ execute_ (Query " insert into artist values ('Deep Purple', 1968)" ) c
60+ let
61+ q :: Query Int
62+ q = Query " insert into artist values ('Fairport Convention', 1967) returning year"
63+
64+ year <- queryValue_ q c
65+ year `shouldEqual` (Just 1967 )
66+
67+ artists <- query_ (Query " select * from artist" :: Query Artist ) c
68+ length artists `shouldEqual` 3
69+
70+ describe " Low level API" do
71+ it " Can be used to manage connections manually" do
72+ client <- connect connectionInfo
73+ execute_ (Query " delete from artist" ) client
74+ execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) client
75+
76+ artists <- query_ (Query " select * from artist order by name desc" :: Query Artist ) client
77+ artists `shouldEqual` [Artist { name: " Led Zeppelin" , year: 1968 }]
78+
79+ liftEff $ end client
80+
81+ describe " Error handling" do
82+ it " When query cannot be converted to the requested data type we get an error" do
83+ res <- attempt exampleError
84+ either (const $ pure unit) (const $ fail " FAIL" ) res
85+
86+ describe " Query params" do
87+ it " Select using a query param" do
88+ withClient connectionInfo $ \c -> do
89+ execute_ (Query " delete from artist" ) c
90+ execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) c
91+ execute_ (Query " insert into artist values ('Deep Purple', 1968)" ) c
92+ execute_ (Query " insert into artist values ('Toto', 1977)" ) c
93+ artists <- query (Query " select * from artist where name = $1" :: Query Artist ) [toSql " Toto" ] c
94+ length artists `shouldEqual` 1
95+
96+ noRows <- query (Query " select * from artist where name = $1" :: Query Artist ) [toSql " FAIL" ] c
97+ length noRows `shouldEqual` 0
98+
99+ describe " data types" do
100+ it " datetimes can be inserted" do
101+ withConnection connectionInfo \c -> do
102+ execute_ (Query " delete from types" ) c
103+ let date = canonicalDate <$> toEnum 2016 <*> Just January <*> toEnum 25
104+ time = Time <$> toEnum 23 <*> toEnum 1 <*> toEnum 59 <*> toEnum 0
105+ dt = DateTime <$> date <*> time
106+ maybe (fail " Not a datetime" ) (\ts -> do
107+ execute (Query " insert into types(timestamp_no_tz) VALUES ($1)" ) [toSql ts] c
108+ ts' <- queryValue_ (Query " select timestamp_no_tz at time zone 'UTC' from types" :: Query Foreign ) c
109+ let res = unsafeCoerce <$> ts' >>= toDateTime
110+ res `shouldEqual` (Just ts)
111+ ) dt
112+
113+
114+ describe " transactions" do
115+ it " does not commit after an error inside a transation" do
116+ withConnection connectionInfo $ \c -> do
117+ execute_ (Query " delete from artist" ) c
118+ apathize $ tryInsert c
119+ one <- queryOne_ (Query " select * from artist" :: Query Artist ) c
120+
121+ one `shouldEqual` Nothing
122+ where
123+ tryInsert = withTransaction $ \c -> do
124+ execute_ (Query " insert into artist values ('Not there', 1999)" ) c
125+ throwError $ error " fail"
64126
65127exampleError :: forall eff . Aff (db :: DB | eff ) (Maybe Artist )
66128exampleError = withConnection connectionInfo $ \c -> do
67129 execute_ (Query " delete from artist" ) c
68130 execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) c
69131 queryOne_ (Query " select year from artist" ) c
70132
71- exampleQueries :: forall eff . Aff (console :: C.CONSOLE , db :: DB | eff ) Unit
72- exampleQueries = withClient connectionInfo $ \c -> do
73- log " Example queries with params:"
74- execute_ (Query " delete from artist" ) c
75- execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) c
76- execute_ (Query " insert into artist values ('Deep Purple', 1968)" ) c
77- execute_ (Query " insert into artist values ('Toto', 1977)" ) c
78- artists <- query (Query " select * from artist where name = $1" :: Query Artist ) [toSql " Toto" ] c
79- printRows artists
80-
81- exampleTransaction :: forall eff . Aff (console :: C.CONSOLE , db :: DB | eff ) Unit
82- exampleTransaction = withConnection connectionInfo $ \c -> do
83- execute_ (Query " delete from artist" ) c
84- apathize $ tryInsert c
85- one <- queryOne_ (Query " select * from artist" :: Query Artist ) c
86- void $ logShow one
87- where
88- tryInsert = withTransaction $ \c -> do
89- execute_ (Query " insert into artist values ('Not there', 1999)" ) c
90- throwError $ error " fail"
91-
92- printRows :: forall a eff . (Show a ) => Array a -> Aff (console :: C.CONSOLE | eff ) Unit
93- printRows rows = void $ log $ " result:\n " <> foldMap stringify rows
94- where stringify = show >>> flip (<>) " \n "
95-
96133instance artistShow :: Show Artist where
97134 show (Artist p) = " Artist (" <> p.name <> " , " <> show p.year <> " )"
98135
136+ derive instance genericArtist :: Generic Artist
137+
138+ instance eqArtist :: Eq Artist where
139+ eq = gEq
140+
99141instance artistIsForeign :: IsForeign Artist where
100142 read obj = do
101143 n <- readProp " name" obj
0 commit comments