Inventory: with_objects cleanup
[kivitendo-erp.git] / t / db_helper / with_transaction.t
1 use Test::More tests => 17;
2 use Test::Exception;
3
4 use strict;
5
6 use lib 't';
7 use utf8;
8
9 use Carp;
10 use Data::Dumper;
11 use Support::TestSetup;
12 use SL::DB::Part;
13 use SL::Dev::Part qw(new_part);
14
15 Support::TestSetup::login();
16
17 SL::DB::Manager::Part->delete_all(all => 1, cascade => 1);
18
19 # silence the Test::Harness warn handler
20 local $SIG{__WARN__} = sub {};
21
22 # test simple transaction
23
24 my $part = new_part();
25 SL::DB->client->with_transaction(sub {
26   $part->save;
27   ok 1, 'part saved';
28   1;
29 }) or do {
30   ok 0, 'error saving part';
31 };
32
33 # test failing transaction
34 my $part2 = new_part(partnumber => $part->partnumber); # woops, duplicate partnumber
35 SL::DB->client->with_transaction(sub {
36   $part2->save;
37   ok 0, 'part saved';
38   1;
39 }) or do {
40   ok 1, 'saving part with duplicate partnumber generates graceful error';
41 };
42
43 # test transaction with run time exception
44 dies_ok {
45   SL::DB->client->with_transaction(sub {
46     $part->method_that_does_not_exist;
47     ok 0, 'this should have died';
48     1;
49   }) or do {
50     ok 0, 'this should not get here';
51   };
52 } 'method not found in transaction died as expect';
53
54 # test transaction with hook error
55 # TODO - not possible to test without locally adding hooks in run time
56
57 # test if error gets correctly stored in db->error
58 $part2 = new_part(partnumber => $part->partnumber); # woops, duplicate partnumber
59 SL::DB->client->with_transaction(sub {
60   $part2->save;
61   ok 0, 'part saved';
62   1;
63 }) or do {
64   like(SL::DB->client->error, qr/unique.constraint/i, 'error is in db->error');
65 };
66
67 # test stacked transactions
68 # 1. test that it works
69 SL::DB->client->with_transaction(sub {
70   $part->sellprice(1);
71   $part->save;
72
73   SL::DB->client->with_transaction(sub {
74     $part->sellprice(2);
75     $part->save;
76   }) or do {
77     ok 0, 'error saving part';
78   };
79
80   $part->sellprice(3);
81   $part->save;
82   1;
83 }) or do {
84   ok 0, 'error saving part';
85 };
86
87 $part->load;
88 is $part->sellprice, "3.00000", 'part saved';
89
90 # 2. with a transaction rollback
91 SL::DB->client->with_transaction(sub {
92   $part->sellprice(1);
93   $part2->save;
94   $part->save;
95
96   SL::DB->client->with_transaction(sub {
97     $part->sellprice(2);
98     $part->save;
99   }) or do {
100     ok 0, 'should not get here';
101   };
102
103   $part->sellprice(3);
104   $part->save;
105   ok 0, 'should not get here';
106   1;
107 }) or do {
108   ok 1, 'sql error skips rest of the transaction';
109 };
110
111
112 SL::DB->client->with_transaction(sub {
113   $part->sellprice(1);
114   $part->save;
115
116   SL::DB->client->with_transaction(sub {
117     $part->sellprice(2);
118     $part->save;
119     $part2->save;
120   }) or do {
121     ok 0, 'should not get here';
122   };
123
124   $part->sellprice(3);
125   $part->save;
126   ok 0, 'should not get here';
127   1;
128 }) or do {
129   ok 1, 'sql error in nested transaction rolls back';
130   like(SL::DB->client->error, qr/unique.constraint/i, 'error from nested transaction is in db->error');
131 };
132
133 $part->load;
134 is $part->sellprice, "3.00000", 'saved part is not affected';
135
136
137
138 SL::DB->client->with_transaction(sub {
139   $part->sellprice(1);
140   $part->save;
141
142   SL::DB->client->with_transaction(sub {
143     $part->sellprice(2);
144     $part->save;
145   }) or do {
146     ok 0, 'should not get here';
147   };
148
149   $part->sellprice(4);
150   $part->save;
151   $part2->save;
152   ok 0, 'should not get here';
153   1;
154 }) or do {
155   ok 1, 'sql error after nested transaction rolls back';
156 };
157
158 $part->load;
159 is $part->sellprice, "3.00000", 'saved part is not affected';
160
161 eval {
162   SL::DB->client->with_transaction(sub {
163     $part->sellprice(1);
164     $part->not_existing_function();
165     $part->save;
166
167     SL::DB->client->with_transaction(sub {
168       $part->sellprice(2);
169       $part->save;
170     }) or do {
171       ok 0, 'should not get here';
172     };
173
174     $part->sellprice(4);
175     $part->save;
176     ok 0, 'should not get here';
177     1;
178   }) or do {
179     ok 0, 'should not get here';
180   };
181   1;
182 } or do {
183   ok 1, 'runtime exception error before nested transaction rolls back';
184 };
185
186 $part->load;
187 is $part->sellprice, "3.00000", 'saved part is not affected';
188
189 eval {
190   SL::DB->client->with_transaction(sub {
191     $part->sellprice(1);
192     $part->save;
193
194     SL::DB->client->with_transaction(sub {
195       $part->sellprice(2);
196       $part->not_existing_function();
197       $part->save;
198     }) or do {
199       ok 0, 'should not get here';
200     };
201
202     $part->sellprice(4);
203     $part->save;
204     ok 0, 'should not get here';
205     1;
206   }) or do {
207     ok 0, 'should not get here';
208   };
209   1;
210 } or do {
211   ok 1, 'runtime exception error in nested transaction rolls back';
212 };
213
214 $part->load;
215 is $part->sellprice, "3.00000", 'saved part is not affected';
216
217
218 eval {
219   SL::DB->client->with_transaction(sub {
220     $part->sellprice(1);
221     $part->save;
222
223     SL::DB->client->with_transaction(sub {
224       $part->sellprice(2);
225       $part->save;
226     }) or do {
227       ok 0, 'should not get here';
228     };
229
230     $part->sellprice(4);
231     $part->save;
232     $part->not_existing_function();
233     ok 0, 'should not get here';
234     1;
235   }) or do {
236     ok 0, 'should not get here';
237   };
238   1;
239 } or do {
240   ok 1, 'runtime exception error after nested transaction rolls back';
241 };
242
243 $part->load;
244 is $part->sellprice, "3.00000", 'saved part is not affected';