API-Docker

 view release on metacpan or  search on metacpan

t/distribution.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use FindBin;
use lib "$FindBin::Bin/lib";
use JSON::MaybeXS qw( decode_json );
use API::Docker;
use Test::API::Docker::FakeTransport;
use Test::API::Docker::Mock;

# GET /distribution/{name}/json -- asking a registry for an image manifest
# without pulling it (karr k15).
#
# Nothing here opens a socket or reaches a daemon, and nothing is gated on
# is_live() except the one subtest that says so. Test::API::Docker::Mock is
# deliberately not used for the bulk of this file: under API_DOCKER_TEST_HOST
# it ignores its route table and returns a real client, and the only engine
# reachable here is Podman, which serves no route for this endpoint at all --
# measured, rootless Podman 5.4.2 (API 1.41):
#
#   GET /v1.41/distribution/nginx:latest/json
#   -> 404 {"cause":"","message":"Path /v1.41/distribution/nginx:latest/json
#           is not supported","response":0}
#
# and the same for a bare name and for a percent-escaped reference. That
# version number just echoes the requested path prefix, not a fixed daemon
# constant -- re-measured live on 5.8.4 / API 1.44 the same request gets
# "/v1.44/distribution/..." back instead (karr k62). $PODMAN_404 below reads
# "/v1.41/..." because fake_client() always negotiates api_version 1.41; no
# assertion matches the number either way, only qr/is not supported/. So the
# daemon is faked below the socket instead, and the real _request runs. The
# success payload is the Engine API reference's own example descriptor.
#
# The one exception is the karr k38 subtest near the end, which drives the
# same ->exists 404-handling through Mock's route table instead of the real
# transport, now that Mock honours a >= 400 mock_response the way _request
# does. It is skipped live for the same reason as the rest of this file would
# be if it used Mock: no engine reachable here serves the route to check it
# against.

my $DESCRIPTOR = <<'JSON';
{"Descriptor":{"MediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96","size":3987},"Platforms":[{"architecture":"amd64","os":"linux"}]}
JSON

sub fake_client {
  my ($body, $status) = @_;
  return Test::API::Docker::FakeTransport->new(
    host        => 'unix:///nonexistent.sock',
    api_version => '1.41',
    canned      => [$status // 200, 'Not Found', {}, $body // $DESCRIPTOR],
  );
}

sub request_line {
  my ($raw) = @_;
  my ($line) = $raw =~ /\A([^\r\n]*)/;
  return $line;
}

# The two 404s this endpoint can give. Only the message tells them apart.
my $REGISTRY_404 = '{"message":"manifest unknown"}';
my $PODMAN_404   = '{"cause":"","message":"Path /v1.41/distribution/'
  . 'nginx:latest/json is not supported","response":0}';
my $DOCKER_404   = '{"message":"page not found"}';

# ---------------------------------------------------------------------------
subtest 'the reference keeps its slashes and its tag in the path' => sub {
  # Percent-encoding them breaks the reference: the daemon parses the path
  # segment as a docker reference, and %2F is not a repository separator.
  my $c = fake_client();
  $c->distribution->inspect('myrepo/app:1.0');
  is request_line($c->written),
    'GET /v1.41/distribution/myrepo/app:1.0/json HTTP/1.1',
    'slashes and the colon survive into the request line';

  my $plain = fake_client();
  $plain->distribution->inspect('nginx:latest');
  is request_line($plain->written),
    'GET /v1.41/distribution/nginx:latest/json HTTP/1.1',
    'and so does a plain library reference';

  ok !eval { fake_client()->distribution->inspect; 1 },
    'no reference croaks';
  like $@, qr/image reference/, 'and says what was missing';
};

# ---------------------------------------------------------------------------
subtest 'inspect returns the descriptor as the engine gave it' => sub {
  my $c = fake_client();
  my $d = $c->distribution->inspect('nginx:latest');

  is_deeply $d, decode_json($DESCRIPTOR),
    'the decoded response, not an entity object -- there is no '
    . 'API::Docker::Distribution class to wrap it in';
  is $d->{Descriptor}{size}, 3987, 'the descriptor is reachable';
};

# ---------------------------------------------------------------------------
subtest 'inspect croaks on 404, like every other endpoint method' => sub {
  my $c = fake_client($REGISTRY_404, 404);
  my %res;
  ok !eval { $c->distribution->inspect('nginx:latest', response => \%res); 1 },
    'a reference the registry does not have croaks';
  like $@, qr/manifest unknown/, 'with the registry message';
  is $res{status}, 404,
    'and response is filled before the croak, so an eval-ing caller reaches '
    . 'the status';



( run in 0.526 second using v1.01-cache-2.11-cpan-364913b4093 )