1
1
module Test.Main where
2
2
3
- import Control.Monad.Eff.Console as C
4
- import Control.Monad.Aff (Aff , apathize , attempt , runAff )
5
- import Control.Monad.Aff.Console (log , print )
3
+ import Prelude
4
+ import Control.Monad.Aff (Aff , apathize , attempt )
6
5
import Control.Monad.Eff (Eff )
7
6
import Control.Monad.Eff.Class (liftEff )
8
7
import Control.Monad.Eff.Console (CONSOLE )
9
8
import Control.Monad.Eff.Exception (error )
10
9
import 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 (..))
11
16
import Data.Either (either )
12
- import Data.Foldable (foldMap )
17
+ import Data.Foreign (Foreign )
18
+ import Data.JSDate (toDateTime )
19
+ import Data.Maybe (Maybe (Nothing, Just), maybe )
13
20
import Data.Foreign.Class (class IsForeign , readProp )
14
- import Data.Maybe ( Maybe )
15
- 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 )
16
23
import Database.Postgres.SqlValue (toSql )
17
24
import Database.Postgres.Transaction (withTransaction )
18
- import Prelude ( class Show , Unit , return , ($), bind , show , (<>), void , flip , (>>>), const )
25
+ import Node.Process ( PROCESS )
19
26
20
- main :: forall eff . Eff ( console :: CONSOLE , db :: DB | eff ) Unit
21
- main = runAff C .print (const $ C .log " All ok" ) $ do
22
- print $ " connecting to " <> mkConnectionString connectionInfo <> " ..."
23
- exampleUsingWithConnection
24
- exampleLowLevel
27
+ import Unsafe.Coerce (unsafeCoerce )
25
28
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 )
34
33
35
34
data Artist = Artist
36
35
{ name :: String
37
36
, year :: Int
38
37
}
39
38
39
+ connectionInfo :: { host :: String , db :: String , port :: Int , user :: String , password :: String }
40
40
connectionInfo =
41
41
{ host: " localhost"
42
42
, db: " test"
@@ -45,59 +45,101 @@ connectionInfo =
45
45
, password: " test"
46
46
}
47
47
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
- print (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"
64
126
65
127
exampleError :: forall eff . Aff (db :: DB | eff ) (Maybe Artist )
66
128
exampleError = withConnection connectionInfo $ \c -> do
67
129
execute_ (Query " delete from artist" ) c
68
130
execute_ (Query " insert into artist values ('Led Zeppelin', 1968)" ) c
69
131
queryOne_ (Query " select year from artist" ) c
70
132
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 $ print 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
-
96
133
instance artistShow :: Show Artist where
97
134
show (Artist p) = " Artist (" <> p.name <> " , " <> show p.year <> " )"
98
135
136
+ derive instance genericArtist :: Generic Artist
137
+
138
+ instance eqArtist :: Eq Artist where
139
+ eq = gEq
140
+
99
141
instance artistIsForeign :: IsForeign Artist where
100
142
read obj = do
101
143
n <- readProp " name" obj
102
144
y <- readProp " year" obj
103
- return $ Artist { name: n, year: y }
145
+ pure $ Artist { name: n, year: y }
0 commit comments