| Test-SMTP documentation | Contained in the Test-SMTP distribution. |
Test::SMTP - Module for writing SMTP Server tests
use Test::SMTP;
plan tests => 10;
# Constructors
my $client1 = Test::SMTP->connect_ok('connect to mailhost',
Host => '127.0.0.1', AutoHello => 1);
$client1->mail_from_ok('test@example.com', 'Accept an example mail from');
$client1->rcpt_to_ko('test2@example.com', 'Reject an example domain in rcpt to');
$client1->quit_ok('Quit OK');
my $client2 = Test::SMTP->connect_ok('connect to mailhost',
Host => '127.0.0.1', AutoHello => 1);
...
This module is designed for easily building tests for SMTP servers.
Test::SMTP is a subclass of Net::SMTP_auth, that is a subclass of Net::SMTP, that in turn is a subclass of Net::Cmd and IO::Socket::INET. Don't be too confident of it beeing a Net::SMTP_auth subclass for too much time, though (v 0.03 changed from Net::SMTP to Net::SMTP_auth so you can control authentication tests better). Compatibility will always try to be kept so you can still call the subclass methods.
Plan tests a la Test::More. Exported on demand (not necessary to export if you are already using a test module that exports plan).
use Test::SMTP qw(plan); plan tests => 5;
Passes if the client connects to the SMTP Server. Everything after name is passed to the Net::SMTP_auth new method. returns a Test::SMTP object.
Net::SMTP_auth parameters of interest: Port => $port (connect to non-standard SMTP port) Hello => 'my (he|eh)lo' hello to send to the server Debug => 1 Outputs via STDERR the conversation with the server
You have to pass AutoHello => 1, this will enable auto EHLO/HELO negotiation.
Passes test if the client does not connect to the SMTP Server. Everything after name is passed to the Net::SMTP_auth new method.
Passes if the last SMTP code returned by the server was expected.
Passes if the last SMTP code returned by the server was'nt expected.
Passes if the last SMTP code returned by the server indicates success.
Passes if the last SMTP code returned by the server doesn't indicate success.
Passes if the last SMTP code returned by the server indicates failure (either temporary or permanent).
Passes if the last SMTP code returned by the server doesn't indicate failure (either temporary or permanent).
Passes if the last SMTP code returned by the server indicates temporary failure
Passes if the last SMTP code returned by the server doesn't indicate temporary failure
Passes if the last SMTP code returned by the server indicates permanent failure
Passes if the last SMTP code returned by the server doesn't indicate permanent failure
Passes if the last SMTP message returned by the server matches the regex.
Passes if the last SMTP message returned by the server does'nt match the regex.
Passes if $user with $password with SASL method $method is AUTHorized on the server.
Passes if $user with $password with SASL method $method is not AUTHorized on the server.
Start TLS conversation with the server. Pass if server said that it's OK to start TLS and the SSL negotiation went OK.
Start TLS conversation with the server. Pass if server said that it's not OK to start TLS or if the SSL negotiation failed.
Do EHLO/HELO negotiation. Useful only after starttls_ok/ko
Do EHLO/HELO negotiation. Useful only after starttls_ok/ko
Send a RSET command to the server. Pass if command was successful
Send an RSET to the server. Pass if command was not successful
Passes test if server said it supported capa capability on ESMTP EHLO
Passes test if server didn't say it supported capa capability on ESMTP EHLO
Compares server capa capability extra information with operator against expected.
Passes if server capa capability extra information matches against REGEX.
Passes if server capa capability extra information doesn't match against REGEX.
Passes if server banner matches against REGEX.
Passes if server banner doesn't match against REGEX.
Passes if server's announced domain matches against REGEX.
Passes if server's announced domain doesn't match against REGEX.
Sends a MAIL FROM: from to the server. Passes if the command succeeds
Sends a MAIL FROM: from to the server. Passes if the command isn't successful
Sends a RCPT TO: to to the server. Passes if the command succeeds
Sends a RCPT TO: to to the server. Passes if the command isn't successful
Sends a DATA command to the server. Passes if the command is successful. After calling this method, you should call datasend.
Sends a DATA command to the server. Passes if the command is'nt successful
Sends a .<CR><LF> command to the server. Passes if the command is successful.
Sends a .<CR><LF> command to the server. Passes if the command is not successful.
Sends HELP HELP_ON command to the server. If the returned text matches REGEX, the test passes. To test plain HELP command, pass undef in HELP_ON.
Sends HELP HELP_ON command to the server. If the returned text doesn't match REGEX, the test passes. To test plain HELP command, pass undef in HELP_ON.
Send a QUIT command to the server. Pass if command was successful
Send a QUIT command to the server. Pass if command was'nt successful
Issues a MAIL FROM: from command to the server.
Issues a RCPT TO: to command to the server.
Jose Luis Martinez
CAPSiDE
jlmartinez@capside.com
http://www.pplusdomain.net/
http://www.capside.com/
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the LICENSE file included with this module.
| Test-SMTP documentation | Contained in the Test-SMTP distribution. |
package Test::SMTP; use strict; use warnings; BEGIN { use Exporter (); use Carp; use Net::SMTP_auth; use Test::Builder::Module; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); $VERSION = '0.04'; @ISA = qw(Net::SMTP_auth Test::Builder::Module); #Give a hoot don't pollute, do not export more than needed by default @EXPORT = qw(); @EXPORT_OK = qw(plan); %EXPORT_TAGS = (); }
sub plan { my $tb = __PACKAGE__->builder; $tb->plan(@_); }
sub connect_ok { my ($class, $name, %params) = @_; if ((not defined($params{'AutoHello'})) or ($params{'AutoHello'} != 1)){ croak "Can only handle AutoHello for now..."; } my $smtp = Net::SMTP_auth->new(%params); my $tb = __PACKAGE__->builder(); $tb->ok(defined $smtp, $name); if (not defined($smtp)){ return undef; } bless $smtp, $class; return $smtp; }
sub connect_ko { my ($class, $name, @params) = @_; my $smtp = Net::SMTP_auth->new(@params); my $tb = __PACKAGE__->builder(); $tb->ok(not(defined $smtp), $name); if (not defined $smtp){ return undef; } bless $smtp, $class; return $smtp; }
sub code_is { my ($self, $expected, $name) = @_; my $tb = __PACKAGE__->builder(); $tb->cmp_ok($self->code(), '==', $expected, $name); }
sub code_isnt { my ($self, $expected, $name) = @_; my $tb = __PACKAGE__->builder(); $tb->cmp_ok($self->code(), '!=', $expected, $name); }
sub code_is_success { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (_is_between($self->code(), 200, 399)){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub code_isnt_success { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (_is_between($self->code(), 200, 399)){ $tb->ok(0, $name); $self->_smtp_diag; } else { $tb->ok(1, $name); } }
sub code_is_failure { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (not _is_between($self->code(), 200, 399)){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub code_isnt_failure { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (not _is_between($self->code(), 200, 399)){ $tb->ok(0, $name); $self->_smtp_diag; } else { $tb->ok(1, $name); } }
sub code_is_temporary { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (_is_between($self->code(), 400, 499)){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub code_isnt_temporary { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (_is_between($self->code(), 400, 499)){ $tb->ok(0, $name); $self->_smtp_diag; } else { $tb->ok(1, $name); } }
sub code_is_permanent { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (_is_between($self->code(), 500, 599)){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub code_isnt_permanent { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (_is_between($self->code(), 500, 599)){ $tb->ok(0, $name); $self->_smtp_diag; } else { $tb->ok(1, $name); } }
sub message_like { my ($self, $expected, $name) = @_; my $tb = __PACKAGE__->builder(); my $message = $self->message(); $tb->like($message, $expected, $name); }
sub message_unlike { my ($self, $expected, $name) = @_; my $tb = __PACKAGE__->builder(); my $message = $self->message(); $tb->unlike($message, $expected, $name); }
sub auth_ok { my ($self, $method, $user, $password, $name) = @_; my $tb = __PACKAGE__->builder(); my $result = $self->auth($method, $user, $password); if ($result){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub auth_ko { my ($self, $method, $user, $password, $name) = @_; my $tb = __PACKAGE__->builder(); my $result = $self->auth($method, $user, $password); if ($result){ $tb->ok(0, $name); $self->_smtp_diag; } else { $tb->ok(1, $name); } }
sub starttls_ok { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (not ($self->command('STARTTLS')->response() == Net::Cmd::CMD_OK)){ $tb->ok(0, $name); $self->_smtp_diag; return; } if (not $self->_convert_to_ssl()){ $tb->ok(0, $name); $tb->diag('SSL: ' . IO::Socket::SSL::errstr()); return; } $tb->ok(1, $name); }
sub starttls_ko { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if (not ($self->command('STARTTLS')->response() == Net::Cmd::CMD_OK)){ $tb->ok(1, $name); return; } if (not $self->_convert_to_ssl()){ $tb->ok(1, $name); return; } $tb->ok(0, $name); $self->_smtp_diag; $tb->diag('And SSL negotiation went OK'); } sub _convert_to_ssl { my ($self) = @_; require IO::Socket::SSL or die "starttls requires IO::Socket::SSL"; # the socket is stored in ${*self}{'_ssl_sock'}. # If not, when starttls sub ends *$self is not tied to the SSL # socket anymore, instead, it's tied to the old socket. my $ssl_sock = IO::Socket::SSL->new_from_fd($self->fileno) or return 0; ${*self}{'_ssl_sock'} = $ssl_sock; *$self = *$ssl_sock; }
sub hello_ok { my ($self, $hello, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->hello($hello)){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub hello_ko { my ($self, $hello, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->hello($hello)){ $tb->ok(0, $name); $self->_smtp_diag; } else { $tb->ok(1, $name); } }
sub rset_ok { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->reset){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub rset_ko { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->reset){ $tb->ok(0, $name); $self->_smtp_diag; } else { $tb->ok(1, $name); } }
sub supports_ok { my ($self, $capa, $name) = @_; my $tb = __PACKAGE__->builder(); if (defined $self->supports($capa)){ $tb->ok(1, $name); } else { $tb->ok(0, $name); } }
sub supports_ko { my ($self, $capa, $name) = @_; my $tb = __PACKAGE__->builder(); if (defined $self->supports($capa)){ $tb->ok(0, $name); $tb->diag("Server supports the feature $capa with " . $self->supports($capa)); } else { $tb->ok(1, $name); } }
sub supports_cmp_ok { my ($self, $capa, $operator, $expected, $name) = @_; my $tb = __PACKAGE__->builder(); my $val = $self->supports($capa); $tb->cmp_ok($val, $operator, $expected, $name); }
sub supports_like { my ($self, $capa, $expected, $name) = @_; my $tb = __PACKAGE__->builder(); my $val = $self->supports($capa); $tb->like($val, $expected, $name); }
sub supports_unlike { my ($self, $capa, $expected, $name) = @_; my $tb = __PACKAGE__->builder(); my $val = $self->supports($capa); $tb->unlike($val, $expected, $name); }
sub banner_like { my ($self, $qr, $name) = @_; my $tb = __PACKAGE__->builder(); $tb->like($self->banner(), $qr, $name); }
sub banner_unlike { my ($self, $qr, $name) = @_; my $tb = __PACKAGE__->builder(); $tb->unlike($self->banner(), $qr, $name); }
sub domain_like { my ($self, $qr, $name) = @_; my $tb = __PACKAGE__->builder(); $tb->like($self->domain(), $qr, $name); }
sub domain_unlike { my ($self, $qr, $name) = @_; my $tb = __PACKAGE__->builder(); $tb->unlike($self->domain(), $qr, $name); }
sub mail_from_ok { my ($self, $from, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->mail_from($from)) { $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub mail_from_ko { my ($self, $from, $name) = @_; my $tb = __PACKAGE__->builder(); if (not $self->mail_from($from)) { $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub rcpt_to_ok { my ($self, $to, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->rcpt_to($to)) { $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub rcpt_to_ko { my ($self, $to, $name) = @_; my $tb = __PACKAGE__->builder(); if (not $self->rcpt_to($to)) { $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub data_ok { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->data == 1){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub data_ko { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->data != 1){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } } #sub datasend { # my ($self, $data) = @_; # # if (ref($data) eq 'ARRAY'){ # $self::SUPER->datasend($data); # } #}
sub dataend_ok { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->dataend() == 1){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag(); } }
sub dataend_ko { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); if ($self->dataend() != 1){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag(); } }
sub help_like { my ($self, $help_on, $expected, $name) = @_; my $tb = __PACKAGE__->builder(); $tb->like($self->help($help_on), $expected, $name); }
sub help_unlike { my ($self, $help_on, $expected, $name) = @_; my $tb = __PACKAGE__->builder(); $tb->unlike($self->help($help_on), $expected, $name); }
sub quit_ok { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); $self->quit(); if (_is_between($self->code(), 200, 399)){ $tb->ok(1, $name); } else { $tb->ok(0, $name); $self->_smtp_diag; } }
sub quit_ko { my ($self, $name) = @_; my $tb = __PACKAGE__->builder(); $self->quit(); if (_is_between($self->code(), 200, 399)){ $tb->ok(0, $name); $self->_smtp_diag; } else { $tb->ok(1, $name); } } sub _is_between { my ($what, $start, $end) = @_; return ($what >= $start and $what <= $end); } sub _smtp_diag { my $self = shift; my $tb = __PACKAGE__->builder(); $tb->diag(sprintf(" Got from server %s %s\n", $self->code, $self->message)); }
sub mail_from { return shift->command("MAIL", "FROM:", @_)->response() == Net::Cmd::CMD_OK }
sub rcpt_to { return shift->command("RCPT", "TO:", @_)->response() == Net::Cmd::CMD_OK }
1;