API-Docker

 view release on metacpan or  search on metacpan

.claude/CLAUDE.md  view on Meta::CPAN


Perl client for the Docker Engine API.

## Docker Engine API

- **Unix Socket**: Default transport via `/var/run/docker.sock`
- **TCP**: Remote Docker daemons via `tcp://host:port`
- **TLS**: Optional TLS for secure remote connections
- **Auto-Negotiate**: Detects highest API version from daemon

## Build & Test

```bash
dzil build
dzil test
prove -lv t/
```

## Test Architecture

Unified mock/live tests controlled by environment variables:

```bash
# Mock mode (default):
prove -l t/

# Read tests against real Docker:
API_DOCKER_TEST_HOST=unix:///var/run/docker.sock prove -l t/

# Full live mode (read + write):
API_DOCKER_TEST_HOST=unix:///var/run/docker.sock API_DOCKER_TEST_WRITE=1 prove -l t/
```

| Env Var | Effect |
|---------|--------|
| (none) | All tests run with mocks |
| `API_DOCKER_TEST_HOST` | Read tests live, write tests skip |
| `API_DOCKER_TEST_HOST` + `API_DOCKER_TEST_WRITE=1` | All tests live |

Test helper: `t/lib/Test/API/Docker/Mock.pm` exports `test_docker`, `is_live`, `can_write`, `skip_unless_write`, `check_live_access`, `register_cleanup`, `load_fixture`.

## Structure

```
lib/API/
├── Docker.pm                  # Main entry point + auto-negotiate
└── Docker/
    ├── Role/
    │   └── HTTP.pm            # HTTP over Unix Socket / TCP
    ├── API/

CLAUDE.md  view on Meta::CPAN

lib/API/Docker.pm                       # main client, version negotiation
lib/API/Docker/Role/HTTP.pm             # HTTP/1.1 transport (unix:// + tcp://)
lib/API/Docker/API/System.pm            # /version, /info, /_ping
lib/API/Docker/API/Containers.pm        # container endpoints
lib/API/Docker/API/Images.pm            # image endpoints (build, pull, push, ...)
lib/API/Docker/API/Networks.pm          # network endpoints
lib/API/Docker/API/Volumes.pm           # volume endpoints
lib/API/Docker/API/Exec.pm              # exec endpoints
lib/API/Docker/{Container,Image,Network,Volume}.pm  # entity classes
t/                                      # tests (prove -l t/)
t/lib/Test/API/Docker/Mock.pm           # fixture-driven mock helper
t/fixtures/*.json                       # captured daemon responses
```

## Build and test

```bash
dzil build              # build the dist
dzil test               # full test suite
prove -lv t/images.t    # single test
cpanm --installdeps .   # install deps from cpanfile

CLAUDE.md  view on Meta::CPAN

  `headers` (extra HTTP headers — used by push for `X-Registry-Auth`).
- **`/build`, `/images/create`, `/images/.../push`** are streaming
  endpoints. `_request` parses newline-delimited JSON and returns an
  arrayref of events; callers iterate and look for `errorDetail`,
  `progress`, `aux`, etc.
- **`X-Registry-Auth` is required on every push** by the Docker Engine —
  even anonymous attempts. `images->push` always sends the header; pass
  `auth => { username, password, serveraddress, identitytoken }` to
  authenticate, omit it for the empty-`{}` form.

## Testing notes

- New tests should use the `Test::API::Docker::Mock` helper. Pass a
  `'METHOD /path' => $fixture_or_coderef` route table; the helper
  monkey-patches `_request` to dispatch against it.
- Don't add network-dependent assertions to default test runs. Gate them
  on `is_live()` / `can_write()` from the mock helper.
- Fixtures live in `t/fixtures/*.json`. Capture them from a real daemon
  rather than hand-rolling — keeps drift detectable.

## When changing behavior

- Add a `Changes` entry under `{{$NEXT}}`.

MANIFEST  view on Meta::CPAN

t/containers.t
t/fixtures/container_inspect.json
t/fixtures/containers_list.json
t/fixtures/images_list.json
t/fixtures/networks_list.json
t/fixtures/system_info.json
t/fixtures/system_version.json
t/fixtures/volumes_list.json
t/images.t
t/images_push_auth.t
t/lib/Test/API/Docker/Mock.pm
t/networks.t
t/release-changes_has_content.t
t/system.t
t/version.t
t/volumes.t

META.json  view on Meta::CPAN

      "configure" : {
         "requires" : {
            "ExtUtils::MakeMaker" : "0"
         }
      },
      "develop" : {
         "recommends" : {
            "Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007"
         },
         "requires" : {
            "Test::Pod" : "1.41"
         }
      },
      "runtime" : {
         "requires" : {
            "IO::Socket::UNIX" : "0",
            "JSON::MaybeXS" : "0",
            "Log::Any" : "0",
            "MIME::Base64" : "0",
            "Moo" : "0",
            "URI" : "0",
            "namespace::clean" : "0"
         }
      },
      "test" : {
         "requires" : {
            "Path::Tiny" : "0",
            "Test::More" : "0"
         }
      }
   },
   "release_status" : "stable",
   "resources" : {
      "bugtracker" : {
         "web" : "https://github.com/Getty/p5-api-docker/issues"
      },
      "homepage" : "https://github.com/Getty/p5-api-docker",
      "repository" : {

META.json  view on Meta::CPAN

            "class" : "Dist::Zilla::Plugin::License",
            "name" : "@Author::GETTY/@Filter/License",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::Readme",
            "name" : "@Author::GETTY/@Filter/Readme",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::ExtraTests",
            "name" : "@Author::GETTY/@Filter/ExtraTests",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::ExecDir",
            "name" : "@Author::GETTY/@Filter/ExecDir",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::ShareDir",
            "name" : "@Author::GETTY/@Filter/ShareDir",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::MakeMaker",
            "config" : {
               "Dist::Zilla::Role::TestRunner" : {
                  "default_jobs" : 1
               }
            },
            "name" : "@Author::GETTY/@Filter/MakeMaker",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::Manifest",
            "name" : "@Author::GETTY/@Filter/Manifest",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::TestRelease",
            "name" : "@Author::GETTY/@Filter/TestRelease",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::ConfirmRelease",
            "name" : "@Author::GETTY/@Filter/ConfirmRelease",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::UploadToCPAN",
            "name" : "@Author::GETTY/@Filter/UploadToCPAN",

META.json  view on Meta::CPAN

            "class" : "Dist::Zilla::Plugin::MetaConfig",
            "name" : "@Author::GETTY/MetaConfig",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::MetaJSON",
            "name" : "@Author::GETTY/MetaJSON",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::PodSyntaxTests",
            "name" : "@Author::GETTY/PodSyntaxTests",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::Test::ChangesHasContent",
            "name" : "@Author::GETTY/Test::ChangesHasContent",
            "version" : "0.011"
         },
         {
            "class" : "Dist::Zilla::Plugin::GithubMeta",
            "name" : "@Author::GETTY/GithubMeta",
            "version" : "0.58"
         },
         {
            "class" : "Dist::Zilla::Plugin::InstallRelease",
            "name" : "@Author::GETTY/InstallRelease",

META.json  view on Meta::CPAN

            "name" : ":InstallModules",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":IncModules",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":TestFiles",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":ExtraTestFiles",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":ExecFiles",
            "version" : "6.037"
         },
         {
            "class" : "Dist::Zilla::Plugin::FinderCode",
            "name" : ":PerlExecFiles",

META.yml  view on Meta::CPAN

---
abstract: 'Perl client for the Docker Engine API'
author:
  - 'Torsten Raudssus <getty@cpan.org>'
build_requires:
  Path::Tiny: '0'
  Test::More: '0'
configure_requires:
  ExtUtils::MakeMaker: '0'
dynamic_config: 0
generated_by: 'Dist::Zilla version 6.037, CPAN::Meta::Converter version 2.150010'
license: perl
meta-spec:
  url: http://module-build.sourceforge.net/META-spec-v1.4.html
  version: '1.4'
name: API-Docker
requires:

META.yml  view on Meta::CPAN

      version: '6.037'
    -
      class: Dist::Zilla::Plugin::License
      name: '@Author::GETTY/@Filter/License'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::Readme
      name: '@Author::GETTY/@Filter/Readme'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::ExtraTests
      name: '@Author::GETTY/@Filter/ExtraTests'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::ExecDir
      name: '@Author::GETTY/@Filter/ExecDir'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::ShareDir
      name: '@Author::GETTY/@Filter/ShareDir'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::MakeMaker
      config:
        Dist::Zilla::Role::TestRunner:
          default_jobs: 1
      name: '@Author::GETTY/@Filter/MakeMaker'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::Manifest
      name: '@Author::GETTY/@Filter/Manifest'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::TestRelease
      name: '@Author::GETTY/@Filter/TestRelease'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::ConfirmRelease
      name: '@Author::GETTY/@Filter/ConfirmRelease'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::UploadToCPAN
      name: '@Author::GETTY/@Filter/UploadToCPAN'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::MetaConfig
      name: '@Author::GETTY/MetaConfig'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::MetaJSON
      name: '@Author::GETTY/MetaJSON'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::PodSyntaxTests
      name: '@Author::GETTY/PodSyntaxTests'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::Test::ChangesHasContent
      name: '@Author::GETTY/Test::ChangesHasContent'
      version: '0.011'
    -
      class: Dist::Zilla::Plugin::GithubMeta
      name: '@Author::GETTY/GithubMeta'
      version: '0.58'
    -
      class: Dist::Zilla::Plugin::InstallRelease
      name: '@Author::GETTY/InstallRelease'
      version: '0.008'
    -

META.yml  view on Meta::CPAN

    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':InstallModules'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':IncModules'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':TestFiles'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':ExtraTestFiles'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':ExecFiles'
      version: '6.037'
    -
      class: Dist::Zilla::Plugin::FinderCode
      name: ':PerlExecFiles'
      version: '6.037'
    -

Makefile.PL  view on Meta::CPAN

    "IO::Socket::UNIX" => 0,
    "JSON::MaybeXS" => 0,
    "Log::Any" => 0,
    "MIME::Base64" => 0,
    "Moo" => 0,
    "URI" => 0,
    "namespace::clean" => 0
  },
  "TEST_REQUIRES" => {
    "Path::Tiny" => 0,
    "Test::More" => 0
  },
  "VERSION" => "0.002",
  "test" => {
    "TESTS" => "t/*.t"
  }
);


my %FallbackPrereqs = (
  "IO::Socket::UNIX" => 0,
  "JSON::MaybeXS" => 0,
  "Log::Any" => 0,
  "MIME::Base64" => 0,
  "Moo" => 0,
  "Path::Tiny" => 0,
  "Test::More" => 0,
  "URI" => 0,
  "namespace::clean" => 0
);


unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
  delete $WriteMakefileArgs{TEST_REQUIRES};
  delete $WriteMakefileArgs{BUILD_REQUIRES};
  $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs;
}

cpanfile  view on Meta::CPAN

requires 'Moo';
requires 'JSON::MaybeXS';
requires 'MIME::Base64';
requires 'IO::Socket::UNIX';
requires 'URI';
requires 'namespace::clean';
requires 'Log::Any';

on test => sub {
    requires 'Test::More';
    requires 'Path::Tiny';
};

t/author-pod-syntax.t  view on Meta::CPAN

#!perl

BEGIN {
  unless ($ENV{AUTHOR_TESTING}) {
    print qq{1..0 # SKIP these tests are for testing by the author\n};
    exit
  }
}

# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests
use strict; use warnings;
use Test::More;
use Test::Pod 1.41;

all_pod_files_ok();

t/basic.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;

use_ok('API::Docker');
use_ok('API::Docker::Role::HTTP');
use_ok('API::Docker::API::System');
use_ok('API::Docker::API::Containers');
use_ok('API::Docker::API::Images');
use_ok('API::Docker::API::Networks');
use_ok('API::Docker::API::Volumes');
use_ok('API::Docker::API::Exec');
use_ok('API::Docker::Container');
use_ok('API::Docker::Image');
use_ok('API::Docker::Network');
use_ok('API::Docker::Volume');

# Test default construction
my $docker = API::Docker->new(api_version => '1.47');
isa_ok($docker, 'API::Docker');
is($docker->host, 'unix:///var/run/docker.sock', 'default host');
is($docker->api_version, '1.47', 'api_version set');
is($docker->tls, 0, 'tls off by default');

# Test custom host
my $docker_tcp = API::Docker->new(
  host        => 'tcp://remote:2375',
  api_version => '1.47',
);
is($docker_tcp->host, 'tcp://remote:2375', 'custom host');

# Test API accessors exist
can_ok($docker, qw(system containers images networks volumes exec));

# Test API accessor types
isa_ok($docker->system, 'API::Docker::API::System');
isa_ok($docker->containers, 'API::Docker::API::Containers');
isa_ok($docker->images, 'API::Docker::API::Images');
isa_ok($docker->networks, 'API::Docker::API::Networks');
isa_ok($docker->volumes, 'API::Docker::API::Volumes');
isa_ok($docker->exec, 'API::Docker::API::Exec');

done_testing;

t/containers.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use lib 't/lib';
use Test::API::Docker::Mock;

check_live_access();

# --- Read Tests (always run) ---

subtest 'list containers' => sub {
  my $docker = test_docker(
    'GET /containers/json' => load_fixture('containers_list'),
  );

  my $containers = $docker->containers->list(all => 1);

  is(ref $containers, 'ARRAY', 'returns array');
  if (@$containers) {

t/containers.t  view on Meta::CPAN

    is($first->State, 'running', 'container state');
    ok($first->is_running, 'is_running returns true for running container');

    my $second = $containers->[1];
    is($second->Id, 'def789ghi012', 'second container id');
    is($second->State, 'exited', 'second container state');
    ok(!$second->is_running, 'is_running returns false for exited container');
  }
};

# --- Write Tests (mock always, live only with WRITE) ---

subtest 'container lifecycle' => sub {
  skip_unless_write();

  my $docker = test_docker(
    'POST /containers/create'         => { Id => 'mock123', Warnings => [] },
    'POST /containers/mock123/start'  => undef,
    'GET /containers/mock123/json'    => load_fixture('container_inspect'),
    'GET /containers/mock123/top'     => {
      Titles    => ['UID', 'PID', 'PPID', 'C', 'STIME', 'TTY', 'TIME', 'CMD'],

t/containers.t  view on Meta::CPAN

  $docker->containers->unpause($id);
  pass('container unpaused');

  $docker->containers->stop($id, timeout => 3);
  pass('container stopped');

  $docker->containers->remove($id);
  pass('container removed');
};

# --- Validation Tests (always run, no Docker needed) ---

subtest 'container ID required' => sub {
  my $docker = test_docker();

  eval { $docker->containers->inspect(undef) };
  like($@, qr/Container ID required/, 'croak on missing ID for inspect');

  eval { $docker->containers->start(undef) };
  like($@, qr/Container ID required/, 'croak on missing ID for start');

t/images.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use lib 't/lib';
use Test::API::Docker::Mock;

check_live_access();

# --- Read Tests (always run) ---

subtest 'list images' => sub {
  my $docker = test_docker(
    'GET /images/json' => load_fixture('images_list'),
  );

  my $images = $docker->images->list;

  is(ref $images, 'ARRAY', 'returns array');
  if (@$images) {

t/images.t  view on Meta::CPAN


  my $results = $docker->images->search('nginx');

  is(ref $results, 'ARRAY', 'search returns array');

  unless (is_live()) {
    is($results->[0]{name}, 'nginx', 'found nginx');
  }
};

# --- Write Tests (mock always, live only with WRITE) ---

subtest 'image build and pull lifecycle' => sub {
  skip_unless_write();

  my $docker = test_docker(
    'POST /build' => sub {
      my ($method, $path, %opts) = @_;
      ok(defined $opts{raw_body}, 'raw_body present in request');
      is($opts{content_type}, 'application/x-tar', 'content type is tar');
      return { stream => 'Successfully built abc123def456' };

t/images.t  view on Meta::CPAN

    pass('pull completed');

    $docker->images->tag('nginx:latest', repo => 'myrepo/nginx', tag => 'v1');
    pass('tag completed');

    my $removed = $docker->images->remove('nginx:latest');
    is(ref $removed, 'ARRAY', 'remove returns array of actions');
  }
};

# --- Validation Tests (always run, no Docker needed) ---

subtest 'build requires context' => sub {
  my $docker = test_docker();

  eval { $docker->images->build(t => 'myapp:latest') };
  like($@, qr/Build context required/, 'croak on missing context');
};

subtest 'image name required' => sub {
  my $docker = test_docker();

t/images_push_auth.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use JSON::MaybeXS qw( decode_json );
use MIME::Base64 qw( decode_base64 decode_base64url );

use API::Docker::API::Images;

sub b64url_decode {
    my ($s) = @_;
    $s =~ tr{-_}{+/};
    my $pad = (4 - length($s) % 4) % 4;
    $s .= '=' x $pad;

t/lib/Test/API/Docker/Mock.pm  view on Meta::CPAN

package Test::API::Docker::Mock;
use strict;
use warnings;
use JSON::MaybeXS qw( decode_json encode_json );
use Path::Tiny;
use Carp qw( croak );
use Test::More;

use Exporter 'import';
our @EXPORT = qw(
  test_docker
  load_fixture
  is_live
  can_write
  skip_unless_write
  check_live_access
  register_cleanup

t/networks.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use lib 't/lib';
use Test::API::Docker::Mock;

check_live_access();

# --- Read Tests (always run) ---

subtest 'list networks' => sub {
  my $docker = test_docker(
    'GET /networks' => load_fixture('networks_list'),
  );

  my $networks = $docker->networks->list;

  is(ref $networks, 'ARRAY', 'returns array');
  if (@$networks) {

t/networks.t  view on Meta::CPAN

    is(scalar @$networks, 2, 'two networks');

    my $first = $networks->[0];
    is($first->Name, 'bridge', 'network name');
    is($first->Driver, 'bridge', 'network driver');
    is($first->Scope, 'local', 'network scope');
    ok(!$first->Internal, 'not internal');
  }
};

# --- Write Tests (mock always, live only with WRITE) ---

subtest 'network lifecycle' => sub {
  skip_unless_write();

  my $docker = test_docker(
    'POST /networks/create' => sub {
      my ($method, $path, %opts) = @_;
      is($opts{body}{Name}, 'test-net', 'network name in body') unless is_live();
      return { Id => 'mock-net-123', Warning => '' };
    },

t/networks.t  view on Meta::CPAN

    pass('connect completed');

    $docker->networks->disconnect($id, Container => 'abc123');
    pass('disconnect completed');
  }

  $docker->networks->remove($id);
  pass('network removed');
};

# --- Validation Tests (always run, no Docker needed) ---

subtest 'network ID required' => sub {
  my $docker = test_docker();

  eval { $docker->networks->inspect(undef) };
  like($@, qr/Network ID required/, 'croak on missing ID for inspect');

  eval { $docker->networks->remove(undef) };
  like($@, qr/Network ID required/, 'croak on missing ID for remove');
};

t/release-changes_has_content.t  view on Meta::CPAN


BEGIN {
  unless ($ENV{RELEASE_TESTING}) {
    print qq{1..0 # SKIP these tests are for release candidate testing\n};
    exit
  }
}

use Test::More tests => 2;

note 'Checking Changes';
my $changes_file = 'Changes';
my $newver = '0.002';
my $trial_token = '-TRIAL';
my $encoding = 'UTF-8';

SKIP: {
    ok(-e $changes_file, "$changes_file file exists")
        or skip 'Changes is missing', 1;

t/system.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use lib 't/lib';
use Test::API::Docker::Mock;

check_live_access();

subtest 'system info' => sub {
  my $docker = test_docker(
    'GET /info' => load_fixture('system_info'),
  );

  my $info = $docker->system->info;

t/version.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use lib 't/lib';
use Test::API::Docker::Mock;

check_live_access();

subtest 'version info' => sub {
  my $docker = test_docker(
    'GET /version' => load_fixture('system_version'),
  );

  my $version = $docker->system->version;

t/volumes.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use lib 't/lib';
use Test::API::Docker::Mock;

check_live_access();

# --- Read Tests (always run) ---

subtest 'list volumes' => sub {
  my $docker = test_docker(
    'GET /volumes' => load_fixture('volumes_list'),
  );

  my $volumes = $docker->volumes->list;

  is(ref $volumes, 'ARRAY', 'returns array');
  if (@$volumes) {

t/volumes.t  view on Meta::CPAN


    my $first = $volumes->[0];
    is($first->Name, 'my-data', 'volume name');
    is($first->Driver, 'local', 'volume driver');
    is($first->Scope, 'local', 'volume scope');
    is_deeply($first->Labels, { project => 'test' }, 'volume labels');
    like($first->Mountpoint, qr{/var/lib/docker/volumes/my-data}, 'mountpoint');
  }
};

# --- Write Tests (mock always, live only with WRITE) ---

subtest 'volume lifecycle' => sub {
  skip_unless_write();

  my $docker = test_docker(
    'POST /volumes/create' => sub {
      my ($method, $path, %opts) = @_;
      is($opts{body}{Name}, 'test-vol', 'volume name in body') unless is_live();
      return {
        Name       => 'test-vol',

t/volumes.t  view on Meta::CPAN

  register_cleanup(sub { eval { $docker->volumes->remove($name, force => 1) } }) if is_live();

  my $inspected = $docker->volumes->inspect($name);
  isa_ok($inspected, 'API::Docker::Volume');
  is($inspected->Driver, 'local', 'volume driver is local');

  $docker->volumes->remove($name);
  pass('volume removed');
};

# --- Validation Tests (always run, no Docker needed) ---

subtest 'volume name required' => sub {
  my $docker = test_docker();

  eval { $docker->volumes->inspect(undef) };
  like($@, qr/Volume name required/, 'croak on missing name for inspect');

  eval { $docker->volumes->remove(undef) };
  like($@, qr/Volume name required/, 'croak on missing name for remove');
};



( run in 2.116 seconds using v1.01-cache-2.11-cpan-cdf2f3d4e48 )