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 (Date , canonicalDate )
12+ import Data.DateTime (DateTime (..))
13+ import Data.Date.Component (Day (..), Month (..), Year (..))
14+ import Data.Enum (toEnum )
15+ import Data.Time (Time (..))
16+ import Data.Time.Component (Hour (..), Minute (..), Second (..))
1217import Data.Either (either )
13- import Data.Foldable (foldMap )
18+ import Data.Foreign (Foreign )
19+ import Data.JSDate (JSDate , toDateTime )
20+ import Data.Maybe (Maybe (Nothing, Just), maybe )
1421import 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 )
22+ import Data.Generic ( class Generic , gEq )
23+ import Database.Postgres (DB , Query (Query), queryOne_ , execute , execute_ , withConnection , query , withClient , end , query_ , connect , queryValue_ , mkConnectionString )
1724import Database.Postgres.SqlValue (toSql )
1825import Database.Postgres.Transaction (withTransaction )
26+ import Node.Process (PROCESS )
1927
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
28+ import Unsafe.Coerce (unsafeCoerce )
2529
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
30+ import Test.Spec (describe , it )
31+ import Test.Spec.Runner (run )
32+ import Test.Spec.Assertions (fail , shouldEqual )
33+ import Test.Spec.Reporter.Console (consoleReporter )
3434
3535data Artist = Artist
3636 { name :: String
3737 , year :: Int
3838 }
3939
40+ connectionInfo :: { host :: String , db :: String , port :: Int , user :: String , password :: String }
4041connectionInfo =
4142 { host: " localhost"
4243 , db: " test"
@@ -45,57 +46,99 @@ connectionInfo =
4546 , password: " test"
4647 }
4748
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
49+ main :: Eff (process :: PROCESS , console :: CONSOLE , db :: DB ) Unit
50+ main = run [consoleReporter] do
51+ describe " connection string" do
52+ it " should build one from the connection record" do
53+ mkConnectionString connectionInfo `shouldEqual` " postgres://testuser:test@localhost:5432/test"
54+
55+ describe " withConnection" do
56+ it " Returns a connection" do
57+ withConnection connectionInfo $ \c -> do
58+ execute_ (Query " delete from artist" ) c
59+ execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) c
60+ execute_ (Query " insert into artist values ('Deep Purple', 1968)" ) c
61+ let
62+ q :: Query Int
63+ q = Query " insert into artist values ('Fairport Convention', 1967) returning year"
64+
65+ year <- queryValue_ q c
66+ year `shouldEqual` (Just 1967 )
67+
68+ artists <- query_ (Query " select * from artist" :: Query Artist ) c
69+ length artists `shouldEqual` 3
70+
71+ describe " Low level API" do
72+ it " Can be used to manage connections manually" do
73+ client <- connect connectionInfo
74+ execute_ (Query " delete from artist" ) client
75+ execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) client
76+
77+ artists <- query_ (Query " select * from artist order by name desc" :: Query Artist ) client
78+ artists `shouldEqual` [Artist { name: " Led Zeppelin" , year: 1968 }]
79+
80+ liftEff $ end client
81+
82+ describe " Error handling" do
83+ it " When query cannot be converted to the requested data type we get an error" do
84+ res <- attempt exampleError
85+ either (const $ pure unit) (const $ fail " FAIL" ) res
86+
87+ describe " Query params" do
88+ it " Select using a query param" do
89+ withClient connectionInfo $ \c -> do
90+ execute_ (Query " delete from artist" ) c
91+ execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) c
92+ execute_ (Query " insert into artist values ('Deep Purple', 1968)" ) c
93+ execute_ (Query " insert into artist values ('Toto', 1977)" ) c
94+ artists <- query (Query " select * from artist where name = $1" :: Query Artist ) [toSql " Toto" ] c
95+ length artists `shouldEqual` 1
96+
97+ noRows <- query (Query " select * from artist where name = $1" :: Query Artist ) [toSql " FAIL" ] c
98+ length noRows `shouldEqual` 0
99+
100+ describe " data types" do
101+ it " datetimes can be inserted" do
102+ withConnection connectionInfo \c -> do
103+ execute_ (Query " delete from types" ) c
104+ let date = canonicalDate <$> toEnum 2016 <*> Just January <*> toEnum 25
105+ time = Time <$> toEnum 23 <*> toEnum 1 <*> toEnum 59 <*> toEnum 0
106+ dt = DateTime <$> date <*> time
107+ maybe (fail " Not a datetime" ) (\ts -> do
108+ execute (Query " insert into types(timestamp_no_tz) VALUES ($1)" ) [toSql ts] c
109+ ts' <- queryValue_ (Query " select timestamp_no_tz at time zone 'UTC' from types" :: Query Foreign ) c
110+ let res = unsafeCoerce <$> ts' >>= toDateTime
111+ res `shouldEqual` (Just ts)
112+ ) dt
113+
114+
115+ describe " transactions" do
116+ it " does not commit after an error inside a transation" do
117+ withConnection connectionInfo $ \c -> do
118+ execute_ (Query " delete from artist" ) c
119+ apathize $ tryInsert c
120+ one <- queryOne_ (Query " select * from artist" :: Query Artist ) c
121+
122+ one `shouldEqual` Nothing
123+ where
124+ tryInsert = withTransaction $ \c -> do
125+ execute_ (Query " insert into artist values ('Not there', 1999)" ) c
126+ throwError $ error " fail"
64127
65128exampleError :: forall eff . Aff (db :: DB | eff ) (Maybe Artist )
66129exampleError = withConnection connectionInfo $ \c -> do
67130 execute_ (Query " delete from artist" ) c
68131 execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) c
69132 queryOne_ (Query " select year from artist" ) c
70133
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-
96134instance artistShow :: Show Artist where
97135 show (Artist p) = " Artist (" <> p.name <> " , " <> show p.year <> " )"
98136
137+ derive instance genericArtist :: Generic Artist
138+
139+ instance eqArtist :: Eq Artist where
140+ eq = gEq
141+
99142instance artistIsForeign :: IsForeign Artist where
100143 read obj = do
101144 n <- readProp " name" obj
0 commit comments