v / vlib / orm / orm_transaction_test.v
302 lines · 258 sloc · 5.21 KB · 171afcbed98696173fe9b26973ae3127b476f5e8
Raw
1// vtest build: present_sqlite3? && !sanitize-memory-clang
2import db.sqlite
3import orm
4
5struct TxUser {
6 id int @[primary; sql: serial]
7 name string
8}
9
10fn setup_tx_db() !sqlite.DB {
11 mut db := sqlite.connect(':memory:')!
12 sql db {
13 create table TxUser
14 }!
15 return db
16}
17
18fn insert_callback_user(mut tx orm.Tx) !int {
19 user := TxUser{
20 name: 'callback_commit'
21 }
22 sql tx {
23 insert user into TxUser
24 }!
25 return tx.last_id()
26}
27
28fn insert_and_fail(mut tx orm.Tx) !int {
29 user := TxUser{
30 name: 'callback_rollback'
31 }
32 sql tx {
33 insert user into TxUser
34 }!
35 return error('force rollback')
36}
37
38fn nested_success_inner(mut tx orm.Tx) !int {
39 user := TxUser{
40 name: 'nested_success'
41 }
42 sql tx {
43 insert user into TxUser
44 }!
45 return tx.last_id()
46}
47
48fn nested_failure_inner(mut tx orm.Tx) !int {
49 user := TxUser{
50 name: 'nested_failure'
51 }
52 sql tx {
53 insert user into TxUser
54 }!
55 return error('nested failure')
56}
57
58fn count_tx_users(db sqlite.DB) !int {
59 return db.q_int('select count(*) from TxUser')!
60}
61
62fn tx_user_names(db sqlite.DB) ![]string {
63 rows := sql db {
64 select from TxUser order by id
65 }!
66 return rows.map(it.name)
67}
68
69fn test_transaction_callback_commits_on_success() {
70 mut db := setup_tx_db()!
71 defer {
72 db.close() or {}
73 }
74
75 id := orm.transaction[int](mut db, insert_callback_user)!
76 assert id == 1
77 assert count_tx_users(db)! == 1
78 assert tx_user_names(db)! == ['callback_commit']
79}
80
81fn test_transaction_callback_rolls_back_on_error() {
82 mut db := setup_tx_db()!
83 defer {
84 db.close() or {}
85 }
86
87 orm.transaction[int](mut db, insert_and_fail) or {
88 assert err.msg() == 'force rollback'
89 assert count_tx_users(db)! == 0
90 return
91 }
92 assert false
93}
94
95fn test_manual_begin_with_sql_tx_queries() {
96 mut db := setup_tx_db()!
97 defer {
98 db.close() or {}
99 }
100
101 mut tx := orm.begin(mut db)!
102 user := TxUser{
103 name: 'manual'
104 }
105 sql tx {
106 insert user into TxUser
107 }!
108 inserted := sql tx {
109 select from TxUser where name == 'manual'
110 }!
111 assert inserted.len == 1
112 assert inserted[0].name == 'manual'
113
114 sql tx {
115 update TxUser set name = 'manual_updated' where name == 'manual'
116 }!
117 updated := sql tx {
118 select from TxUser where name == 'manual_updated'
119 }!
120 assert updated.len == 1
121
122 sql tx {
123 delete from TxUser where name == 'manual_updated'
124 }!
125 assert count_tx_users(db)! == 0
126 tx.commit()!
127}
128
129fn test_query_builder_works_inside_transaction() {
130 mut db := setup_tx_db()!
131 defer {
132 db.close() or {}
133 }
134
135 mut tx := orm.begin(mut db)!
136 mut qb := orm.new_query[TxUser](tx)
137 qb.insert(TxUser{
138 name: 'qb_user'
139 })!
140
141 selected := qb.where('name = ?', 'qb_user')!.query()!
142 assert selected.len == 1
143 assert selected[0].name == 'qb_user'
144
145 tx.commit()!
146 assert tx_user_names(db)! == ['qb_user']
147}
148
149fn test_nested_transaction_success_releases_savepoint() {
150 mut db := setup_tx_db()!
151 defer {
152 db.close() or {}
153 }
154
155 orm.transaction[int](mut db, fn (mut tx orm.Tx) !int {
156 before := TxUser{
157 name: 'outer_before'
158 }
159 sql tx {
160 insert before into TxUser
161 }!
162
163 nested_id := tx.transaction[int](nested_success_inner)!
164 after := TxUser{
165 name: 'outer_after'
166 }
167 sql tx {
168 insert after into TxUser
169 }!
170 return nested_id
171 })!
172
173 assert tx_user_names(db)! == ['outer_before', 'nested_success', 'outer_after']
174}
175
176fn test_nested_transaction_failure_rolls_back_inner_work_only() {
177 mut db := setup_tx_db()!
178 defer {
179 db.close() or {}
180 }
181
182 orm.transaction[int](mut db, fn (mut tx orm.Tx) !int {
183 before := TxUser{
184 name: 'outer_before'
185 }
186 sql tx {
187 insert before into TxUser
188 }!
189
190 tx.transaction[int](nested_failure_inner) or {}
191
192 after := TxUser{
193 name: 'outer_after'
194 }
195 sql tx {
196 insert after into TxUser
197 }!
198 return 0
199 })!
200
201 assert tx_user_names(db)! == ['outer_before', 'outer_after']
202}
203
204fn test_manual_savepoint_rollback_and_release() {
205 mut db := setup_tx_db()!
206 defer {
207 db.close() or {}
208 }
209
210 mut tx := orm.begin(mut db)!
211 before := TxUser{
212 name: 'before_savepoint'
213 }
214 sql tx {
215 insert before into TxUser
216 }!
217
218 mut sp := tx.savepoint()!
219 rollback_user := TxUser{
220 name: 'rollback_me'
221 }
222 sql tx {
223 insert rollback_user into TxUser
224 }!
225 sp.rollback()!
226
227 after := TxUser{
228 name: 'after_rollback'
229 }
230 sql tx {
231 insert after into TxUser
232 }!
233
234 mut sp2 := tx.savepoint()!
235 released := TxUser{
236 name: 'keep_me'
237 }
238 sql tx {
239 insert released into TxUser
240 }!
241 sp2.release()!
242
243 tx.commit()!
244 assert tx_user_names(db)! == ['before_savepoint', 'after_rollback', 'keep_me']
245}
246
247fn test_inactive_transaction_errors() {
248 mut db := setup_tx_db()!
249 defer {
250 db.close() or {}
251 }
252
253 mut tx := orm.begin(mut db)!
254 tx.commit()!
255
256 tx.commit() or { assert err.msg().contains('inactive') }
257
258 user := TxUser{
259 name: 'after_close'
260 }
261 sql tx {
262 insert user into TxUser
263 } or {
264 assert err.msg().contains('inactive')
265 return
266 }
267 assert false
268}
269
270fn test_inactive_savepoint_errors() {
271 mut db := setup_tx_db()!
272 defer {
273 db.close() or {}
274 }
275
276 mut tx := orm.begin(mut db)!
277 mut sp := tx.savepoint()!
278 sp.release()!
279 sp.rollback() or {
280 assert err.msg().contains('inactive')
281 tx.rollback()!
282 return
283 }
284 assert false
285}
286
287fn test_savepoint_becomes_inactive_when_parent_transaction_closes() {
288 mut db := setup_tx_db()!
289 defer {
290 db.close() or {}
291 }
292
293 mut tx := orm.begin(mut db)!
294 mut sp := tx.savepoint()!
295 tx.commit()!
296
297 sp.release() or {
298 assert err.msg().contains('inactive')
299 return
300 }
301 assert false
302}
303