API-Docker
view release on metacpan or search on metacpan
t/images_commit.t view on Meta::CPAN
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use FindBin;
use lib "$FindBin::Bin/lib";
use Test::API::Docker::Mock;
use JSON::MaybeXS qw( decode_json );
use API::Docker;
# karr k22 -- images->commit, POST /commit. Turns a container into an image:
# the one image-producing path that does not go through a build context.
#
# Everything asserted about the engine here was measured against the rootless
# Podman socket (5.4.2, API 1.41): 201 Created with {"Id":"<64 hex>"} and no
# sha256: prefix, `changes` accepted both newline-joined and as repeated
# query pairs with the same result, and a ContainerConfig body whose Labels
# and Cmd replace the container's while Env is merged onto the inherited one.
check_live_access();
# ---------------------------------------------------------------------------
# See t/images_tar.t for why this exists: the mock harness replaces _request
# wholesale, so the request line and body it assembles are only reachable
# through a client whose socket is an in-memory sink.
package Test::ImagesCommit::FakeTransport;
use Moo;
extends 'API::Docker';
has canned => (is => 'rw', default => sub { [201, 'Created', {}, '{"Id":"deadbeef"}'] });
has _sink => (is => 'rw');
sub _build__socket {
my ($self) = @_;
my $sink = '';
$self->_sink(\$sink);
open my $fh, '>', \$sink or die "open: $!";
return $fh;
}
sub _read_response { return $_[0]->canned }
sub written { return ${ $_[0]->_sink } }
package main;
sub fake_client {
return Test::ImagesCommit::FakeTransport->new(
host => 'unix:///nonexistent.sock',
api_version => '1.41',
);
}
sub request_line {
my ($t) = @_;
my ($line) = $t->written =~ /\A(\S+ [^\r\n]+) HTTP\/1\.1\r\n/;
return $line;
}
# ---------------------------------------------------------------------------
subtest 'commit: returns the daemon response and requires a container' => sub {
plan skip_all => 'route assertions are fixture-only' if is_live();
my %seen;
my $docker = test_docker(
'POST /commit' => sub {
my ($method, $path, %opts) = @_;
%seen = %opts;
return { Id => 'ae3996fdfd897ec18aa3eaa07e095418dda8f19902e43a2761373399f13132b3' };
},
);
my $result = $docker->images->commit(
container => 'c0ffee',
repo => 'myapp',
tag => 'snapshot',
comment => 'after the migration ran',
author => 'Jane <jane@example.com>',
);
is ref $result, 'HASH', 'the raw daemon response, not an entity object';
like $result->{Id}, qr/^[0-9a-f]{64}$/,
'Podman answers a bare hex digest with no sha256: prefix';
is_deeply $seen{params}, {
container => 'c0ffee',
repo => 'myapp',
tag => 'snapshot',
comment => 'after the migration ran',
author => 'Jane <jane@example.com>',
}, 'every option went to the query string';
ok !exists $seen{body}, 'and no body was sent without a config';
my $err = do { local $@; eval { $docker->images->commit(repo => 'myapp') }; $@ };
like $err, qr/container required/, 'a missing container croaks';
};
# ---------------------------------------------------------------------------
subtest 'commit: the options ride in the query string' => sub {
my $t = fake_client();
$t->images->commit(container => 'c0ffee', repo => 'myapp', tag => 'v2');
is request_line($t),
'POST /v1.41/commit?container=c0ffee&repo=myapp&tag=v2',
'container, repo and tag on the wire, sorted by the transport';
};
subtest 'commit: pause is normalised to 1/0, not passed through' => sub {
( run in 0.889 second using v1.01-cache-2.11-cpan-364913b4093 )