File Coverage

blib/lib/Testcontainers/Module/PostgreSQL.pm
Criterion Covered Total %
statement 30 50 60.0
branch 0 6 0.0
condition 0 12 0.0
subroutine 10 14 71.4
pod 0 4 0.0
total 40 86 46.5


line stmt bran cond sub pod time code
1             package Testcontainers::Module::PostgreSQL;
2             # ABSTRACT: PostgreSQL container module for Testcontainers
3              
4 2     2   136477 use strict;
  2         4  
  2         84  
5 2     2   10 use warnings;
  2         4  
  2         120  
6 2     2   13 use Carp qw( croak );
  2         17  
  2         106  
7 2     2   506 use Testcontainers;
  2         4  
  2         84  
8 2     2   10 use Testcontainers::Wait;
  2         4  
  2         80  
9              
10             our $VERSION = '0.001';
11              
12 2     2   21 use Exporter 'import';
  2         3  
  2         96  
13             our @EXPORT_OK = qw( postgres_container );
14              
15             use constant {
16 2         547 DEFAULT_IMAGE => 'postgres:16-alpine',
17             DEFAULT_PORT => '5432/tcp',
18             DEFAULT_USER => 'test',
19             DEFAULT_PASSWORD => 'test',
20             DEFAULT_DB => 'testdb',
21 2     2   6 };
  2         3  
22              
23             =head1 SYNOPSIS
24              
25             use Testcontainers::Module::PostgreSQL qw( postgres_container );
26              
27             # Quick start with defaults
28             my $pg = postgres_container();
29              
30             # Custom configuration
31             my $pg = postgres_container(
32             image => 'postgres:15-alpine',
33             username => 'myuser',
34             password => 'mypass',
35             database => 'mydb',
36             );
37              
38             # Get connection details
39             my $host = $pg->host;
40             my $port = $pg->mapped_port('5432/tcp');
41             my $dsn = $pg->connection_string; # "postgresql://test:test@localhost:32789/testdb"
42              
43             # Clean up
44             $pg->terminate;
45              
46             =head1 DESCRIPTION
47              
48             Pre-configured PostgreSQL container module, equivalent to Go's
49             C. Provides a PostgreSQL database
50             ready for testing with sensible defaults.
51              
52             =cut
53              
54             sub postgres_container {
55 0     0 0   my (%opts) = @_;
56              
57 0   0       my $image = $opts{image} // DEFAULT_IMAGE;
58 0   0       my $username = $opts{username} // DEFAULT_USER;
59 0   0       my $password = $opts{password} // DEFAULT_PASSWORD;
60 0   0       my $database = $opts{database} // DEFAULT_DB;
61 0   0       my $port = $opts{port} // DEFAULT_PORT;
62              
63             my $container = Testcontainers::run($image,
64             exposed_ports => [$port],
65             env => {
66             POSTGRES_USER => $username,
67             POSTGRES_PASSWORD => $password,
68             POSTGRES_DB => $database,
69             },
70             _internal_labels => {
71             'org.testcontainers.module' => 'postgresql',
72             },
73             wait_for => Testcontainers::Wait::for_log(
74             'database system is ready to accept connections',
75             occurrences => 2,
76             ),
77             startup_timeout => $opts{startup_timeout} // 60,
78 0 0 0       ($opts{name} ? (name => $opts{name}) : ()),
79             );
80              
81             # Bless into our subclass for extra methods
82 0           return Testcontainers::Module::PostgreSQL::Container->new(
83             _inner => $container,
84             username => $username,
85             password => $password,
86             database => $database,
87             port => $port,
88             );
89             }
90              
91             =func postgres_container(%opts)
92              
93             Create and start a PostgreSQL container. Returns a container object with
94             additional PostgreSQL-specific methods.
95              
96             Options:
97              
98             =over
99              
100             =item * C - Docker image (default: C)
101              
102             =item * C - PostgreSQL user (default: C)
103              
104             =item * C - PostgreSQL password (default: C)
105              
106             =item * C - Database name (default: C)
107              
108             =item * C - Container port (default: C<5432/tcp>)
109              
110             =item * C - Timeout in seconds (default: 60)
111              
112             =item * C - Container name
113              
114             =back
115              
116             =cut
117              
118              
119             package Testcontainers::Module::PostgreSQL::Container;
120              
121 2     2   11 use strict;
  2         3  
  2         31  
122 2     2   6 use warnings;
  2         3  
  2         86  
123 2     2   7 use Moo;
  2         4  
  2         10  
124              
125             has _inner => (is => 'ro', required => 1, handles => [qw(
126             id image host mapped_port mapped_port_info endpoint container_id
127             name state is_running logs exec stop start terminate refresh
128             )]);
129             has username => (is => 'ro', required => 1);
130             has password => (is => 'ro', required => 1);
131             has database => (is => 'ro', required => 1);
132             has port => (is => 'ro', required => 1);
133              
134             sub connection_string {
135 0     0 0   my ($self) = @_;
136 0           my $host = $self->host;
137 0           my $mapped = $self->mapped_port($self->port);
138 0           return sprintf("postgresql://%s:%s\@%s:%s/%s",
139             $self->username, $self->password, $host, $mapped, $self->database);
140             }
141              
142             =method connection_string
143              
144             Returns a PostgreSQL connection string:
145             C
146              
147             =cut
148              
149             sub dsn {
150 0     0 0   my ($self) = @_;
151 0           my $host = $self->host;
152 0           my $mapped = $self->mapped_port($self->port);
153 0           return sprintf("dbi:Pg:dbname=%s;host=%s;port=%s",
154             $self->database, $host, $mapped);
155             }
156              
157             =method dsn
158              
159             Returns a DBI-compatible DSN: C
160              
161             =cut
162              
163             sub DEMOLISH {
164 0     0 0   my ($self, $in_global) = @_;
165 0 0         return if $in_global;
166 0 0         $self->_inner->terminate if $self->_inner;
167 0           return;
168             }
169              
170             1;