9 my $schema = DBICTest->init_schema();
14 my ($artist, @cd_titles) = @_;
16 $artist->create_related('cds', {
19 }) foreach (@cd_titles);
21 return $artist->cds->all;
24 # Test checking of parameters
27 (ref $schema)->txn_do(sub{});
29 like($@, qr/storage/, "can't call txn_do without storage");
33 like($@, qr/must be a CODE reference/, '$coderef parameter check ok');
36 # Test successful txn_do() - scalar context
38 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
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');
46 title => "txn_do test CD $_",
47 })->first->year, 2006, "new CD $_ year correct") for (1..5);
49 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
52 # Test successful txn_do() - list context
54 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
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');
62 title => "txn_do test CD $_",
63 })->first->year, 2006, "new CD $_ year correct") for (6..10);
65 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
68 # Test nested successful txn_do()
70 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
72 my $nested_code = sub {
73 my ($schema, $artist, $code) = @_;
75 my @titles1 = map {'nested txn_do test CD ' . $_} (1..5);
76 my @titles2 = map {'nested txn_do test CD ' . $_} (6..10);
78 $schema->txn_do($code, $artist, @titles1);
79 $schema->txn_do($code, $artist, @titles2);
82 my $artist = $schema->resultset('Artist')->find(2);
83 my $count_before = $artist->cds->count;
86 $schema->txn_do($nested_code, $schema, $artist, $code);
91 ok(!$error, 'nested txn_do succeeded');
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');
97 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
100 my $fail_code = sub {
102 $artist->create_related('cds', {
103 title => 'this should not exist',
106 die "the sky is falling";
109 # Test failed txn_do()
112 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
114 my $artist = $schema->resultset('Artist')->find(3);
117 $schema->txn_do($fail_code, $artist);
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',
127 ok(!defined($cd), q{failed txn_do didn't change the cds table});
129 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
132 # do the same transaction again
134 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
136 my $artist = $schema->resultset('Artist')->find(3);
139 $schema->txn_do($fail_code, $artist);
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',
149 ok(!defined($cd), q{failed txn_do didn't change the cds table});
151 is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
154 # Test failed txn_do() with failed rollback
156 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
158 my $artist = $schema->resultset('Artist')->find(3);
160 # Force txn_rollback() to throw an exception
161 no warnings 'redefine';
164 # die in rollback, but maintain sanity for further tests ...
165 local *{"DBIx::Class::Storage::DBI::SQLite::txn_rollback"} = sub{
167 $storage->{transaction_depth}--;
172 $schema->txn_do($fail_code, $artist);
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');
182 my $cd = $artist->cds({
183 title => 'this should not exist',
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
190 title => 'this should not exist',
193 ok(!defined($cd), q{deleted the failed txn's cd});
194 $schema->storage->_dbh->rollback;
197 # Test nested failed txn_do()
199 is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
201 my $nested_fail_code = sub {
202 my ($schema, $artist, $code1, $code2) = @_;
204 my @titles = map {'nested txn_do test CD ' . $_} (1..5);
206 $schema->txn_do($code1, $artist, @titles); # successful txn
207 $schema->txn_do($code2, $artist); # failed txn
210 my $artist = $schema->resultset('Artist')->find(3);
213 $schema->txn_do($nested_fail_code, $schema, $artist, $code, $fail_code);
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 '.$_,
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',
227 ok(!defined($cd), q{failed txn_do didn't add failed txn's cd});
230 # Grab a new schema to test txn before connect
232 my $schema2 = DBICTest->init_schema(no_deploy => 1);
234 $schema2->txn_begin();
235 $schema2->txn_begin();
238 ok(! $err, 'Pre-connection nested transactions.');
240 # although not connected DBI would still warn about rolling back at disconnect
241 $schema2->txn_rollback;
242 $schema2->txn_rollback;
243 $schema2->storage->disconnect;
245 $schema->storage->disconnect;
247 # Test txn_scope_guard
249 my $schema = DBICTest->init_schema();
251 is($schema->storage->transaction_depth, 0, "Correct transaction depth");
252 my $artist_rs = $schema->resultset('Artist');
254 my $guard = $schema->txn_scope_guard;
258 name => 'Death Cab for Cutie',
263 } qr/No such column made_up_column .*? at .*?81transactions.t line \d+/s, "Error propogated okay";
265 ok(!$artist_rs->find({name => 'Death Cab for Cutie'}), "Artist not created");
271 is($@, $inner_exception, "Nested exceptions propogated");
273 ok(!$artist_rs->find({name => 'Death Cab for Cutie'}), "Artist not created");
277 local $SIG{__WARN__} = sub { $w = shift };
279 # The 0 arg says don't die, just let the scope guard go out of scope
280 # forcing a txn_rollback to happen
283 like ($w, qr/A DBIx::Class::Storage::TxnScopeGuard went out of scope without explicit commit or an error/, 'Out of scope warning detected');
284 ok(!$artist_rs->find({name => 'Death Cab for Cutie'}), "Artist not created");
285 }, 'rollback successful withot exception');
290 my $guard = $schema->txn_scope_guard;
291 $schema->resultset('Artist')->create({
292 name => 'Death Cab for Cutie',
298 my ($schema, $fatal) = @_;
300 my $inner_guard = $schema->txn_scope_guard;
301 is($schema->storage->transaction_depth, 2, "Correct transaction depth");
303 my $artist = $artist_rs->find({ name => 'Death Cab for Cutie' });
306 $artist->cds->create({
309 $fatal ? ( foo => 'bar' ) : ()
313 # Record what got thrown so we can test it propgates out properly.
314 $inner_exception = $@;
318 # inner guard should commit without consequences
319 $inner_guard->commit;