@@ -6,11 +6,10 @@ import Control.Monad.Error.Class (throwError)
6import Data.Array as Array
6import Data.Array as Array
7import Data.Maybe (Maybe(..), isNothing)
7import Data.Maybe (Maybe(..), isNothing)
8import Effect.Aff (Aff)
8import Effect.Aff (Aff)
9−import Effect.Class (liftEffect)
9+import Effect.Class (class MonadEffect, liftEffect)
10−import Effect.Exception (error)
10+import Effect.Exception as Exception
11import Foreign (MultipleErrors)
11import Foreign (MultipleErrors)
12import Test.Spec (Spec, describe, it)
12import Test.Spec (Spec, describe, it)
13−import Test.Spec.Assertions (shouldEqual)
14import Type.Function (type (#))
13import Type.Function (type (#))
15import Type.Proxy (Proxy(..))
14import Type.Proxy (Proxy(..))
16import Yoga.Om as Om
15import Yoga.Om as Om
@@ -32,422 +31,400 @@ import Yoga.Postgres.ClientOm
32 , gt, gte, ilike, inArray, join, like_, lt, lte
31 , gt, gte, ilike, inArray, join, like_, lt, lte
33 , maxOf, minOf, minOfWhere, ne, not_, or_
32 , maxOf, minOf, minOfWhere, ne, not_, or_
34 , selectFrom, selectFromWhere, selectOneFrom, selectOneFromWhere
33 , selectFrom, selectFromWhere, selectOneFrom, selectOneFromWhere
35− , sumOf, sumOfWhere, updateWhere, upsert, upsertMany, withTransaction
34+ , sumOf, sumOfWhere, transactionally, updateWhere, upsert, upsertMany
37−import Yoga.Postgres.Schema (Table, PrimaryKey, AutoIncrement, Unique, Default, DefaultExpr, ForeignKey, References, PGUUID)
36+import Yoga.Postgres.Schema (Table, PrimaryKey, AutoIncrement, Unique, Default, ForeignKey, References, PGUUID)
39spec :: Spec Unit
38spec :: Spec Unit
40spec = describe "ClientOm" do
39spec = describe "ClientOm" do
42 describe "Creating records" do
41 describe "Creating records" do
43− it "inserts a record and returns it with generated fields" $ withUsersTable \conn -> do
42+ it "inserts a record and returns it with generated fields" $ withUsersTable do
44− created <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
43+ created <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
45 alice <- shouldBeJust created
44 alice <- shouldBeJust created
46 alice.name `shouldEqual` "Alice"
45 alice.name `shouldEqual` "Alice"
47 (alice.id > 0) `shouldEqual` true
46 (alice.id > 0) `shouldEqual` true
49− it "lets the database fill in defaults" $ withUsersTable \conn -> do
48+ it "lets the database fill in defaults" $ withUsersTable do
50− created <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
49+ created <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
51 alice <- shouldBeJust created
50 alice <- shouldBeJust created
52 alice.active `shouldEqual` true
51 alice.active `shouldEqual` true
54− it "inserts many records at once" $ withUsersTable \conn -> do
53+ it "inserts many records at once" $ withUsersTable do
55 let noUsers = [] :: Array { name :: String, email :: String, role :: String }
54 let noUsers = [] :: Array { name :: String, email :: String, role :: String }
56− empty <- conn # run do usersTable # createMany noUsers
55+ empty <- usersTable # createMany noUsers
57 Array.length empty `shouldEqual` 0
56 Array.length empty `shouldEqual` 0
58− users <- conn # run do
59− usersTable # createMany
57+ users <- usersTable # createMany
60− [ { name: "Alice", email: "a@t.com", role: "admin" }
58+ [ { name: "Alice", email: "a@t.com", role: "admin" }
61− , { name: "Bob", email: "b@t.com", role: "user" }
59+ , { name: "Bob", email: "b@t.com", role: "user" }
62− , { name: "Carol", email: "c@t.com", role: "user" }
60+ , { name: "Carol", email: "c@t.com", role: "user" }
64 Array.length users `shouldEqual` 3
62 Array.length users `shouldEqual` 3
65− n <- conn # runNoErr do usersTable # count
63+ n <- usersTable # count
66 n `shouldEqual` 3
64 n `shouldEqual` 3
68− it "skips insert when a conflict would occur" $ withSettingsTable \conn -> do
66+ it "skips insert when a conflict would occur" $ withSettingsTable do
69− inserted <- conn # run do settingsTable # createIfNotExists { key: "theme", value: "dark" }
67+ inserted <- settingsTable # createIfNotExists { key: "theme", value: "dark" }
70 r1 <- shouldBeJust inserted
68 r1 <- shouldBeJust inserted
71 r1.value `shouldEqual` "dark"
69 r1.value `shouldEqual` "dark"
72− skipped <- conn # run do settingsTable # createIfNotExists { key: "theme", value: "light" }
70+ skipped <- settingsTable # createIfNotExists { key: "theme", value: "light" }
73 isNothing skipped `shouldEqual` true
71 isNothing skipped `shouldEqual` true
74− found <- conn # run do settingsTable # findOneBy @"key" "theme"
72+ found <- settingsTable # findOneBy @"key" "theme"
75 existing <- shouldBeJust found
73 existing <- shouldBeJust found
76 existing.value `shouldEqual` "dark"
74 existing.value `shouldEqual` "dark"
78− it "upserts: inserts or updates on conflict" $ withSettingsTable \conn -> do
76+ it "upserts: inserts or updates on conflict" $ withSettingsTable do
79− inserted <- conn # run do settingsTable # upsert { key: "theme", value: "dark" }
77+ inserted <- settingsTable # upsert { key: "theme", value: "dark" }
80 r1 <- shouldBeJust inserted
78 r1 <- shouldBeJust inserted
81 r1.value `shouldEqual` "dark"
79 r1.value `shouldEqual` "dark"
82− updated <- conn # run do settingsTable # upsert { key: "theme", value: "light" }
80+ updated <- settingsTable # upsert { key: "theme", value: "light" }
83 r2 <- shouldBeJust updated
81 r2 <- shouldBeJust updated
84 r2.value `shouldEqual` "light"
82 r2.value `shouldEqual` "light"
85− n <- conn # runNoErr do settingsTable # count
83+ n <- settingsTable # count
86 n `shouldEqual` 1
84 n `shouldEqual` 1
88− it "bulk upserts a batch" $ withSettingsTable \conn -> do
86+ it "bulk upserts a batch" $ withSettingsTable do
90− settingsTable # upsertMany
87+ _ <- settingsTable # upsertMany
91− [ { key: "theme", value: "dark" }
88+ [ { key: "theme", value: "dark" }
92− , { key: "lang", value: "en" }
89+ , { key: "lang", value: "en" }
94− n1 <- conn # runNoErr do settingsTable # count
91+ n1 <- settingsTable # count
95 n1 `shouldEqual` 2
92 n1 `shouldEqual` 2
96− updated <- conn # run do
97− settingsTable # upsertMany
93+ updated <- settingsTable # upsertMany
98− [ { key: "theme", value: "light" }
94+ [ { key: "theme", value: "light" }
99− , { key: "lang", value: "de" }
95+ , { key: "lang", value: "de" }
100− , { key: "tz", value: "UTC" }
96+ , { key: "tz", value: "UTC" }
102 Array.length updated `shouldEqual` 3
98 Array.length updated `shouldEqual` 3
103− n2 <- conn # runNoErr do settingsTable # count
99+ n2 <- settingsTable # count
104 n2 `shouldEqual` 3
100 n2 `shouldEqual` 3
105− found <- conn # run do settingsTable # findOneBy @"key" "theme"
101+ found <- settingsTable # findOneBy @"key" "theme"
106 setting <- shouldBeJust found
102 setting <- shouldBeJust found
107 setting.value `shouldEqual` "light"
103 setting.value `shouldEqual` "light"
109 describe "Finding records" do
105 describe "Finding records" do
110− it "finds a single record by primary key" $ withUsersTable \conn -> do
106+ it "finds a single record by primary key" $ withUsersTable do
111− created <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
107+ created <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
112 alice <- shouldBeJust created
108 alice <- shouldBeJust created
113− found <- conn # run do usersTable # findById alice.id
109+ found <- usersTable # findById alice.id
114 r <- shouldBeJust found
110 r <- shouldBeJust found
115 r.name `shouldEqual` "Alice"
111 r.name `shouldEqual` "Alice"
117− it "finds a single record by any unique column" $ withUsersTable \conn -> do
113+ it "finds a single record by any unique column" $ withUsersTable do
118− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
114+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
119− found <- conn # run do usersTable # findOneBy @"email" "a@t.com"
115+ found <- usersTable # findOneBy @"email" "a@t.com"
120 alice <- shouldBeJust found
116 alice <- shouldBeJust found
121 alice.name `shouldEqual` "Alice"
117 alice.name `shouldEqual` "Alice"
123− it "finds all records matching a column value" $ withUsersTable \conn -> do
119+ it "finds all records matching a column value" $ withUsersTable do
124− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
120+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
125− _ <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "admin" }
121+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "admin" }
126− _ <- conn # run do usersTable # create { name: "Carol", email: "c@t.com", role: "user" }
122+ _ <- usersTable # create { name: "Carol", email: "c@t.com", role: "user" }
127− admins <- conn # run do usersTable # findBy @"role" "admin"
123+ admins <- usersTable # findBy @"role" "admin"
128 Array.length admins `shouldEqual` 2
124 Array.length admins `shouldEqual` 2
130− it "finds all records matching multiple conditions" $ withUsersTable \conn -> do
126+ it "finds all records matching multiple conditions" $ withUsersTable do
131− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
127+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
132− _ <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "admin" }
128+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "admin" }
133− _ <- conn # run do usersTable # create { name: "Carol", email: "c@t.com", role: "user" }
129+ _ <- usersTable # create { name: "Carol", email: "c@t.com", role: "user" }
134− alices <- conn # run do usersTable # findAllWhere { name: "Alice", role: "admin" }
130+ alices <- usersTable # findAllWhere { name: "Alice", role: "admin" }
135 Array.length alices `shouldEqual` 1
131 Array.length alices `shouldEqual` 1
136 alice <- shouldBeJust (Array.head alices)
132 alice <- shouldBeJust (Array.head alices)
137 alice.email `shouldEqual` "a@t.com"
133 alice.email `shouldEqual` "a@t.com"
139− it "returns Nothing when no record matches" $ withUsersTable \conn -> do
135+ it "returns Nothing when no record matches" $ withUsersTable do
140− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
136+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
141− missing <- conn # run do usersTable # findOneBy @"email" "nobody@t.com"
137+ missing <- usersTable # findOneBy @"email" "nobody@t.com"
142 isNothing missing `shouldEqual` true
138 isNothing missing `shouldEqual` true
143− alsoMissing <- conn # run do usersTable # findOneWhere { name: "Alice", role: "user" }
139+ alsoMissing <- usersTable # findOneWhere { name: "Alice", role: "user" }
144 isNothing alsoMissing `shouldEqual` true
140 isNothing alsoMissing `shouldEqual` true
146− it "finds records by a list of primary keys" $ withUsersTable \conn -> do
142+ it "finds records by a list of primary keys" $ withUsersTable do
147− created1 <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
143+ created1 <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
148 u1 <- shouldBeJust created1
144 u1 <- shouldBeJust created1
149− created2 <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
145+ created2 <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
150 u2 <- shouldBeJust created2
146 u2 <- shouldBeJust created2
151− _ <- conn # run do usersTable # create { name: "Carol", email: "c@t.com", role: "user" }
147+ _ <- usersTable # create { name: "Carol", email: "c@t.com", role: "user" }
152− found <- conn # run do usersTable # findByIdIn [ u1.id, u2.id ]
148+ found <- usersTable # findByIdIn [ u1.id, u2.id ]
153 Array.length found `shouldEqual` 2
149 Array.length found `shouldEqual` 2
154− empty <- conn # run do usersTable # findByIdIn ([] :: Array Int)
150+ empty <- usersTable # findByIdIn ([] :: Array Int)
155 Array.length empty `shouldEqual` 0
151 Array.length empty `shouldEqual` 0
157− it "returns all records in the table" $ withUsersTable \conn -> do
153+ it "returns all records in the table" $ withUsersTable do
159− usersTable # createMany
154+ _ <- usersTable # createMany
160− [ { name: "Alice", email: "a@t.com", role: "admin" }
155+ [ { name: "Alice", email: "a@t.com", role: "admin" }
161− , { name: "Bob", email: "b@t.com", role: "user" }
156+ , { name: "Bob", email: "b@t.com", role: "user" }
163− all <- conn # run do usersTable # findAll
158+ all <- usersTable # findAll
164 Array.length all `shouldEqual` 2
159 Array.length all `shouldEqual` 2
166 describe "Filtering with operators" do
161 describe "Filtering with operators" do
167− it "compares with gt, gte, lt, lte" $ withUsersTable \conn -> do
162+ it "compares with gt, gte, lt, lte" $ withUsersTable do
169− usersTable # createMany
163+ _ <- usersTable # createMany
170− [ { name: "Alice", email: "a@t.com", role: "admin" }
164+ [ { name: "Alice", email: "a@t.com", role: "admin" }
171− , { name: "Bob", email: "b@t.com", role: "user" }
165+ , { name: "Bob", email: "b@t.com", role: "user" }
172− , { name: "Carol", email: "c@t.com", role: "user" }
166+ , { name: "Carol", email: "c@t.com", role: "user" }
174− all <- conn # run do usersTable # findAll
168+ all <- usersTable # findAll
176 id1 = case Array.head all of
170 id1 = case Array.head all of
177 Just u -> u.id
171 Just u -> u.id
178 Nothing -> 0
172 Nothing -> 0
179− gtRows <- conn # run do usersTable # findAllWhere { id: gt id1 }
173+ gtRows <- usersTable # findAllWhere { id: gt id1 }
180 Array.length gtRows `shouldEqual` 2
174 Array.length gtRows `shouldEqual` 2
181− gteRows <- conn # run do usersTable # findAllWhere { id: gte id1 }
175+ gteRows <- usersTable # findAllWhere { id: gte id1 }
182 Array.length gteRows `shouldEqual` 3
176 Array.length gteRows `shouldEqual` 3
183− ltRows <- conn # run do usersTable # findAllWhere { id: lt (id1 + 3) }
177+ ltRows <- usersTable # findAllWhere { id: lt (id1 + 3) }
184 Array.length ltRows `shouldEqual` 3
178 Array.length ltRows `shouldEqual` 3
185− lteRows <- conn # run do usersTable # findAllWhere { id: lte id1 }
179+ lteRows <- usersTable # findAllWhere { id: lte id1 }
186 Array.length lteRows `shouldEqual` 1
180 Array.length lteRows `shouldEqual` 1
188− it "excludes with ne" $ withUsersTable \conn -> do
182+ it "excludes with ne" $ withUsersTable do
190− usersTable # createMany
183+ _ <- usersTable # createMany
191− [ { name: "Alice", email: "a@t.com", role: "admin" }
184+ [ { name: "Alice", email: "a@t.com", role: "admin" }
192− , { name: "Bob", email: "b@t.com", role: "user" }
185+ , { name: "Bob", email: "b@t.com", role: "user" }
193− , { name: "Carol", email: "c@t.com", role: "user" }
186+ , { name: "Carol", email: "c@t.com", role: "user" }
195− rows <- conn # run do usersTable # findAllWhere { role: ne "admin" }
188+ rows <- usersTable # findAllWhere { role: ne "admin" }
196 Array.length rows `shouldEqual` 2
189 Array.length rows `shouldEqual` 2
198− it "pattern-matches with like_ and ilike" $ withUsersTable \conn -> do
191+ it "pattern-matches with like_ and ilike" $ withUsersTable do
200− usersTable # createMany
192+ _ <- usersTable # createMany
201− [ { name: "Alice", email: "a@t.com", role: "admin" }
193+ [ { name: "Alice", email: "a@t.com", role: "admin" }
202− , { name: "Bob", email: "b@t.com", role: "user" }
194+ , { name: "Bob", email: "b@t.com", role: "user" }
203− , { name: "Carol", email: "c@t.com", role: "user" }
195+ , { name: "Carol", email: "c@t.com", role: "user" }
205− likeRows <- conn # run do usersTable # findAllWhere { name: like_ "A%" }
197+ likeRows <- usersTable # findAllWhere { name: like_ "A%" }
206 Array.length likeRows `shouldEqual` 1
198 Array.length likeRows `shouldEqual` 1
207− ilikeRows <- conn # run do usersTable # findAllWhere { name: ilike "a%" }
199+ ilikeRows <- usersTable # findAllWhere { name: ilike "a%" }
208 Array.length ilikeRows `shouldEqual` 1
200 Array.length ilikeRows `shouldEqual` 1
210− it "checks membership with inArray" $ withUsersTable \conn -> do
202+ it "checks membership with inArray" $ withUsersTable do
212− usersTable # createMany
203+ _ <- usersTable # createMany
213− [ { name: "Alice", email: "a@t.com", role: "admin" }
204+ [ { name: "Alice", email: "a@t.com", role: "admin" }
214− , { name: "Bob", email: "b@t.com", role: "user" }
205+ , { name: "Bob", email: "b@t.com", role: "user" }
215− , { name: "Carol", email: "c@t.com", role: "mod" }
206+ , { name: "Carol", email: "c@t.com", role: "mod" }
217− rows <- conn # run do usersTable # findAllWhere { role: inArray [ "admin", "mod" ] }
208+ rows <- usersTable # findAllWhere { role: inArray [ "admin", "mod" ] }
218 Array.length rows `shouldEqual` 2
209 Array.length rows `shouldEqual` 2
220− it "combines conditions with or_" $ withUsersTable \conn -> do
211+ it "combines conditions with or_" $ withUsersTable do
222− usersTable # createMany
212+ _ <- usersTable # createMany
223− [ { name: "Alice", email: "a@t.com", role: "admin" }
213+ [ { name: "Alice", email: "a@t.com", role: "admin" }
224− , { name: "Bob", email: "b@t.com", role: "user" }
214+ , { name: "Bob", email: "b@t.com", role: "user" }
225− , { name: "Carol", email: "c@t.com", role: "mod" }
215+ , { name: "Carol", email: "c@t.com", role: "mod" }
227− rows <- conn # run do usersTable # findAllWhere { role: or_ (eq_ "admin") (eq_ "mod") }
217+ rows <- usersTable # findAllWhere { role: or_ (eq_ "admin") (eq_ "mod") }
228 Array.length rows `shouldEqual` 2
218 Array.length rows `shouldEqual` 2
230− it "negates conditions with not_" $ withUsersTable \conn -> do
220+ it "negates conditions with not_" $ withUsersTable do
232− usersTable # createMany
221+ _ <- usersTable # createMany
233− [ { name: "Alice", email: "a@t.com", role: "admin" }
222+ [ { name: "Alice", email: "a@t.com", role: "admin" }
234− , { name: "Bob", email: "b@t.com", role: "user" }
223+ , { name: "Bob", email: "b@t.com", role: "user" }
235− , { name: "Carol", email: "c@t.com", role: "user" }
224+ , { name: "Carol", email: "c@t.com", role: "user" }
237− rows <- conn # run do usersTable # findAllWhere { role: not_ (eq_ "admin") }
226+ rows <- usersTable # findAllWhere { role: not_ (eq_ "admin") }
238 Array.length rows `shouldEqual` 2
227 Array.length rows `shouldEqual` 2
240 describe "Updating and deleting" do
229 describe "Updating and deleting" do
241− it "updates rows matching a condition" $ withUsersTable \conn -> do
230+ it "updates rows matching a condition" $ withUsersTable do
243− usersTable # createMany
231+ _ <- usersTable # createMany
244− [ { name: "Alice", email: "a@t.com", role: "admin" }
232+ [ { name: "Alice", email: "a@t.com", role: "admin" }
245− , { name: "Bob", email: "b@t.com", role: "user" }
233+ , { name: "Bob", email: "b@t.com", role: "user" }
246− , { name: "Carol", email: "c@t.com", role: "user" }
234+ , { name: "Carol", email: "c@t.com", role: "user" }
248− updated <- conn # run do usersTable # updateWhere { role: "user" } { role: "member" }
236+ updated <- usersTable # updateWhere { role: "user" } { role: "member" }
249 Array.length updated `shouldEqual` 2
237 Array.length updated `shouldEqual` 2
250− members <- conn # run do usersTable # findBy @"role" "member"
238+ members <- usersTable # findBy @"role" "member"
251 Array.length members `shouldEqual` 2
239 Array.length members `shouldEqual` 2
253− it "deletes rows matching a condition" $ withUsersTable \conn -> do
241+ it "deletes rows matching a condition" $ withUsersTable do
255− usersTable # createMany
242+ _ <- usersTable # createMany
256− [ { name: "Alice", email: "a@t.com", role: "admin" }
243+ [ { name: "Alice", email: "a@t.com", role: "admin" }
257− , { name: "Bob", email: "b@t.com", role: "user" }
244+ , { name: "Bob", email: "b@t.com", role: "user" }
258− , { name: "Carol", email: "c@t.com", role: "user" }
245+ , { name: "Carol", email: "c@t.com", role: "user" }
260− deleted <- conn # runNoErr do usersTable # deleteWhere { role: "user" }
247+ deleted <- usersTable # deleteWhere { role: "user" }
261 deleted `shouldEqual` 2
248 deleted `shouldEqual` 2
262− remaining <- conn # runNoErr do usersTable # count
249+ remaining <- usersTable # count
263 remaining `shouldEqual` 1
250 remaining `shouldEqual` 1
265− it "deletes and returns the deleted rows" $ withUsersTable \conn -> do
252+ it "deletes and returns the deleted rows" $ withUsersTable do
267− usersTable # createMany
253+ _ <- usersTable # createMany
268− [ { name: "Alice", email: "a@t.com", role: "admin" }
254+ [ { name: "Alice", email: "a@t.com", role: "admin" }
269− , { name: "Bob", email: "b@t.com", role: "user" }
255+ , { name: "Bob", email: "b@t.com", role: "user" }
270− , { name: "Carol", email: "c@t.com", role: "user" }
256+ , { name: "Carol", email: "c@t.com", role: "user" }
272− deleted <- conn # run do usersTable # deleteWhereReturning { role: "user" }
258+ deleted <- usersTable # deleteWhereReturning { role: "user" }
273 Array.length deleted `shouldEqual` 2
259 Array.length deleted `shouldEqual` 2
274 Array.sort (map _.name deleted) `shouldEqual` [ "Bob", "Carol" ]
260 Array.sort (map _.name deleted) `shouldEqual` [ "Bob", "Carol" ]
275− remaining <- conn # runNoErr do usersTable # count
261+ remaining <- usersTable # count
276 remaining `shouldEqual` 1
262 remaining `shouldEqual` 1
278− it "deletes everything" $ withUsersTable \conn -> do
264+ it "deletes everything" $ withUsersTable do
279− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
265+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
280− _ <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
266+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
281− deleted <- conn # runNoErr do usersTable # deleteAll
267+ deleted <- usersTable # deleteAll
282 deleted `shouldEqual` 2
268 deleted `shouldEqual` 2
283− n <- conn # runNoErr do usersTable # count
269+ n <- usersTable # count
284 n `shouldEqual` 0
270 n `shouldEqual` 0
286 describe "Ordering and pagination" do
272 describe "Ordering and pagination" do
287− it "orders results by a column" $ withUsersTable \conn -> do
273+ it "orders results by a column" $ withUsersTable do
289− usersTable # createMany
274+ _ <- usersTable # createMany
290− [ { name: "Carol", email: "c@t.com", role: "user" }
275+ [ { name: "Carol", email: "c@t.com", role: "user" }
291− , { name: "Alice", email: "a@t.com", role: "admin" }
276+ , { name: "Alice", email: "a@t.com", role: "admin" }
292− , { name: "Bob", email: "b@t.com", role: "user" }
277+ , { name: "Bob", email: "b@t.com", role: "user" }
294− asc <- conn # run do usersTable # findOrdered @"name" Asc
279+ asc <- usersTable # findOrdered @"name" Asc
295 (map _.name asc) `shouldEqual` [ "Alice", "Bob", "Carol" ]
280 (map _.name asc) `shouldEqual` [ "Alice", "Bob", "Carol" ]
296− desc <- conn # run do usersTable # findOrdered @"name" Desc
281+ desc <- usersTable # findOrdered @"name" Desc
297 (map _.name desc) `shouldEqual` [ "Carol", "Bob", "Alice" ]
282 (map _.name desc) `shouldEqual` [ "Carol", "Bob", "Alice" ]
299− it "filters, orders, and limits in one call" $ withUsersTable \conn -> do
284+ it "filters, orders, and limits in one call" $ withUsersTable do
301− usersTable # createMany
285+ _ <- usersTable # createMany
302− [ { name: "Alice", email: "a@t.com", role: "user" }
286+ [ { name: "Alice", email: "a@t.com", role: "user" }
303− , { name: "Bob", email: "b@t.com", role: "user" }
287+ , { name: "Bob", email: "b@t.com", role: "user" }
304− , { name: "Carol", email: "c@t.com", role: "user" }
288+ , { name: "Carol", email: "c@t.com", role: "user" }
305− , { name: "Dave", email: "d@t.com", role: "user" }
289+ , { name: "Dave", email: "d@t.com", role: "user" }
307− page1 <- conn # run do usersTable # findWhereLimited @"name" { role: "user" } Asc { limit: 2, offset: 0 }
291+ page1 <- usersTable # findWhereLimited @"name" { role: "user" } Asc { limit: 2, offset: 0 }
308 (map _.name page1) `shouldEqual` [ "Alice", "Bob" ]
292 (map _.name page1) `shouldEqual` [ "Alice", "Bob" ]
309− page2 <- conn # run do usersTable # findWhereLimited @"name" { role: "user" } Asc { limit: 2, offset: 2 }
293+ page2 <- usersTable # findWhereLimited @"name" { role: "user" } Asc { limit: 2, offset: 2 }
310 (map _.name page2) `shouldEqual` [ "Carol", "Dave" ]
294 (map _.name page2) `shouldEqual` [ "Carol", "Dave" ]
312− it "paginates forward with cursors" $ withUsersTable \conn -> do
296+ it "paginates forward with cursors" $ withUsersTable do
314− usersTable # createMany
297+ _ <- usersTable # createMany
315− [ { name: "Alice", email: "a@t.com", role: "admin" }
298+ [ { name: "Alice", email: "a@t.com", role: "admin" }
316− , { name: "Bob", email: "b@t.com", role: "user" }
299+ , { name: "Bob", email: "b@t.com", role: "user" }
317− , { name: "Carol", email: "c@t.com", role: "user" }
300+ , { name: "Carol", email: "c@t.com", role: "user" }
318− , { name: "Dave", email: "d@t.com", role: "user" }
301+ , { name: "Dave", email: "d@t.com", role: "user" }
319− , { name: "Eve", email: "e@t.com", role: "admin" }
302+ , { name: "Eve", email: "e@t.com", role: "admin" }
321− page1 <- conn # run do usersTable # findFirst @"id" 2
304+ page1 <- usersTable # findFirst @"id" 2
322 Array.length page1.items `shouldEqual` 2
305 Array.length page1.items `shouldEqual` 2
323 page1.hasMore `shouldEqual` true
306 page1.hasMore `shouldEqual` true
325 lastId = case Array.last page1.items of
308 lastId = case Array.last page1.items of
326 Just u -> u.id
309 Just u -> u.id
327 Nothing -> 0
310 Nothing -> 0
328− page2 <- conn # run do usersTable # findAfter @"id" lastId 2
311+ page2 <- usersTable # findAfter @"id" lastId 2
329 Array.length page2.items `shouldEqual` 2
312 Array.length page2.items `shouldEqual` 2
330 page2.hasMore `shouldEqual` true
313 page2.hasMore `shouldEqual` true
332 lastId2 = case Array.last page2.items of
315 lastId2 = case Array.last page2.items of
333 Just u -> u.id
316 Just u -> u.id
334 Nothing -> 0
317 Nothing -> 0
335− page3 <- conn # run do usersTable # findAfter @"id" lastId2 2
318+ page3 <- usersTable # findAfter @"id" lastId2 2
336 Array.length page3.items `shouldEqual` 1
319 Array.length page3.items `shouldEqual` 1
337 page3.hasMore `shouldEqual` false
320 page3.hasMore `shouldEqual` false
339− it "paginates backward with cursors" $ withUsersTable \conn -> do
322+ it "paginates backward with cursors" $ withUsersTable do
341− usersTable # createMany
323+ _ <- usersTable # createMany
342− [ { name: "Alice", email: "a@t.com", role: "admin" }
324+ [ { name: "Alice", email: "a@t.com", role: "admin" }
343− , { name: "Bob", email: "b@t.com", role: "user" }
325+ , { name: "Bob", email: "b@t.com", role: "user" }
344− , { name: "Carol", email: "c@t.com", role: "user" }
326+ , { name: "Carol", email: "c@t.com", role: "user" }
345− , { name: "Dave", email: "d@t.com", role: "user" }
327+ , { name: "Dave", email: "d@t.com", role: "user" }
346− , { name: "Eve", email: "e@t.com", role: "admin" }
328+ , { name: "Eve", email: "e@t.com", role: "admin" }
348− all <- conn # run do usersTable # findAll
330+ all <- usersTable # findAll
350 lastUser = case Array.last all of
332 lastUser = case Array.last all of
351 Just u -> u
333 Just u -> u
352 Nothing -> { id: 0, name: "", email: "", active: true, role: "" }
334 Nothing -> { id: 0, name: "", email: "", active: true, role: "" }
353− page <- conn # run do usersTable # findBefore @"id" lastUser.id 2
335+ page <- usersTable # findBefore @"id" lastUser.id 2
354 Array.length page.items `shouldEqual` 2
336 Array.length page.items `shouldEqual` 2
355 page.hasMore `shouldEqual` true
337 page.hasMore `shouldEqual` true
357− it "paginates with a filter applied" $ withUsersTable \conn -> do
339+ it "paginates with a filter applied" $ withUsersTable do
359− usersTable # createMany
340+ _ <- usersTable # createMany
360− [ { name: "Alice", email: "a@t.com", role: "admin" }
341+ [ { name: "Alice", email: "a@t.com", role: "admin" }
361− , { name: "Bob", email: "b@t.com", role: "user" }
342+ , { name: "Bob", email: "b@t.com", role: "user" }
362− , { name: "Carol", email: "c@t.com", role: "user" }
343+ , { name: "Carol", email: "c@t.com", role: "user" }
363− , { name: "Dave", email: "d@t.com", role: "user" }
344+ , { name: "Dave", email: "d@t.com", role: "user" }
364− , { name: "Eve", email: "e@t.com", role: "admin" }
345+ , { name: "Eve", email: "e@t.com", role: "admin" }
366− page1 <- conn # run do usersTable # findFirstWhere @"id" { role: "user" } 2
347+ page1 <- usersTable # findFirstWhere @"id" { role: "user" } 2
367 Array.length page1.items `shouldEqual` 2
348 Array.length page1.items `shouldEqual` 2
368 page1.hasMore `shouldEqual` true
349 page1.hasMore `shouldEqual` true
370 lastId = case Array.last page1.items of
351 lastId = case Array.last page1.items of
371 Just u -> u.id
352 Just u -> u.id
372 Nothing -> 0
353 Nothing -> 0
373− page2 <- conn # run do usersTable # findAfterWhere @"id" { role: "user" } lastId 2
354+ page2 <- usersTable # findAfterWhere @"id" { role: "user" } lastId 2
374 Array.length page2.items `shouldEqual` 1
355 Array.length page2.items `shouldEqual` 1
375 page2.hasMore `shouldEqual` false
356 page2.hasMore `shouldEqual` false
377 describe "Aggregations and checks" do
358 describe "Aggregations and checks" do
378− it "counts rows, optionally with a filter" $ withUsersTable \conn -> do
359+ it "counts rows, optionally with a filter" $ withUsersTable do
379− n0 <- conn # runNoErr do usersTable # count
360+ n0 <- usersTable # count
380 n0 `shouldEqual` 0
361 n0 `shouldEqual` 0
382− usersTable # createMany
362+ _ <- usersTable # createMany
383− [ { name: "Alice", email: "a@t.com", role: "admin" }
363+ [ { name: "Alice", email: "a@t.com", role: "admin" }
384− , { name: "Bob", email: "b@t.com", role: "user" }
364+ , { name: "Bob", email: "b@t.com", role: "user" }
385− , { name: "Carol", email: "c@t.com", role: "admin" }
365+ , { name: "Carol", email: "c@t.com", role: "admin" }
387− n3 <- conn # runNoErr do usersTable # count
367+ n3 <- usersTable # count
388 n3 `shouldEqual` 3
368 n3 `shouldEqual` 3
389− nAdmins <- conn # runNoErr do usersTable # countWhere { role: "admin" }
369+ nAdmins <- usersTable # countWhere { role: "admin" }
390 nAdmins `shouldEqual` 2
370 nAdmins `shouldEqual` 2
392− it "checks whether a record exists" $ withUsersTable \conn -> do
372+ it "checks whether a record exists" $ withUsersTable do
393− created <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
373+ created <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
394 alice <- shouldBeJust created
374 alice <- shouldBeJust created
395− yes <- conn # runNoErr do usersTable # exists alice.id
375+ yes <- usersTable # exists alice.id
396 yes `shouldEqual` true
376 yes `shouldEqual` true
397− no <- conn # runNoErr do usersTable # exists 99999
377+ no <- usersTable # exists 99999
398 no `shouldEqual` false
378 no `shouldEqual` false
399− yesWhere <- conn # runNoErr do usersTable # existsWhere { name: "Alice", role: "admin" }
379+ yesWhere <- usersTable # existsWhere { name: "Alice", role: "admin" }
400 yesWhere `shouldEqual` true
380 yesWhere `shouldEqual` true
401− noWhere <- conn # runNoErr do usersTable # existsWhere { name: "Alice", role: "user" }
381+ noWhere <- usersTable # existsWhere { name: "Alice", role: "user" }
402 noWhere `shouldEqual` false
382 noWhere `shouldEqual` false
404− it "sums and averages a numeric column" $ withUsersTable \conn -> do
384+ it "sums and averages a numeric column" $ withUsersTable do
406− usersTable # createMany
385+ _ <- usersTable # createMany
407− [ { name: "Alice", email: "a@t.com", role: "admin" }
386+ [ { name: "Alice", email: "a@t.com", role: "admin" }
408− , { name: "Bob", email: "b@t.com", role: "user" }
387+ , { name: "Bob", email: "b@t.com", role: "user" }
410− total <- conn # runNoErr do usersTable # sumOf @"id"
389+ total <- usersTable # sumOf @"id"
411 (total > 0.0) `shouldEqual` true
390 (total > 0.0) `shouldEqual` true
412− emptySum <- conn # runNoErr do usersTable # sumOfWhere @"id" { role: "nobody" }
391+ emptySum <- usersTable # sumOfWhere @"id" { role: "nobody" }
413 emptySum `shouldEqual` 0.0
392 emptySum `shouldEqual` 0.0
414− avg <- conn # runNoErr do usersTable # avgOf @"id"
393+ avg <- usersTable # avgOf @"id"
415 isNothing avg `shouldEqual` false
394 isNothing avg `shouldEqual` false
416− emptyAvg <- conn # runNoErr do usersTable # avgOfWhere @"id" { role: "nobody" }
395+ emptyAvg <- usersTable # avgOfWhere @"id" { role: "nobody" }
417 isNothing emptyAvg `shouldEqual` true
396 isNothing emptyAvg `shouldEqual` true
419− it "finds the min and max of a column" $ withUsersTable \conn -> do
398+ it "finds the min and max of a column" $ withUsersTable do
421− usersTable # createMany
399+ _ <- usersTable # createMany
422− [ { name: "Alice", email: "a@t.com", role: "admin" }
400+ [ { name: "Alice", email: "a@t.com", role: "admin" }
423− , { name: "Bob", email: "b@t.com", role: "user" }
401+ , { name: "Bob", email: "b@t.com", role: "user" }
424− , { name: "Carol", email: "c@t.com", role: "user" }
402+ , { name: "Carol", email: "c@t.com", role: "user" }
426− minName <- conn # run do usersTable # minOf @"name"
404+ minName <- usersTable # minOf @"name"
427 minName `shouldEqual` (Just "Alice")
405 minName `shouldEqual` (Just "Alice")
428− maxName <- conn # run do usersTable # maxOf @"name"
406+ maxName <- usersTable # maxOf @"name"
429 maxName `shouldEqual` (Just "Carol")
407 maxName `shouldEqual` (Just "Carol")
430− noMin <- conn # run do usersTable # minOfWhere @"name" { role: "nobody" }
408+ noMin <- usersTable # minOfWhere @"name" { role: "nobody" }
431 isNothing noMin `shouldEqual` true
409 isNothing noMin `shouldEqual` true
433− it "lists distinct values of a column" $ withUsersTable \conn -> do
411+ it "lists distinct values of a column" $ withUsersTable do
435− usersTable # createMany
412+ _ <- usersTable # createMany
436− [ { name: "Alice", email: "a@t.com", role: "admin" }
413+ [ { name: "Alice", email: "a@t.com", role: "admin" }
437− , { name: "Bob", email: "b@t.com", role: "user" }
414+ , { name: "Bob", email: "b@t.com", role: "user" }
438− , { name: "Carol", email: "c@t.com", role: "user" }
415+ , { name: "Carol", email: "c@t.com", role: "user" }
440− roles <- conn # run do usersTable # findDistinct @"role"
417+ roles <- usersTable # findDistinct @"role"
441 Array.sort roles `shouldEqual` [ "admin", "user" ]
418 Array.sort roles `shouldEqual` [ "admin", "user" ]
442− activeRoles <- conn # run do usersTable # findDistinctWhere @"role" { active: true }
419+ activeRoles <- usersTable # findDistinctWhere @"role" { active: true }
443 Array.sort activeRoles `shouldEqual` [ "admin", "user" ]
420 Array.sort activeRoles `shouldEqual` [ "admin", "user" ]
445 describe "Selecting specific columns" do
422 describe "Selecting specific columns" do
446− it "fetches only the columns you need" $ withUsersTable \conn -> do
423+ it "fetches only the columns you need" $ withUsersTable do
447− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
424+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
448− _ <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
425+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
449− _ <- conn # run do usersTable # create { name: "Carol", email: "c@t.com", role: "user" }
426+ _ <- usersTable # create { name: "Carol", email: "c@t.com", role: "user" }
450− rows <- conn # run do usersTable # selectFrom @"name, email"
427+ rows <- usersTable # selectFrom @"name, email"
451 Array.length rows `shouldEqual` 3
428 Array.length rows `shouldEqual` 3
453 first = case Array.head rows of
430 first = case Array.head rows of
@@ -455,31 +432,31 @@ spec = describe "ClientOm" do
455 Nothing -> { name: "", email: "" }
432 Nothing -> { name: "", email: "" }
456 first.name `shouldEqual` "Alice"
433 first.name `shouldEqual` "Alice"
457 first.email `shouldEqual` "a@t.com"
434 first.email `shouldEqual` "a@t.com"
458− filtered <- conn # run do usersTable # selectFromWhere @"name" { role: "user" }
435+ filtered <- usersTable # selectFromWhere @"name" { role: "user" }
459 Array.length filtered `shouldEqual` 2
436 Array.length filtered `shouldEqual` 2
460 Array.sort (map _.name filtered) `shouldEqual` [ "Bob", "Carol" ]
437 Array.sort (map _.name filtered) `shouldEqual` [ "Bob", "Carol" ]
462− it "fetches one row with specific columns and a filter" $ withUsersTable \conn -> do
439+ it "fetches one row with specific columns and a filter" $ withUsersTable do
463− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
440+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
464− _ <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
441+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
465− found <- conn # run do usersTable # selectOneFrom @"name, role"
442+ found <- usersTable # selectOneFrom @"name, role"
466 r <- shouldBeJust found
443 r <- shouldBeJust found
467 r.name `shouldEqual` "Alice"
444 r.name `shouldEqual` "Alice"
468 r.role `shouldEqual` "admin"
445 r.role `shouldEqual` "admin"
469− byName <- conn # run do usersTable # selectOneFromWhere @"email" { name: "Bob" }
446+ byName <- usersTable # selectOneFromWhere @"email" { name: "Bob" }
470 r2 <- shouldBeJust byName
447 r2 <- shouldBeJust byName
471 r2.email `shouldEqual` "b@t.com"
448 r2.email `shouldEqual` "b@t.com"
472− missing <- conn # run do usersTable # selectOneFromWhere @"email" { name: "Nobody" }
449+ missing <- usersTable # selectOneFromWhere @"email" { name: "Nobody" }
473 isNothing missing `shouldEqual` true
450 isNothing missing `shouldEqual` true
475 describe "Relationships" do
452 describe "Relationships" do
476− it "joins a child table to its parent (flat)" $ withPostsTable \conn -> do
453+ it "joins a child table to its parent (flat)" $ withPostsTable do
477− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
454+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
478− _ <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
455+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
479− _ <- conn # run do postsTable # create { title: "First Post", body: "Hello", user_id: 1 }
456+ _ <- postsTable # create { title: "First Post", body: "Hello", user_id: 1 }
480− _ <- conn # run do postsTable # create { title: "Second Post", body: "World", user_id: 1 }
457+ _ <- postsTable # create { title: "Second Post", body: "World", user_id: 1 }
481− _ <- conn # run do postsTable # create { title: "Bob's Post", body: "Hi", user_id: 2 }
458+ _ <- postsTable # create { title: "Bob's Post", body: "Hi", user_id: 2 }
482− rows <- conn # run do findWith postsTable usersTable
459+ rows <- findWith postsTable usersTable
483 Array.length rows `shouldEqual` 3
460 Array.length rows `shouldEqual` 3
485 r = case Array.head rows of
462 r = case Array.head rows of
@@ -488,20 +465,20 @@ spec = describe "ClientOm" do
488 r.test_om_posts.title `shouldEqual` "First Post"
465 r.test_om_posts.title `shouldEqual` "First Post"
489 r.test_om_users.name `shouldEqual` "Alice"
466 r.test_om_users.name `shouldEqual` "Alice"
491− it "finds a joined record by primary key" $ withPostsTable \conn -> do
468+ it "finds a joined record by primary key" $ withPostsTable do
492− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
469+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
493− _ <- conn # run do postsTable # create { title: "My Post", body: "Content", user_id: 1 }
470+ _ <- postsTable # create { title: "My Post", body: "Content", user_id: 1 }
494− found <- conn # run do findByIdWith 1 postsTable usersTable
471+ found <- findByIdWith 1 postsTable usersTable
495 r <- shouldBeJust found
472 r <- shouldBeJust found
496 r.test_om_posts.title `shouldEqual` "My Post"
473 r.test_om_posts.title `shouldEqual` "My Post"
497 r.test_om_users.name `shouldEqual` "Alice"
474 r.test_om_users.name `shouldEqual` "Alice"
499− it "joins with a filter" $ withPostsTable \conn -> do
476+ it "joins with a filter" $ withPostsTable do
500− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
477+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
501− _ <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
478+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
502− _ <- conn # run do postsTable # create { title: "Post A", body: "A", user_id: 1 }
479+ _ <- postsTable # create { title: "Post A", body: "A", user_id: 1 }
503− _ <- conn # run do postsTable # create { title: "Post B", body: "B", user_id: 2 }
480+ _ <- postsTable # create { title: "Post B", body: "B", user_id: 2 }
504− rows <- conn # run do findWithWhere { user_id: 1 } postsTable usersTable
481+ rows <- findWithWhere { user_id: 1 } postsTable usersTable
505 Array.length rows `shouldEqual` 1
482 Array.length rows `shouldEqual` 1
507 r = case Array.head rows of
484 r = case Array.head rows of
@@ -509,12 +486,12 @@ spec = describe "ClientOm" do
509 Nothing -> { test_om_posts: { id: 0, title: "", body: "", user_id: 0 }, test_om_users: { id: 0, name: "", email: "", active: false, role: "" } }
486 Nothing -> { test_om_posts: { id: 0, title: "", body: "", user_id: 0 }, test_om_users: { id: 0, name: "", email: "", active: false, role: "" } }
510 r.test_om_posts.title `shouldEqual` "Post A"
487 r.test_om_posts.title `shouldEqual` "Post A"
512− it "nests children under their parent" $ withPostsTable \conn -> do
489+ it "nests children under their parent" $ withPostsTable do
513− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
490+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
514− _ <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
491+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
515− _ <- conn # run do postsTable # create { title: "Post 1", body: "A", user_id: 1 }
492+ _ <- postsTable # create { title: "Post 1", body: "A", user_id: 1 }
516− _ <- conn # run do postsTable # create { title: "Post 2", body: "B", user_id: 1 }
493+ _ <- postsTable # create { title: "Post 2", body: "B", user_id: 1 }
517− rows <- conn # run do findIncluding @"posts" usersTable postsTable
494+ rows <- findIncluding @"posts" usersTable postsTable
518 Array.length rows `shouldEqual` 2
495 Array.length rows `shouldEqual` 2
520 alice = case Array.head rows of
497 alice = case Array.head rows of
@@ -523,22 +500,22 @@ spec = describe "ClientOm" do
523 alice.name `shouldEqual` "Alice"
500 alice.name `shouldEqual` "Alice"
524 Array.length alice.posts `shouldEqual` 2
501 Array.length alice.posts `shouldEqual` 2
526− it "nests children for a single parent by id" $ withPostsTable \conn -> do
503+ it "nests children for a single parent by id" $ withPostsTable do
527− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
504+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
528− _ <- conn # run do postsTable # create { title: "Post 1", body: "A", user_id: 1 }
505+ _ <- postsTable # create { title: "Post 1", body: "A", user_id: 1 }
529− _ <- conn # run do postsTable # create { title: "Post 2", body: "B", user_id: 1 }
506+ _ <- postsTable # create { title: "Post 2", body: "B", user_id: 1 }
530− found <- conn # run do findByIdIncluding @"posts" 1 usersTable postsTable
507+ found <- findByIdIncluding @"posts" 1 usersTable postsTable
531 r <- shouldBeJust found
508 r <- shouldBeJust found
532 r.name `shouldEqual` "Alice"
509 r.name `shouldEqual` "Alice"
533 Array.length r.posts `shouldEqual` 2
510 Array.length r.posts `shouldEqual` 2
535− it "uses the join combinator to compose tables" $ withPostsTable \conn -> do
512+ it "uses the join combinator to compose tables" $ withPostsTable do
536− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
513+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
537− _ <- conn # run do usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
514+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
538− _ <- conn # run do postsTable # create { title: "Post 1", body: "A", user_id: 1 }
515+ _ <- postsTable # create { title: "Post 1", body: "A", user_id: 1 }
539− _ <- conn # run do postsTable # create { title: "Post 2", body: "B", user_id: 1 }
516+ _ <- postsTable # create { title: "Post 2", body: "B", user_id: 1 }
540− _ <- conn # run do postsTable # create { title: "Post 3", body: "C", user_id: 2 }
517+ _ <- postsTable # create { title: "Post 3", body: "C", user_id: 2 }
541− rows <- conn # run do (usersTable # join postsTable) # findAll
518+ rows <- (usersTable # join postsTable) # findAll
542 Array.length rows `shouldEqual` 2
519 Array.length rows `shouldEqual` 2
544 alice = case Array.head rows of
521 alice = case Array.head rows of
@@ -547,12 +524,12 @@ spec = describe "ClientOm" do
547 alice.name `shouldEqual` "Alice"
524 alice.name `shouldEqual` "Alice"
548 Array.length alice.test_om_posts `shouldEqual` 2
525 Array.length alice.test_om_posts `shouldEqual` 2
550− it "joins multiple child tables at once" $ withPostsAndCommentsTable \conn -> do
527+ it "joins multiple child tables at once" $ withPostsAndCommentsTable do
551− _ <- conn # run do usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
528+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
552− _ <- conn # run do postsTable # create { title: "Post 1", body: "A", user_id: 1 }
529+ _ <- postsTable # create { title: "Post 1", body: "A", user_id: 1 }
553− _ <- conn # run do postsTable # create { title: "Post 2", body: "B", user_id: 1 }
530+ _ <- postsTable # create { title: "Post 2", body: "B", user_id: 1 }
554− _ <- conn # run do commentsTable # create { text: "Nice!", user_id: 1 }
531+ _ <- commentsTable # create { text: "Nice!", user_id: 1 }
555− rows <- conn # run do (usersTable # join postsTable # join commentsTable) # findAll
532+ rows <- (usersTable # join postsTable # join commentsTable) # findAll
556 Array.length rows `shouldEqual` 1
533 Array.length rows `shouldEqual` 1
558 r = case Array.head rows of
535 r = case Array.head rows of
@@ -563,29 +540,28 @@ spec = describe "ClientOm" do
563 Array.length r.test_om_comments `shouldEqual` 1
540 Array.length r.test_om_comments `shouldEqual` 1
565 describe "Transactions" do
542 describe "Transactions" do
566− it "commits multiple inserts atomically" $ withUsersTable \conn -> do
543+ it "commits multiple inserts atomically" $ withUsersTable do
567− conn # run do
544+ transactionally do
569− _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
545+ _ <- usersTable # create { name: "Alice", email: "a@t.com", role: "admin" }
570− _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
546+ _ <- usersTable # create { name: "Bob", email: "b@t.com", role: "user" }
571− pure unit
547+ pure unit
572− n <- conn # runNoErr do usersTable # count
548+ n <- usersTable # count
573 n `shouldEqual` 2
549 n `shouldEqual` 2
575 describe "UUIDs and newtypes" do
551 describe "UUIDs and newtypes" do
576− it "round-trips a UUID primary key through create/find/exists" $ withUuidTable \conn -> do
552+ it "round-trips a UUID primary key through create/find/exists" $ withUuidTable do
577− created <- conn # run do uuidTable # create { label: "first" }
553+ created <- uuidTable # create { label: "first" }
578 row <- shouldBeJust created
554 row <- shouldBeJust created
579 row.label `shouldEqual` "first"
555 row.label `shouldEqual` "first"
580− found <- conn # run do uuidTable # findById row.id
556+ found <- uuidTable # findById row.id
581 r2 <- shouldBeJust found
557 r2 <- shouldBeJust found
582 r2.label `shouldEqual` "first"
558 r2.label `shouldEqual` "first"
583− byCol <- conn # run do uuidTable # findBy @"label" "first"
559+ byCol <- uuidTable # findBy @"label" "first"
584 Array.length byCol `shouldEqual` 1
560 Array.length byCol `shouldEqual` 1
585− byId <- conn # run do uuidTable # findOneBy @"id" row.id
561+ byId <- uuidTable # findOneBy @"id" row.id
586 r3 <- shouldBeJust byId
562 r3 <- shouldBeJust byId
587 r3.label `shouldEqual` "first"
563 r3.label `shouldEqual` "first"
588− yes <- conn # runNoErr do uuidTable # exists row.id
564+ yes <- uuidTable # exists row.id
589 yes `shouldEqual` true
565 yes `shouldEqual` true
591-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
567-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -612,7 +588,7 @@ settingsTable :: Proxy SettingsTable
612settingsTable = Proxy
588settingsTable = Proxy
614type UuidTable = Table "test_om_uuid_things"
590type UuidTable = Table "test_om_uuid_things"
615− ( id :: PGUUID # PrimaryKey # DefaultExpr "gen_random_uuid()"
591+ ( id :: PGUUID # PrimaryKey # Default "gen_random_uuid()"
616 , label :: String
592 , label :: String
@@ -642,6 +618,15 @@ commentsTable = Proxy
642-- Helpers
618-- Helpers
643-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
619-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
621+type TestOm = Om.Om { postgresConnection :: PG.Connection } (parseError :: MultipleErrors) Unit
623+shouldEqual :: forall m t. MonadEffect m => Show t => Eq t => t -> t -> m Unit
624+shouldEqual v1 v2 = when (v1 /= v2) $ liftEffect $ Exception.throwException $ Exception.error $ show v1 <> " ≠ " <> show v2
626+shouldBeJust :: forall m a. MonadEffect m => Maybe a -> m a
627+shouldBeJust (Just a) = pure a
628+shouldBeJust Nothing = liftEffect $ Exception.throwException $ Exception.error "Expected Just but got Nothing"
645testConnection :: Aff PG.Connection
630testConnection :: Aff PG.Connection
646testConnection =
631testConnection =
647 PG.postgres
632 PG.postgres
@@ -652,31 +637,25 @@ testConnection =
652 , password: PG.PostgresPassword "postgres"
637 , password: PG.PostgresPassword "postgres"
653 } # liftEffect
638 } # liftEffect
655−run
640+runOm :: TestOm -> Aff Unit
656− :: forall a
641+runOm om = do
657− . Om.Om { postgresConnection :: PG.Connection } (parseError :: MultipleErrors) a
642+ conn <- testConnection
660−run om conn = Om.runOm { postgresConnection: conn }
643+ Om.runOm { postgresConnection: conn }
661− { exception: \e -> throwError e
644+ { exception: \e -> throwError e
662− , parseError: \errs -> throwError (error ("Parse error: " <> show errs))
645+ , parseError: \errs -> throwError (Exception.error ("Parse error: " <> show errs))
668− . Om.Om { postgresConnection :: PG.Connection } () a
671−runNoErr om conn = Om.runOm { postgresConnection: conn }
672− { exception: \e -> throwError e }
675−withUsersTable :: (PG.Connection -> Aff Unit) -> Aff Unit
650+withUsersTable :: TestOm -> Aff Unit
676withUsersTable test = do
651withUsersTable test = do
677 conn <- testConnection
652 conn <- testConnection
678 _ <- PG.executeSimple createDDL conn
653 _ <- PG.executeSimple createDDL conn
679− test conn
654+ Om.runOm { postgresConnection: conn }
655+ { exception: \e -> throwError e
656+ , parseError: \errs -> throwError (Exception.error ("Parse error: " <> show errs))
680 _ <- PG.executeSimple dropDDL conn
659 _ <- PG.executeSimple dropDDL conn
681 PG.end conn
660 PG.end conn
@@ -691,11 +670,15 @@ withUsersTable test = do
691 role TEXT NOT NULL
670 role TEXT NOT NULL
694−withSettingsTable :: (PG.Connection -> Aff Unit) -> Aff Unit
673+withSettingsTable :: TestOm -> Aff Unit
695withSettingsTable test = do
674withSettingsTable test = do
696 conn <- testConnection
675 conn <- testConnection
697 _ <- PG.executeSimple createDDL conn
676 _ <- PG.executeSimple createDDL conn
698− test conn
677+ Om.runOm { postgresConnection: conn }
678+ { exception: \e -> throwError e
679+ , parseError: \errs -> throwError (Exception.error ("Parse error: " <> show errs))
699 _ <- PG.executeSimple dropDDL conn
682 _ <- PG.executeSimple dropDDL conn
700 PG.end conn
683 PG.end conn
@@ -707,11 +690,15 @@ withSettingsTable test = do
707 value TEXT NOT NULL
690 value TEXT NOT NULL
710−withUuidTable :: (PG.Connection -> Aff Unit) -> Aff Unit
693+withUuidTable :: TestOm -> Aff Unit
711withUuidTable test = do
694withUuidTable test = do
712 conn <- testConnection
695 conn <- testConnection
713 _ <- PG.executeSimple createDDL conn
696 _ <- PG.executeSimple createDDL conn
714− test conn
697+ Om.runOm { postgresConnection: conn }
698+ { exception: \e -> throwError e
699+ , parseError: \errs -> throwError (Exception.error ("Parse error: " <> show errs))
715 _ <- PG.executeSimple dropDDL conn
702 _ <- PG.executeSimple dropDDL conn
716 PG.end conn
703 PG.end conn
@@ -723,11 +710,15 @@ withUuidTable test = do
723 label TEXT NOT NULL
710 label TEXT NOT NULL
726−withPostsTable :: (PG.Connection -> Aff Unit) -> Aff Unit
713+withPostsTable :: TestOm -> Aff Unit
727withPostsTable test = do
714withPostsTable test = do
728 conn <- testConnection
715 conn <- testConnection
729 _ <- PG.executeSimple createDDL conn
716 _ <- PG.executeSimple createDDL conn
730− test conn
717+ Om.runOm { postgresConnection: conn }
718+ { exception: \e -> throwError e
719+ , parseError: \errs -> throwError (Exception.error ("Parse error: " <> show errs))
731 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_posts CASCADE") conn
722 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_posts CASCADE") conn
732 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_users CASCADE") conn
723 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_users CASCADE") conn
733 PG.end conn
724 PG.end conn
@@ -749,11 +740,15 @@ withPostsTable test = do
749 user_id INTEGER NOT NULL REFERENCES test_om_users(id)
740 user_id INTEGER NOT NULL REFERENCES test_om_users(id)
752−withPostsAndCommentsTable :: (PG.Connection -> Aff Unit) -> Aff Unit
743+withPostsAndCommentsTable :: TestOm -> Aff Unit
753withPostsAndCommentsTable test = do
744withPostsAndCommentsTable test = do
754 conn <- testConnection
745 conn <- testConnection
755 _ <- PG.executeSimple createDDL conn
746 _ <- PG.executeSimple createDDL conn
756− test conn
747+ Om.runOm { postgresConnection: conn }
748+ { exception: \e -> throwError e
749+ , parseError: \errs -> throwError (Exception.error ("Parse error: " <> show errs))
757 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_comments CASCADE") conn
752 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_comments CASCADE") conn
758 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_posts CASCADE") conn
753 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_posts CASCADE") conn
759 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_users CASCADE") conn
754 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS test_om_users CASCADE") conn
@@ -781,7 +776,3 @@ withPostsAndCommentsTable test = do
781 text TEXT NOT NULL,
776 text TEXT NOT NULL,
782 user_id INTEGER NOT NULL REFERENCES test_om_users(id)
777 user_id INTEGER NOT NULL REFERENCES test_om_users(id)
785−shouldBeJust :: forall a. Maybe a -> Aff a
786−shouldBeJust (Just a) = pure a
787−shouldBeJust Nothing = throwError $ error "Expected Just but got Nothing"