File Coverage

blib/lib/Testcontainers/Wait/HostPort.pm
Criterion Covered Total %
statement 18 49 36.7
branch 0 14 0.0
condition n/a
subroutine 6 8 75.0
pod 0 1 0.0
total 24 72 33.3


line stmt bran cond sub pod time code
1             package Testcontainers::Wait::HostPort;
2             # ABSTRACT: Wait strategy for listening ports
3              
4 4     4   26 use strict;
  4         8  
  4         151  
5 4     4   22 use warnings;
  4         6  
  4         219  
6 4     4   589 use Moo;
  4         9546  
  4         23  
7 4     4   3393 use Carp qw( croak );
  4         9  
  4         281  
8 4     4   617 use IO::Socket::INET;
  4         24732  
  4         44  
9 4     4   3321 use Log::Any qw( $log );
  4         15942  
  4         31  
10              
11             our $VERSION = '0.001';
12              
13             with 'Testcontainers::Wait::Base';
14              
15             =head1 SYNOPSIS
16              
17             use Testcontainers::Wait;
18              
19             # Wait for a specific port
20             my $wait = Testcontainers::Wait::for_listening_port('80/tcp');
21              
22             # Wait for the lowest exposed port
23             my $wait = Testcontainers::Wait::for_exposed_port();
24              
25             =head1 DESCRIPTION
26              
27             Waits for a TCP port to be listening on the container. This is the most common
28             wait strategy, equivalent to Go's C.
29              
30             =cut
31              
32             has port => (
33             is => 'ro',
34             default => undef,
35             );
36              
37             =attr port
38              
39             The container port to check (e.g., C<80/tcp>). If not set and
40             C is true, uses the lowest exposed port.
41              
42             =cut
43              
44             has use_lowest_port => (
45             is => 'ro',
46             default => 0,
47             );
48              
49             =attr use_lowest_port
50              
51             If true, wait for the lowest exposed port instead of a specific one.
52              
53             =cut
54              
55             sub check {
56 0     0 0   my ($self, $container) = @_;
57              
58 0           my $port = $self->_resolve_port($container);
59 0           my $host = $container->host;
60 0           my $mapped_port = eval { $container->mapped_port($port) };
  0            
61              
62 0 0         unless ($mapped_port) {
63 0           $log->tracef("Port %s not yet mapped", $port);
64 0           return 0;
65             }
66              
67 0           $log->tracef("Checking %s:%s (container port %s)", $host, $mapped_port, $port);
68              
69 0           my $sock = IO::Socket::INET->new(
70             PeerAddr => $host,
71             PeerPort => $mapped_port,
72             Proto => 'tcp',
73             Timeout => 1,
74             );
75              
76 0 0         if ($sock) {
77 0           close($sock);
78 0           $log->debugf("Port %s is listening on %s:%s", $port, $host, $mapped_port);
79 0           return 1;
80             }
81              
82 0           return 0;
83             }
84              
85             =method check($container)
86              
87             Check if the target port is listening. Returns true/false.
88              
89             =cut
90              
91             sub _resolve_port {
92 0     0     my ($self, $container) = @_;
93              
94 0 0         if ($self->port) {
95 0           my $port = $self->port;
96 0 0         $port = "$port/tcp" unless $port =~ m{/};
97 0           return $port;
98             }
99              
100 0 0         if ($self->use_lowest_port) {
101 0           my $ports = $container->request->exposed_ports;
102 0 0         croak "No exposed ports configured" unless @$ports;
103              
104             # Find lowest port number
105             my @sorted = sort {
106 0           my ($a_num) = $a =~ /^(\d+)/;
  0            
107 0           my ($b_num) = $b =~ /^(\d+)/;
108 0           $a_num <=> $b_num;
109             } @$ports;
110              
111 0           my $port = $sorted[0];
112 0 0         $port = "$port/tcp" unless $port =~ m{/};
113 0           return $port;
114             }
115              
116 0           croak "No port specified and use_lowest_port is false";
117             }
118              
119             1;