API-Docker
view release on metacpan or search on metacpan
t/truncated_response.t view on Meta::CPAN
cmp_ok length($got[0]), '<', 20,
'and it is less than the whole chunk, so the reader really did stop '
. 'short of what the chunk header announced';
is_deeply $res && $res->[4], { delivered => 1, stopped => 1 },
'the summary says the callback ended it, not the daemon';
};
subtest 'a content-length body the callback stopped inside is not truncation'
=> sub {
my @got;
my $fh = stepped("HTTP/1.1 200 OK\r\nContent-Length: 20\r\n\r\n"
. ('x' x 20), 5);
my $handler = $client->_stream_handler('GET /v1.41/probe', 'on_chunk',
sub { push @got, $_[0]; $_[1]->() }, 0);
my $res = eval {
$client->_read_streaming_response($fh, 'GET', $handler, {}) };
is $@, '', 'stopping inside the body raises nothing' or diag "raised: $@";
is scalar @got, 1, 'one unit reached the callback';
cmp_ok length($got[0]), '<', 20, 'short of the announced length';
is_deeply $res && $res->[4], { delivered => 1, stopped => 1 },
'and the summary says who ended it';
};
subtest 'a close-delimited stream is not truncated by its close' => sub {
my @got;
my $c = client_for("HTTP/1.1 200 OK\r\n\r\n"
. "\x01\x00\x00\x00\x00\x00\x00\x05hello");
my $summary = eval {
$c->get('/containers/abc/attach', on_frame => sub { push @got, $_[0] });
};
is $@, '', 'nothing raised' or diag "raised: $@";
is_deeply $summary, { delivered => 1, stopped => 0 }, 'one frame delivered';
is_deeply \@got, [{ stream => 'stdout', data => 'hello' }], 'and decoded';
close $c->peer;
};
# ---------------------------------------------------------------------------
# What the exception is
# ---------------------------------------------------------------------------
subtest 'the exception behaves like the others' => sub {
my ($err) = over_pair(
"HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nhello wor",
sub { $client->_read_response($_[0], 'GET', { endpoint => 'GET /v1.41/x' }) });
isa_ok $err, 'API::Docker::Error::Truncated';
ok overload::Overloaded($err),
'stringification is in place -- namespace::clean before use overload';
unlike "$err", qr/=HASH\(0x/,
'stringifies to the reason, not to a reference address';
like "$err", qr{\QGET /v1.41/x\E}, 'the request is named in the string';
like "$err", qr/ at \S+ line \d+/, "with Carp's own location suffix";
ok !!$err, 'and it is true as an exception';
is $err->as_string, "$err", 'as_string is what the overload returns';
};
subtest 'a reader driven with no context still names the phase' => sub {
# t/role_http.t drives the readers directly with no request behind them, so
# there is no endpoint to name. The message leaves it out rather than
# interpolating an empty pair of parentheses.
my ($err) = over_pair(
"HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nhello wor",
sub { $client->_read_response($_[0], 'GET', {}) });
isa_ok $err, 'API::Docker::Error::Truncated';
is $err && $err->endpoint, '', 'no endpoint';
like $err && "$err", qr/\ADocker API response truncated: /,
'and the message goes straight to the reason';
};
subtest 'nothing at all is said so rather than reported as zero bytes' => sub {
my ($err) = over_pair("HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\n",
sub { $client->_read_response($_[0], 'GET', {}) });
isa_ok $err, 'API::Docker::Error::Truncated';
is $err && $err->partial, '', 'partial is empty';
is $err && $err->received, 0, 'and nothing was received';
like $err && "$err", qr/nothing arrived at all/, 'the message says so';
};
subtest 'a >= 400 body that is itself cut short is reported as truncation'
=> sub {
# The rule the read timeout already follows, applied to the same place: the
# transport cannot tell a caller what the engine said when it did not finish
# saying it. The price is that a 404 whose body the daemon cut off arrives
# as this class rather than as API::Docker::Error::HTTP, so ->status is not
# there -- which is the honest answer, the status line having been the only
# complete part of a response that was not.
my $c = client_for(sized('{"message":"no such contain',
announce => 40, status => '404 Not Found'));
my %res;
my $got = eval { $c->get('/containers/abc/json', response => \%res) };
my $err = $@;
close $c->peer;
is $got, undef, 'nothing came back';
isa_ok $err, 'API::Docker::Error::Truncated';
ok !$err->isa('API::Docker::Error::HTTP'),
'and it is not the status error, which would have claimed a whole body';
is $err && $err->partial, '{"message":"no such contain',
'the part of the error body that did arrive is carried';
is_deeply \%res, {},
'the response out-parameter is untouched: _request never got that far';
};
done_testing;
( run in 0.945 second using v1.01-cache-2.11-cpan-80ec619307d )