File Coverage

blib/lib/Testcontainers.pm
Criterion Covered Total %
statement 30 52 57.6
branch 0 10 0.0
condition 0 26 0.0
subroutine 10 12 83.3
pod 0 2 0.0
total 40 102 39.2


line stmt bran cond sub pod time code
1             package Testcontainers;
2             # ABSTRACT: Testcontainers for Perl - Docker containers for testing
3              
4 3     3   349527 use strict;
  3         6  
  3         99  
5 3     3   15 use warnings;
  3         5  
  3         182  
6 3     3   29 use Carp qw( croak );
  3         11  
  3         237  
7 3     3   1414 use Log::Any qw( $log );
  3         30321  
  3         17  
8              
9 3     3   8413 use Testcontainers::DockerClient;
  3         13  
  3         103  
10 3     3   1522 use Testcontainers::Container;
  3         11  
  3         125  
11 3     3   1545 use Testcontainers::ContainerRequest;
  3         11  
  3         182  
12 3     3   21 use Testcontainers::Labels qw( session_id );
  3         4  
  3         143  
13 3     3   1496 use Testcontainers::Wait;
  3         16  
  3         254  
14              
15             our $VERSION = '0.001';
16              
17 3     3   27 use Exporter 'import';
  3         7  
  3         1582  
18             our @EXPORT_OK = qw( run terminate_container );
19              
20             =head1 SYNOPSIS
21              
22             use Testcontainers qw( run terminate_container );
23             use Testcontainers::Wait;
24              
25             # Simple usage - run a container
26             my $container = run('nginx:alpine',
27             exposed_ports => ['80/tcp'],
28             wait_for => Testcontainers::Wait::for_listening_port('80/tcp'),
29             );
30              
31             # Get connection details
32             my $host = $container->host;
33             my $port = $container->mapped_port('80/tcp');
34              
35             # Use the container in tests...
36              
37             # Clean up
38             $container->terminate;
39              
40             # Or with environment variables and labels
41             my $container = run('postgres:16-alpine',
42             exposed_ports => ['5432/tcp'],
43             env => { POSTGRES_PASSWORD => 'test', POSTGRES_DB => 'testdb' },
44             labels => { 'testcontainers' => 'true' },
45             wait_for => Testcontainers::Wait::for_log('database system is ready to accept connections'),
46             );
47              
48             =head1 DESCRIPTION
49              
50             Testcontainers for Perl is a Perl library that makes it simple to create and clean up
51             container-based dependencies for automated integration/smoke tests. The clean,
52             easy-to-use API enables developers to programmatically define containers that
53             should be run as part of a test and clean up those resources when the test is done.
54              
55             This library is inspired by the L
56             project and uses L as its Docker client library.
57              
58             =head1 FEATURES
59              
60             =over
61              
62             =item * Simple API for creating and managing test containers
63              
64             =item * Wait strategies: port listening, HTTP endpoints, log messages, health checks
65              
66             =item * Pre-built modules for popular services (PostgreSQL, MySQL, Redis, Nginx)
67              
68             =item * Automatic container cleanup
69              
70             =item * Port mapping and host resolution
71              
72             =item * Environment variable and label support
73              
74             =item * Container lifecycle hooks
75              
76             =back
77              
78             =cut
79              
80             sub run {
81 0     0 0   my ($image, %opts) = @_;
82 0 0         croak "Image name required" unless $image;
83              
84 0           $log->debugf("Creating container from image: %s", $image);
85              
86             # Build a ContainerRequest from the options
87             my $request = Testcontainers::ContainerRequest->new(
88             image => $image,
89             exposed_ports => $opts{exposed_ports} // [],
90             env => $opts{env} // {},
91             labels => $opts{labels} // {},
92             _internal_labels => $opts{_internal_labels} // {},
93             cmd => $opts{cmd} // [],
94             entrypoint => $opts{entrypoint} // [],
95             name => $opts{name} // undef,
96             wait_for => $opts{wait_for} // undef,
97             tmpfs => $opts{tmpfs} // {},
98             startup_timeout => $opts{startup_timeout} // 60,
99             privileged => $opts{privileged} // 0,
100             network_mode => $opts{network_mode} // undef,
101 0   0       networks => $opts{networks} // [],
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
102             session_id => session_id(),
103             );
104              
105             # Create the Docker client
106             my $docker = Testcontainers::DockerClient->new(
107 0 0         ($opts{docker_host} ? (docker_host => $opts{docker_host}) : ()),
108             );
109              
110             # Pull image if needed
111 0 0         $docker->pull_image($image) unless $opts{no_pull};
112              
113             # Create container configuration
114 0           my $config = $request->to_docker_config;
115              
116             # Create the container
117 0           my $create_result = $docker->create_container($config, $request->name);
118 0           my $container_id = $create_result->{Id};
119              
120 0           $log->debugf("Created container: %s", $container_id);
121              
122             # Create the Container object
123 0           my $container = Testcontainers::Container->new(
124             id => $container_id,
125             image => $image,
126             docker => $docker,
127             request => $request,
128             );
129              
130             # Start the container
131 0           $docker->start_container($container_id);
132 0           $log->debugf("Started container: %s", $container_id);
133              
134             # Refresh container info to get port mappings
135 0           $container->refresh;
136              
137             # Execute wait strategy if defined
138 0 0         if ($request->wait_for) {
139 0           $log->debug("Executing wait strategy...");
140 0           $request->wait_for->wait_until_ready($container, $request->startup_timeout);
141 0           $log->debug("Container is ready");
142             }
143              
144 0           return $container;
145             }
146              
147             =func run($image, %opts)
148              
149             Create and start a new container. Returns a L object.
150              
151             Arguments:
152              
153             =over
154              
155             =item * C<$image> - Docker image name (required)
156              
157             =item * C - ArrayRef of ports to expose (e.g., C<['80/tcp', '443/tcp']>)
158              
159             =item * C - HashRef of environment variables
160              
161             =item * C - HashRef of container labels
162              
163             =item * C - ArrayRef of command arguments
164              
165             =item * C - ArrayRef for container entrypoint
166              
167             =item * C - Container name (optional)
168              
169             =item * C - Wait strategy object (from L)
170              
171             =item * C - HashRef of tmpfs mounts (path => options)
172              
173             =item * C - Timeout in seconds for wait strategy (default: 60)
174              
175             =item * C - Run in privileged mode (default: false)
176              
177             =item * C - Docker network mode
178              
179             =item * C - ArrayRef of network names
180              
181             =item * C - Docker daemon URL (overrides DOCKER_HOST)
182              
183             =item * C - Skip pulling the image (default: false)
184              
185             =back
186              
187             =cut
188              
189             sub terminate_container {
190 0     0 0   my ($container) = @_;
191 0 0         return unless $container;
192 0           return $container->terminate;
193             }
194              
195             =func terminate_container($container)
196              
197             Terminate and remove a container. Safe to call with undef.
198              
199             =cut
200              
201             =head1 ENVIRONMENT VARIABLES
202              
203             =over
204              
205             =item C
206              
207             Docker daemon connection URL. Default: C
208              
209             =item C
210              
211             Set to C<1> to disable the Ryuk resource reaper (container cleanup).
212              
213             =back
214              
215             =head1 SEE ALSO
216              
217             =over
218              
219             =item * L - Container instance methods
220              
221             =item * L - Wait strategy factory
222              
223             =item * L - PostgreSQL module
224              
225             =item * L - MySQL module
226              
227             =item * L - Redis module
228              
229             =item * L - Docker client library
230              
231             =item * L - Testcontainers for Go (reference)
232              
233             =back
234              
235             =cut
236              
237             1;