1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use Test::More 0.88;
use Test::Deep;
use Test::Fatal;
use Test::Mock::Redis;
use lib 't/tlib';
=pod
x MULTI
x EXEC
x DISCARD
=cut
# There is a known issue in the real Redis client that screws up the
# interpretation of all results from the client after an error in the middle of
# a multi -- https://github.com/melo/perl-redis/issues/42
# Because of this, this one test file uses a subref for its redis object,
# rather than the object itself, so it can get a new object at the right time
# so we can continue tests...
my $r = sub { Test::Mock::Redis->new };
ok($r->(), 'pretended to connect to our test redis-server');
my @redi = ($r);
my ( $guard, $srv );
if( $ENV{RELEASE_TESTING} ){
use_ok("Redis");
use_ok("Test::SpawnRedisServer");
($guard, $srv) = redis();
my $r = sub { Redis->new(server => $srv) };
my $redis = $r->();
ok($redis, 'connected to our test redis-server');
$redis->flushall;
unshift @redi, $r
}
foreach my $o (@redi)
{
my $redis = $o->();
diag("testing $redis") if $ENV{RELEASE_TESTING};
ok($redis->ping, 'ping');
like(
exception { $redis->exec },
qr/^\[exec\] ERR EXEC without MULTI/,
'cannot call EXEC before MULTI',
);
like(
exception { $redis->discard },
qr/^\[discard\] ERR DISCARD without MULTI/,
'cannot call DISCARD before MULTI',
);
like(
exception { $redis->multi; $redis->multi },
qr/^\[multi\] ERR MULTI calls can not be nested/,
'cannot call MULTI again until EXEC or DISCARD is called',
);
is($redis->discard, 'OK', 'multi state has been reset');
# discarded transactions
is($redis->multi, 'OK', 'multi transaction started');
is($redis->hmset('transaction_key_1', qw(a 1 b 2)), 'QUEUED', 'hmset operation recorded');
cmp_deeply(
$redis->discard,
'OK',
'transaction discarded',
);
cmp_deeply(
{ $redis->hgetall('transaction_key_1') },
{ },
'data was not altered',
);
# successful transactions
is($redis->watch('transaction_key_3'), 'OK', 'watch command');
is($redis->multi, 'OK', 'multi transaction started');
is($redis->hmset('transaction_key_3', qw(a 1 b 2)), 'QUEUED', 'hmset operation recorded');
cmp_deeply([ $redis->keys('transaction_key_*') ], [ 'QUEUED' ], 'keys operation recorded');
is($redis->set('transaction_key_4', 'ohhai'), 'QUEUED', 'set operation recorded');
cmp_deeply([ $redis->keys('transaction_key_*') ], [ 'QUEUED' ], 'keys operation recorded');
cmp_deeply(
[ $redis->exec ],
[
'OK',
[ 'transaction_key_3' ], # transaction_key_4 hasn't been set yet
'OK',
bag(qw(transaction_key_3 transaction_key_4)),
],
'transaction finished, returning the results of all queries',
);
is($redis->unwatch(), 'OK', 'unwatch command');
cmp_deeply(
{ $redis->hgetall('transaction_key_3') },
{
a => '1',
b => '2',
},
'hash data successfully recorded',
);
# an error in replaying a transaction should not abort subsequent commands
# note: this mirrors behaviour in version 2.6.5+
is($redis->multi, 'OK', 'multi transaction started');
is($redis->set('transaction_key_1', 'foo'), 'QUEUED', 'set operation recorded');
is($redis->hset('transaction_key_1', 'bar', '9'), 'QUEUED', 'hset operation recorded');
is($redis->hset('transaction_key_3', 'a', '9'), 'QUEUED', 'hset operation recorded');
like(
exception { $redis->exec },
qr/^\Q[exec] WRONGTYPE Operation against a key holding the wrong kind of value\E/,
'a bad transaction results in an exception',
);
# we need to get a new redis client now -- see notes above
$redis = $o->();
is($redis->get('transaction_key_1'), 'foo', 'the first command was executed');
cmp_deeply(
{ $redis->hgetall('transaction_key_3') },
{
a => '9',
b => '2',
},
'commands after the error were still executed',
);
}
done_testing;
|