Acme-Parataxis
view release on metacpan or search on metacpan
t/160_supervisor.t view on Meta::CPAN
use v5.40;
use blib;
use Acme::Parataxis qw[async fiber yield];
use Acme::Parataxis::Actor;
use Acme::Parataxis::Supervisor;
use Acme::Parataxis::Error;
use Test2::V1 -ipP;
$|++;
# OTP-style supervisor trees. Children are supervised actors (or nested supervisors) that
# are restarted when they die, per restart strategy, until a restart budget runs out - at which
# point the tree stops and run() fails with an aggregate Acme::Parataxis::Error::Supervisor.
#
# Every assertion below is paired with a control: an untouched sibling, a second strategy, a budget
# nobody can exhaust, or the same crash loop under a window that does not expire.
# A factory that starts one supervised actor. The handler answers 'hi', dies on 'boom', and echoes
# anything else, so a restarted child is distinguishable from a dead one by asking it something.
sub make_child ( $name, $capacity = 4 ) {
return sub {
Acme::Parataxis::Actor->spawn(
sub ( $self, $msg ) {
return "hello from $name" if $msg eq 'hi';
die "$name exploded" if $msg eq 'boom';
return "$name:$msg";
},
$capacity,
supervised => 1
);
};
}
# A child that dies as soon as it handles anything, so a restart loop needs no driver at all.
sub make_suicide () {
return sub {
my $actor = Acme::Parataxis::Actor->spawn( sub ( $self, $msg ) { die "kamikaze: $msg" }, 4, supervised => 1 );
$actor->send('boom');
return $actor;
};
}
# Spin the scheduler until $cond holds. Bounded so a condition that can never become true fails the
# assertion instead of hanging the suite.
sub wait_for ($cond) {
for ( 1 .. 5_000 ) { return 1 if $cond->(); yield }
return $cond->() ? 1 : 0;
}
sub all_alive ( $sup, @names ) {
for my $name (@names) {
my $child;
ok lives { $child = $sup->child($name) };
return 0 unless $child && $child->is_alive;
}
return 1;
}
# Await a future the way a caller sees a failure: returns the error, or undef if it answered.
sub ask_err ($future) {
return undef if eval { $future->await };
return "$@";
}
subtest 'configuration and registration are validated' => sub {
my $sup = Acme::Parataxis::Supervisor->new;
is $sup->strategy, 'OneForOne', 'default strategy';
is $sup->max_restarts, 5, 'default restart budget';
is $sup->within, 60, 'default window in seconds';
ok !$sup->running, 'a fresh supervisor is not running';
ok !$sup->stopping, 'and has not been stopped';
ok dies { Acme::Parataxis::Supervisor->new( strategy => 'Whatever' ) }, 'an unknown strategy is rejected';
ok dies { Acme::Parataxis::Supervisor->new( max_restarts => -1 ) }, 'a negative budget is rejected';
( run in 2.900 seconds using v1.01-cache-2.11-cpan-b554fb43c41 )