better money value comparison in tests
[dbsrgits/DBIx-Class-Historic.git] / t / 81transactions.t
CommitLineData
70350518 1use strict;
2use warnings;
3
4use Test::More;
3b7f3eac 5use Test::Exception;
70350518 6use lib qw(t/lib);
7use DBICTest;
8
a47e1233 9my $schema = DBICTest->init_schema();
70350518 10
c52c3466 11plan tests => 64;
a62cf8d4 12
13my $code = sub {
14 my ($artist, @cd_titles) = @_;
15
16 $artist->create_related('cds', {
17 title => $_,
18 year => 2006,
19 }) foreach (@cd_titles);
20
21 return $artist->cds->all;
22};
23
171dadd7 24# Test checking of parameters
25{
171dadd7 26 eval {
27 (ref $schema)->txn_do(sub{});
28 };
f32eb113 29 like($@, qr/storage/, "can't call txn_do without storage");
171dadd7 30 eval {
31 $schema->txn_do('');
32 };
33 like($@, qr/must be a CODE reference/, '$coderef parameter check ok');
34}
35
a62cf8d4 36# Test successful txn_do() - scalar context
37{
57c18b65 38 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
39
a62cf8d4 40 my @titles = map {'txn_do test CD ' . $_} (1..5);
41 my $artist = $schema->resultset('Artist')->find(1);
42 my $count_before = $artist->cds->count;
43 my $count_after = $schema->txn_do($code, $artist, @titles);
44 is($count_after, $count_before+5, 'successful txn added 5 cds');
45 is($artist->cds({
46 title => "txn_do test CD $_",
47 })->first->year, 2006, "new CD $_ year correct") for (1..5);
57c18b65 48
49 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
a62cf8d4 50}
51
52# Test successful txn_do() - list context
53{
57c18b65 54 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
55
a62cf8d4 56 my @titles = map {'txn_do test CD ' . $_} (6..10);
57 my $artist = $schema->resultset('Artist')->find(1);
58 my $count_before = $artist->cds->count;
59 my @cds = $schema->txn_do($code, $artist, @titles);
60 is(scalar @cds, $count_before+5, 'added 5 CDs and returned in list context');
61 is($artist->cds({
62 title => "txn_do test CD $_",
63 })->first->year, 2006, "new CD $_ year correct") for (6..10);
57c18b65 64
65 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
a62cf8d4 66}
67
68# Test nested successful txn_do()
69{
57c18b65 70 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
71
a62cf8d4 72 my $nested_code = sub {
73 my ($schema, $artist, $code) = @_;
74
75 my @titles1 = map {'nested txn_do test CD ' . $_} (1..5);
76 my @titles2 = map {'nested txn_do test CD ' . $_} (6..10);
77
78 $schema->txn_do($code, $artist, @titles1);
79 $schema->txn_do($code, $artist, @titles2);
80 };
81
82 my $artist = $schema->resultset('Artist')->find(2);
83 my $count_before = $artist->cds->count;
84
85 eval {
86 $schema->txn_do($nested_code, $schema, $artist, $code);
87 };
88
89 my $error = $@;
90
91 ok(!$error, 'nested txn_do succeeded');
92 is($artist->cds({
93 title => 'nested txn_do test CD '.$_,
94 })->first->year, 2006, qq{nested txn_do CD$_ year ok}) for (1..10);
95 is($artist->cds->count, $count_before+10, 'nested txn_do added all CDs');
57c18b65 96
97 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
a62cf8d4 98}
99
100my $fail_code = sub {
101 my ($artist) = @_;
102 $artist->create_related('cds', {
103 title => 'this should not exist',
104 year => 2005,
105 });
106 die "the sky is falling";
107};
108
109# Test failed txn_do()
110{
57c18b65 111
112 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
113
114 my $artist = $schema->resultset('Artist')->find(3);
115
116 eval {
117 $schema->txn_do($fail_code, $artist);
118 };
119
120 my $error = $@;
121
122 like($error, qr/the sky is falling/, 'failed txn_do threw an exception');
123 my $cd = $artist->cds({
124 title => 'this should not exist',
125 year => 2005,
126 })->first;
127 ok(!defined($cd), q{failed txn_do didn't change the cds table});
128
129 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
130}
131
132# do the same transaction again
133{
134 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
135
a62cf8d4 136 my $artist = $schema->resultset('Artist')->find(3);
137
138 eval {
139 $schema->txn_do($fail_code, $artist);
140 };
141
142 my $error = $@;
143
144 like($error, qr/the sky is falling/, 'failed txn_do threw an exception');
145 my $cd = $artist->cds({
146 title => 'this should not exist',
147 year => 2005,
148 })->first;
149 ok(!defined($cd), q{failed txn_do didn't change the cds table});
57c18b65 150
151 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
a62cf8d4 152}
153
154# Test failed txn_do() with failed rollback
155{
57c18b65 156 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
157
a62cf8d4 158 my $artist = $schema->resultset('Artist')->find(3);
159
160 # Force txn_rollback() to throw an exception
161 no warnings 'redefine';
58d387fe 162 no strict 'refs';
57c18b65 163
164 # die in rollback, but maintain sanity for further tests ...
165 local *{"DBIx::Class::Storage::DBI::SQLite::txn_rollback"} = sub{
166 my $storage = shift;
167 $storage->{transaction_depth}--;
168 die 'FAILED';
169 };
a62cf8d4 170
171 eval {
172 $schema->txn_do($fail_code, $artist);
173 };
174
175 my $error = $@;
176
177 like($error, qr/Rollback failed/, 'failed txn_do with a failed '.
178 'txn_rollback threw a rollback exception');
179 like($error, qr/the sky is falling/, 'failed txn_do with a failed '.
180 'txn_rollback included the original exception');
181
182 my $cd = $artist->cds({
183 title => 'this should not exist',
184 year => 2005,
185 })->first;
186 isa_ok($cd, 'DBICTest::CD', q{failed txn_do with a failed txn_rollback }.
187 q{changed the cds table});
188 $cd->delete; # Rollback failed
189 $cd = $artist->cds({
190 title => 'this should not exist',
191 year => 2005,
192 })->first;
193 ok(!defined($cd), q{deleted the failed txn's cd});
57c18b65 194 $schema->storage->_dbh->rollback;
a62cf8d4 195}
196
197# Test nested failed txn_do()
198{
57c18b65 199 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
200
a62cf8d4 201 my $nested_fail_code = sub {
202 my ($schema, $artist, $code1, $code2) = @_;
203
204 my @titles = map {'nested txn_do test CD ' . $_} (1..5);
205
206 $schema->txn_do($code1, $artist, @titles); # successful txn
207 $schema->txn_do($code2, $artist); # failed txn
208 };
209
210 my $artist = $schema->resultset('Artist')->find(3);
211
212 eval {
213 $schema->txn_do($nested_fail_code, $schema, $artist, $code, $fail_code);
214 };
215
216 my $error = $@;
217
218 like($error, qr/the sky is falling/, 'nested failed txn_do threw exception');
219 ok(!defined($artist->cds({
220 title => 'nested txn_do test CD '.$_,
221 year => 2006,
222 })->first), qq{failed txn_do didn't add first txn's cd $_}) for (1..5);
223 my $cd = $artist->cds({
224 title => 'this should not exist',
225 year => 2005,
226 })->first;
227 ok(!defined($cd), q{failed txn_do didn't add failed txn's cd});
228}
a62cf8d4 229
291bf95f 230# Grab a new schema to test txn before connect
231{
232 my $schema2 = DBICTest->init_schema(no_deploy => 1);
233 eval {
234 $schema2->txn_begin();
235 $schema2->txn_begin();
236 };
237 my $err = $@;
c52c3466 238 ok(! $err, 'Pre-connection nested transactions.');
239
240 # although not connected DBI would still warn about rolling back at disconnect
241 $schema2->txn_rollback;
242 $schema2->txn_rollback;
a211cb63 243 $schema2->storage->disconnect;
291bf95f 244}
a211cb63 245$schema->storage->disconnect;
3b7f3eac 246
3b7f3eac 247# Test txn_scope_guard
248{
3b7f3eac 249 my $schema = DBICTest->init_schema();
250
251 is($schema->storage->transaction_depth, 0, "Correct transaction depth");
252 my $artist_rs = $schema->resultset('Artist');
253 throws_ok {
254 my $guard = $schema->txn_scope_guard;
255
256
257 $artist_rs->create({
258 name => 'Death Cab for Cutie',
259 made_up_column => 1,
260 });
261
262 $guard->commit;
5ee678c8 263 } qr/No such column made_up_column .*? at .*?81transactions.t line \d+/s, "Error propogated okay";
3b7f3eac 264
265 ok(!$artist_rs->find({name => 'Death Cab for Cutie'}), "Artist not created");
266
267 my $inner_exception;
268 eval {
269 outer($schema, 1);
270 };
271 is($@, $inner_exception, "Nested exceptions propogated");
272
273 ok(!$artist_rs->find({name => 'Death Cab for Cutie'}), "Artist not created");
274
aab0d3b7 275 lives_ok (sub {
c52c3466 276 my $w;
277 local $SIG{__WARN__} = sub { $w = shift };
278
279 # The 0 arg says don't die, just let the scope guard go out of scope
3b7f3eac 280 # forcing a txn_rollback to happen
281 outer($schema, 0);
c52c3466 282
283 like ($w, qr/A DBIx::Class::Storage::TxnScopeGuard went out of scope without explicit commit or an error/, 'Out of scope warning detected');
aab0d3b7 284 ok(!$artist_rs->find({name => 'Death Cab for Cutie'}), "Artist not created");
285 }, 'rollback successful withot exception');
3b7f3eac 286
287 sub outer {
288 my ($schema) = @_;
aab0d3b7 289
3b7f3eac 290 my $guard = $schema->txn_scope_guard;
291 $schema->resultset('Artist')->create({
292 name => 'Death Cab for Cutie',
293 });
294 inner(@_);
3b7f3eac 295 }
296
297 sub inner {
298 my ($schema, $fatal) = @_;
aab0d3b7 299
300 my $inner_guard = $schema->txn_scope_guard;
301 is($schema->storage->transaction_depth, 2, "Correct transaction depth");
3b7f3eac 302
303 my $artist = $artist_rs->find({ name => 'Death Cab for Cutie' });
304
3b7f3eac 305 eval {
306 $artist->cds->create({
307 title => 'Plans',
308 year => 2005,
309 $fatal ? ( foo => 'bar' ) : ()
310 });
311 };
312 if ($@) {
313 # Record what got thrown so we can test it propgates out properly.
314 $inner_exception = $@;
315 die $@;
316 }
317
aab0d3b7 318 # inner guard should commit without consequences
319 $inner_guard->commit;
3b7f3eac 320 }
321}