ForgeOps-Tracker
view release on metacpan or search on metacpan
t/metric_buffer.t view on Meta::CPAN
ok(eval { $buffer->flush; 1 }, 'flush did not die') or diag $@;
is($buffer->count, 1);
$buffer->flush for 1 .. 4;
is($buffer->count, 0);
};
subtest 'an entry recorded while delivery is in flight is never lost' => sub {
my (@delivered, @outcomes) :shared;
my ($in_flight, $release) :shared;
$in_flight = 0;
$release = 0;
my $buffer = ForgeOps::Tracker::MetricBuffer->new(
new_configuration(),
sub {
my ($entries) = @_;
{
lock(@delivered);
push @delivered, JSON::PP::encode_json([ map { $_->{metric_name} } @$entries ]);
}
if (@delivered == 1) {
lock($in_flight);
$in_flight = 1;
cond_signal($in_flight);
}
if (@delivered == 1) {
lock($release);
cond_wait($release) until $release;
}
return 1;
},
sub { 3600 },
);
$buffer->record({ metric_name => 'first', value => 1 });
my $flushing = threads->create(sub { $buffer->flush });
{ lock($in_flight); cond_wait($in_flight) until $in_flight; }
$buffer->record({ metric_name => 'during', value => 2 });
{ lock($release); $release = 1; cond_signal($release); }
$flushing->join;
$buffer->flush;
is_deeply([@delivered], ['["first"]', '["during"]']);
$buffer->_clear;
};
subtest 'is capped and drops further entries until a flush succeeds' => sub {
my (@delivered, @outcomes) :shared;
my $buffer = ForgeOps::Tracker::MetricBuffer->new(new_configuration(), sub { 0 }, sub { 3600 });
my $accepted = 0;
$accepted += $buffer->record({ metric_name => 'm', value => 1 }) for 1 .. ForgeOps::Tracker::MetricBuffer::MAX_ENTRIES + 50;
is($accepted, ForgeOps::Tracker::MetricBuffer::MAX_ENTRIES);
$buffer->_clear;
};
subtest 'the metric uris swap the trailing /events segment' => sub {
my $config = new_configuration();
is($config->custom_metrics_uri, 'https://tracker.example.com/api/v1/custom_metrics');
is($config->infrastructure_metrics_uri, 'https://tracker.example.com/api/v1/infrastructure_metrics');
};
subtest 'capture_metric and capture_infrastructure_metric deliver to their own endpoints end to end' => sub {
my $server = t::lib::EchoServer->start;
ForgeOps::Tracker::_reset_for_testing();
ForgeOps::Tracker::init(
dsn => 'http://key@127.0.0.1:' . $server->{port} . '/api/v1/events',
environment => 'production',
release => 'a1b2c3d',
enabled_environments => { production => 1 },
server_name => 'web-1',
);
ForgeOps::Tracker::capture_metric('signup');
ForgeOps::Tracker::capture_metric('payment', 49);
ForgeOps::Tracker::capture_infrastructure_metric('cpu', 0.42, hostname => 'db-1');
ForgeOps::Tracker::capture_infrastructure_metric('memory', 0.7);
ForgeOps::Tracker::flush_metrics();
my %by_path = map { $_->{path} => $_ } @{ $server->requests };
my $custom = $by_path{'/api/v1/custom_metrics'};
is($custom->{headers}{AUTHORIZATION}, 'Bearer key');
my $body = JSON::PP::decode_json($custom->{body});
is_deeply([ map { $_->{metric_name} } @{ $body->{metrics} } ], ['signup', 'payment']);
is($body->{metrics}[0]{value}, 1);
is($body->{metrics}[1]{value}, 49);
is($body->{metrics}[0]{environment}, 'production');
is($body->{metrics}[0]{release}, 'a1b2c3d');
my $infrastructure = JSON::PP::decode_json($by_path{'/api/v1/infrastructure_metrics'}{body})->{metrics};
is($infrastructure->[0]{hostname}, 'db-1');
is($infrastructure->[1]{hostname}, 'web-1');
ForgeOps::Tracker::_reset_for_testing();
$server->stop;
};
subtest 'capture_metric drops a batch the plan rejects but keeps one the server failed on, end to end' => sub {
my $server = t::lib::EchoServer->start;
my @sent_for;
for my $code (403, 503) {
ForgeOps::Tracker::_reset_for_testing();
ForgeOps::Tracker::init(
dsn => 'http://key@127.0.0.1:' . $server->{port} . "/status/$code/api/v1/events",
environment => 'production',
enabled_environments => { production => 1 },
);
ForgeOps::Tracker::capture_metric('first');
ForgeOps::Tracker::flush_metrics();
ForgeOps::Tracker::capture_metric('second');
ForgeOps::Tracker::flush_metrics();
}
# Nothing may be left for the END block to send once this server is gone.
ForgeOps::Tracker::_reset_for_testing();
for my $request (@{ $server->requests }) {
my ($code) = $request->{path} =~ m{/status/(\d{3})/};
push @{ $sent_for[$code == 403 ? 0 : 1] }, [ map { $_->{metric_name} } @{ JSON::PP::decode_json($request->{body})->{metrics} } ];
}
is_deeply($sent_for[0], [ ['first'], ['second'] ], 'a 403 batch is not sent again');
is_deeply($sent_for[1], [ ['first'], ['first', 'second'] ], 'a 503 batch is kept for the next flush');
$server->stop;
};
subtest 'is a no-op when the client is not enabled' => sub {
ForgeOps::Tracker::_reset_for_testing();
( run in 0.782 second using v1.01-cache-2.11-cpan-16aabcbfbcb )