#!/usr/bin/env perl -w
my
$CLASS
;
BEGIN {
$CLASS
=
'DBIx::Connector'
;
use_ok
$CLASS
or
die
;
}
ok
my
$conn
=
$CLASS
->new(
'dbi:ExampleP:dummy'
,
''
,
''
),
'Get a connection'
;
my
$module
= Test::MockModule->new(
$CLASS
);
$module
->mock(
_connect
=>
sub
{
pass
'_connect should be called'
;
$module
->original(
'_connect'
)->(
@_
);
});
ok
my
$dbh
=
$conn
->dbh,
'Fetch the database handle'
;
ok
$dbh
->{AutoCommit},
'We should not be in a txn'
;
ok !
$conn
->in_txn,
'in_txn() should know that, too'
;
ok !
$conn
->{_in_run},
'_in_run should be false'
;
my
$dbi_mock
= Test::MockModule->new(
ref
$dbh
,
no_auto
=> 1);
my
$ping
= 0;
$dbi_mock
->mock(
ping
=>
sub
{ ++
$ping
} );
is
$conn
->{_dbh},
$dbh
,
'The dbh should be stored'
;
is
$ping
, 0,
'No pings yet'
;
ok
$conn
->connected,
'We should be connected'
;
is
$ping
, 1,
'Ping should have been called'
;
ok
$conn
->txn(
sub
{
is
$ping
, 1,
'Ping should not have been called before the txn'
;
ok !
shift
->{AutoCommit},
'Inside, we should be in a transaction'
;
ok
$conn
->in_txn,
'We should be in a txn'
;
ok
$conn
->{_in_run},
'_in_run should be true'
;
is
$conn
->dbh,
$dbh
,
'Should get same dbh from dbh()'
;
is
$ping
, 1,
'ping should not have been called again'
;
}),
'Do something with no existing handle'
;
$module
->unmock(
'_connect'
);
ok !
$conn
->{_in_run},
'_in_run should be false again'
;
ok
$dbh
->{AutoCommit},
'Transaction should be committed'
;
ok !
$conn
->in_txn,
'in_txn() should know it'
;
is
$conn
->{_dbh},
$dbh
,
'The dbh should be stored'
;
ok
$conn
->connected,
'We should be connected'
;
ok
$conn
->txn(
sub
{
my
$dbha
=
shift
;
is
$dbha
,
$dbh
,
'The handle should have been passed'
;
is
$_
,
$dbh
,
'It should also be in $_'
;
is
$_
,
$dbh
,
'Should have dbh in $_'
;
$ping
= 0;
is
$conn
->dbh,
$dbh
,
'Should get same dbh from dbh()'
;
$ping
= 1;
ok !
$dbha
->{AutoCommit},
'We should be in a transaction'
;
ok
$conn
->in_txn,
'in_txn() should know about it'
;
}),
'Do something with stored handle'
;
ok
$dbh
->{AutoCommit},
'New transaction should be committed'
;
ok !
$conn
->in_txn,
'in_txn() should know it, too'
;
ok
my
$foo
=
$conn
->txn(
sub
{
return
(2, 3, 5);
}),
'Do in scalar context'
;
is
$foo
, 5,
'The return value should be the last value'
;
ok
$foo
=
$conn
->txn(
sub
{
return
wantarray
? (2, 3, 5) :
'scalar'
;
}),
'Do in scalar context'
;
is
$foo
,
'scalar'
,
'Callback should know when its context is scalar'
;
ok
my
@foo
=
$conn
->txn(
sub
{
return
(2, 3, 5);
}),
'Do in array context'
;
is_deeply \
@foo
, [2, 3, 5],
'The return value should be the list'
;
ok
@foo
=
$conn
->txn(
sub
{
return
wantarray
? (2, 3, 5) :
'scalar'
;
}),
'Do in scalar context'
;
is_deeply \
@foo
, [2, 3, 5],
'Callback should know when its context is list'
;
eval
{
$conn
->txn(
sub
{
die
'WTF?'
}) };
ok $@,
'We should have died'
;
ok
$dbh
->{AutoCommit},
'New transaction should rolled back'
;
ok !
$conn
->in_txn,
'in_txn() should know that'
;
$conn
->txn(
sub
{
my
$dbh
=
shift
;
ok !
$dbh
->{AutoCommit},
'We should be in a txn'
;
ok
$conn
->in_txn,
'in_txn() should know about it'
;
local
$dbh
->{Active} = 0;
$conn
->txn(
sub
{
isnt
shift
,
$dbh
,
'Nested txn should not get inactive dbh'
;
ok !
$dbh
->{AutoCommit},
'Nested txn should be in the txn'
;
ok
$conn
->in_txn,
'in_txn() should know it'
;
});
});
$dbh
=
$conn
->dbh;
my
$driver
=
$conn
->driver;
$driver
->begin_work(
$dbh
);
ok !
$dbh
->{AutoCommit},
'Transaction should be started'
;
ok
$conn
->in_txn,
'in_txn() should know it'
;
$conn
->txn(
sub
{
my
$dbha
=
shift
;
is
$dbha
,
$dbh
,
'We should have the same database handle'
;
is
$_
,
$dbh
,
'It should also be in $_'
;
$ping
= 0;
is
$conn
->dbh,
$dbh
,
'Should get same dbh from dbh()'
;
$ping
= 1;
ok !
$dbha
->{AutoCommit},
'Transaction should still be going'
;
ok
$conn
->in_txn,
'in_txn() should know it'
;
});
ok !
$dbh
->{AutoCommit},
'Transaction should stil be live after txn'
;
ok
$conn
->in_txn,
'in_txn() should know it'
;
$driver
->rollback(
$dbh
);
$conn
->txn(
sub
{
my
$dbh
=
shift
;
ok !
$dbh
->{AutoCommit},
'We should be in a txn'
;
ok
$conn
->in_txn,
'in_txn() should know that, too'
;
$dbi_mock
->mock(
ping
=> 0 );
$conn
->txn(
sub
{
is
shift
,
$dbh
,
'Nested txn should get same dbh, even though inactive'
;
ok !
$dbh
->{AutoCommit},
'Nested txn should be in the txn'
;
ok
$conn
->in_txn,
'in_txn() should know that, too'
;
});
});
$conn
->txn(
sub
{
is
$conn
->mode,
'no_ping'
,
'Default mode should be no_ping'
;
});
$conn
->txn(
ping
=>
sub
{
is
$conn
->mode,
'ping'
,
'Mode should be "ping" inside ping txn'
});
is
$conn
->mode,
'no_ping'
,
'Back outside, should be "no_ping" again'
;
$conn
->txn(
fixup
=>
sub
{
is
$conn
->mode,
'fixup'
,
'Mode should be "fixup" inside fixup txn'
});
is
$conn
->mode,
'no_ping'
,
'Back outside, should be "no_ping" again'
;
ok
$conn
->mode(
'ping'
),
'Se mode to "ping"'
;
$conn
->txn(
sub
{
is
$conn
->mode,
'ping'
,
'Mode should implicitly be "ping"'
});
ok
$conn
->mode(
'fixup'
),
'Se mode to "fixup"'
;
$conn
->txn(
sub
{
is
$conn
->mode,
'fixup'
,
'Mode should implicitly be "fixup"'
});
NOEXIT: {
no
warnings;
my
$dr_mock
= Test::MockModule->new(
ref
$driver
,
no_auto
=> 1);
$dr_mock
->mock(
begin_work
=>
sub
{
shift
});
my
$keyword
;
$dr_mock
->mock(
commit
=>
sub
{
pass
"Commit should be called when returning via $keyword"
});
for
my
$mode
(
qw(ping no_ping fixup)
) {
$conn
->mode(
$mode
);
$keyword
=
'next'
;
ok !
$conn
->txn(
sub
{
next
}),
"Return via $keyword should fail"
;
$keyword
=
'last'
;
ok !
$conn
->txn(
sub
{
last
}),
"Return via $keyword should fail"
;
}
}
$dbi_mock
->mock(
begin_work
=>
undef
);
$dbi_mock
->mock(
rollback
=>
sub
{
die
'Rollback WTF'
});
eval
{
$conn
->txn(
sub
{
die
'Transaction WTF'
;
}) };
ok
my
$err
= $@,
'We should have died'
;
isa_ok
$err
,
'DBIx::Connector::TxnRollbackError'
,
'The exception'
;
like
$err
,
qr/Transaction aborted: Transaction WTF/
,
'Should have the transaction error'
;
like
$err
,
qr/Transaction rollback failed: Rollback WTF/
,
'Should have the rollback error'
;
like
$err
->rollback_error,
qr/Rollback WTF/
,
'Should have rollback error'
;
like
$err
->error,
qr/Transaction WTF/
,
'Should have transaction error'
;
eval
{
$conn
->txn(
sub
{
local
$_
->{AutoCommit} = 0;
$conn
->txn(
sub
{
die
'Nested WTF'
});
}) };
ok
$err
= $@,
'We should have died again'
;
isa_ok
$err
,
'DBIx::Connector::TxnRollbackError'
,
'The exception'
;
like
$err
->rollback_error,
qr/Rollback WTF/
,
'Should have rollback error'
;
like
$err
->error,
qr/Nested WTF/
,
'Should have nested transaction error'
;
ok !
ref
$err
->error,
'The nested error should not be an object'
;