API-Docker
view release on metacpan or search on metacpan
t/streaming_callback.t view on Meta::CPAN
subtest 'on_chunk: stopping leaves the rest of a sized body unread' => sub {
my $client = transport(sized('y' x 300, type => 'application/x-tar'),
step => 64, at_end => 'die');
my @got;
my $summary = $client->get('/images/x/get',
on_chunk => sub { my ($bytes, $stop) = @_; push @got, $bytes; $stop->() });
is scalar(@got), 1, 'one slice';
is_deeply $summary, { delivered => 1, stopped => 1 },
'the remaining bytes were never asked for';
};
# ---------------------------------------------------------------------------
subtest 'a failure is not a stream: >= 400 croaks with the daemon message' => sub {
my $client = transport(
sized(encode_json({ message => 'no such container: x' }),
status => 404, reason => 'Not Found'));
my $called = 0;
my %res;
eval {
$client->get('/containers/x/logs',
response => \%res,
on_chunk => sub { $called++ },
);
};
like $@, qr/Docker API error \(404\): no such container: x/,
'the error body is read whole and croaked with, exactly as before';
is $called, 0, 'the callback never saw an error body';
is $res{status}, 404, 'and the response out-parameter is still filled';
};
subtest 'option validation' => sub {
my $client = transport(chunked([], closed => 1));
eval {
$client->get('/events', on_event => sub { }, on_chunk => sub { })
};
like $@, qr/takes one of on_event, on_frame, on_chunk, not on_event and on_chunk/,
'two units for one stream has no answer, so it is refused before the '
. 'request is sent';
eval { $client->get('/events', on_event => 'nope') };
like $@, qr/on_event option must be a CodeRef/, 'and so is a non-callback';
};
subtest 'an empty stream still returns a summary' => sub {
my $client = transport(chunked([], closed => 1));
my $called = 0;
my $summary = $client->get('/events',
croak_on_error => 0,
on_event => sub { $called++ },
);
is $called, 0, 'nothing to deliver';
is_deeply $summary, { delivered => 0, stopped => 0 },
'and the caller is told that, rather than getting undef and guessing';
};
# ---------------------------------------------------------------------------
subtest 'the mock harness speaks the same contract' => sub {
plan skip_all => 'live mode ignores the route table' if is_live();
my $docker = test_docker(
'GET /events' => [
{ status => 'create' },
{ status => 'start' },
{ status => 'die' },
],
);
my @got;
my $summary = $docker->get('/events',
on_event => sub {
my ($event, $stop) = @_;
push @got, $event;
$stop->() if $event->{status} eq 'start';
},
);
is_deeply [ map { $_->{status} } @got ], [qw( create start )],
'a route whose value is the event list streams it one at a time';
is_deeply $summary, { delivered => 2, stopped => 1 },
'and returns the summary shape the transport returns';
my $raw = test_docker(
'GET /images/x/get' => mock_response(
data => 'whole-tarball',
stream => [ 'whole-', 'tarball' ],
),
);
my @chunks;
$raw->get('/images/x/get', on_chunk => sub { push @chunks, $_[0] });
is_deeply \@chunks, [ 'whole-', 'tarball' ],
'and mock_response(stream => ...) says what the units are where they are '
. 'not the buffered value';
};
# ---------------------------------------------------------------------------
subtest 'live: a bounded /events feed through a callback' => sub {
plan skip_all => 'set API_DOCKER_TEST_HOST for the live path' unless is_live();
check_live_access();
my $docker = test_docker();
my @got;
# Bounded, so the buffered path would return too -- what is under test here
# is that the callback path speaks to a real daemon at all: real chunked
# framing, real socket reads, real close.
my $summary = $docker->get('/events',
params => { since => time - 3600, until => time },
croak_on_error => 0,
on_event => sub {
my ($event, $stop) = @_;
push @got, $event;
$stop->() if @got >= 3;
( run in 0.651 second using v1.01-cache-2.11-cpan-54e63673c56 )