Skip to content

Commit 1de6e1e

Browse files
committed
Add new test suite using purescript-spec
1 parent 6a39636 commit 1de6e1e

2 files changed

Lines changed: 109 additions & 62 deletions

File tree

bower.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,14 @@
3131
"purescript-foreign": "^1.0.0",
3232
"purescript-foldable-traversable": "^1.0.0",
3333
"purescript-transformers": "^1.0.0",
34-
"purescript-aff": "~0.17.0",
34+
"purescript-aff": "^1.0.0",
3535
"purescript-integers": "^1.0.0",
3636
"purescript-datetime": "^1.0.0",
37-
"purescript-unsafe-coerce": "~1.0.0"
37+
"purescript-unsafe-coerce": "^1.0.0",
38+
"purescript-nullable": "^1.0.1"
39+
},
40+
"devDependencies": {
41+
"purescript-spec": "~0.8.0",
42+
"purescript-generics": "~1.0.0"
3843
}
3944
}

test/Main.purs

Lines changed: 102 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,42 @@
11
module Test.Main where
22

33
import 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)
75
import Control.Monad.Eff (Eff)
86
import Control.Monad.Eff.Class (liftEff)
97
import Control.Monad.Eff.Console (CONSOLE)
108
import Control.Monad.Eff.Exception (error)
119
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(..))
1216
import 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)
1420
import 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)
1723
import Database.Postgres.SqlValue (toSql)
1824
import 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

3534
data Artist = Artist
3635
{ name :: String
3736
, year :: Int
3837
}
3938

39+
connectionInfo :: { host :: String, db :: String, port :: Int, user :: String, password :: String }
4040
connectionInfo =
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

65127
exampleError :: forall eff. Aff (db :: DB | eff) (Maybe Artist)
66128
exampleError = 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-
96133
instance 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+
99141
instance artistIsForeign :: IsForeign Artist where
100142
read obj = do
101143
n <- readProp "name" obj

0 commit comments

Comments
 (0)