EV-Redis
view release on metacpan or search on metacpan
eval { $r->ping(sub {}) };
like $@, qr/MONITOR is active/, 'command while monitoring croaks';
# Flag clears with the connection
$r->disconnect;
$r->connect_unix($connect_info{sock});
my $pong;
$r->ping(sub { $pong = $_[0]; EV::break });
my $t2; $t2 = EV::timer 2, 0, sub { undef $t2; EV::break };
EV::run;
undef $t2; # kill pending timeout (see above)
is $pong, 'PONG', 'commands work again after reconnect clears monitor state';
$r->disconnect;
}
# Test: subscribe with no channels croaks (server would reject it and the
# persistent tracking entry would strand until disconnect).
{
my $r = EV::Redis->new( path => $connect_info{sock} );
eval { $r->subscribe(sub {}) };
like $@, qr/subscribe requires at least one channel/, 'no-arg subscribe croaks';
eval { $r->psubscribe(sub {}) };
like $@, qr/psubscribe requires at least one channel/, 'no-arg psubscribe croaks';
$r->disconnect;
}
# Test: disconnect with active subscription â callback should fire exactly once
# with a meaningful error (not empty string), and not be double-invoked.
{
my $sub = EV::Redis->new(path => $connect_info{sock});
my @cb_calls;
my $subscribed = 0;
$sub->on_error(sub {}); # suppress
$sub->subscribe('disconnect_test_ch', sub {
my ($result, $error) = @_;
push @cb_calls, [$result, $error];
if ($result && ref $result eq 'ARRAY' && $result->[0] eq 'subscribe') {
$subscribed = 1;
# Disconnect without unsubscribing
$sub->disconnect;
}
});
my $t; $t = EV::timer 2, 0, sub { undef $t; EV::break };
EV::run;
undef $t; # kill pending timeout (see above)
ok $subscribed, 'subscribed before disconnect';
# Expect: subscribe confirmation + exactly one disconnect error
my @errors = grep { defined $_->[1] } @cb_calls;
is scalar(@errors), 1, 'subscribe callback invoked exactly once with error on disconnect';
ok $errors[0][1], 'error string is truthy (not empty)';
like $errors[0][1], qr/disconnected/, 'error string is "disconnected"';
}
# Test: multi-channel subscribe + disconnect â one error callback total.
# (Historically flaky here: guard timers leaked by earlier blocks â kept
# alive by their self-capturing callbacks â fired their deferred EV::break
# inside this block's run, ending it before the subscriber's read interest
# was registered. Fixed by the undef-after-EV::run lines above; a failure
# here now means a real regression.)
{
my $sub = EV::Redis->new(path => $connect_info{sock});
my @cb_calls;
my $sub_count = 0;
$sub->on_error(sub { warn "MULTIDC on_error: @_\n" if $ENV{EV_REDIS_DIAG} });
$sub->subscribe('multi_dc_ch1', 'multi_dc_ch2', sub {
my ($result, $error) = @_;
push @cb_calls, [$result, $error];
if ($result && ref $result eq 'ARRAY' && $result->[0] eq 'subscribe') {
$sub_count++;
if ($sub_count == 2) {
$sub->disconnect;
}
}
});
my $t; $t = EV::timer 5, 0, sub { undef $t; EV::break };
EV::run;
undef $t; # kill pending timeout (see above)
is $sub_count, 2, 'both channels subscribed';
my @errors = grep { defined $_->[1] } @cb_calls;
# With multi-channel, hiredis fires once per channel on teardown.
for my $e (@errors) {
ok $e->[1], 'error string is truthy (not empty)';
}
# 2 subscribe confirmations + at most one error per channel;
# no extra invocation from remove_cb_queue_sv
ok scalar(@errors) <= 2, 'no more than 2 error callbacks for 2-channel subscribe';
}
done_testing;
( run in 2.435 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )