dyndns2: Add tests
This commit is contained in:
parent
622abfca2c
commit
3d345ff08b
3 changed files with 306 additions and 1 deletions
|
@ -75,6 +75,7 @@ handwritten_tests = \
|
|||
t/is-and-extract-ipv6-global.pl \
|
||||
t/logmsg.pl \
|
||||
t/parse_assignments.pl \
|
||||
t/protocol_dyndns2.pl \
|
||||
t/skip.pl \
|
||||
t/ssl-validate.pl \
|
||||
t/use_web.pl \
|
||||
|
|
|
@ -131,6 +131,7 @@ my $daemon_default = ($programd =~ /d$/) ? interval('5m') : undef;
|
|||
# Current Logger instance. To push a context prefix onto the context stack:
|
||||
# local _l = pushlogctx('additional context goes here');
|
||||
our $_l = ddclient::Logger->new();
|
||||
our @_test_headers;
|
||||
|
||||
$ENV{'PATH'} = (exists($ENV{PATH}) ? "$ENV{PATH}:" : "") . "/sbin:/usr/sbin:/bin:/usr/bin:/etc:/usr/lib:";
|
||||
|
||||
|
@ -2728,7 +2729,7 @@ sub geturl {
|
|||
push(@curlopt, "user=\"".escape_curl_param("${login}:${password}").'"') if (defined($login) && defined($password));
|
||||
push(@curlopt, "proxy=\"".escape_curl_param("${protocol}://${proxy}").'"') if defined($proxy);
|
||||
push(@curlopt, "url=\"".escape_curl_param("${protocol}://${server}/${url}").'"');
|
||||
push(@curlopt, map('header="' . escape_curl_param($_) . '"',
|
||||
push(@curlopt, map('header="' . escape_curl_param($_) . '"', @_test_headers,
|
||||
ref($headers) eq 'ARRAY' ? @$headers : split('\n', $headers)));
|
||||
|
||||
# Add in the data if any was provided (for POST/PATCH)
|
||||
|
|
303
t/protocol_dyndns2.pl
Normal file
303
t/protocol_dyndns2.pl
Normal file
|
@ -0,0 +1,303 @@
|
|||
use Test::More;
|
||||
use Scalar::Util qw(blessed);
|
||||
use MIME::Base64;
|
||||
eval { require ddclient::Test::Fake::HTTPD; } or plan(skip_all => $@);
|
||||
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
|
||||
eval { require 'ddclient'; } or BAIL_OUT($@);
|
||||
|
||||
my $httpd = ddclient::Test::Fake::HTTPD->new();
|
||||
$httpd->run(sub {
|
||||
my ($req) = @_;
|
||||
diag('==============================================================================');
|
||||
diag("Test server received request:\n" . $req->as_string());
|
||||
my $headers = ['content-type' => 'text/plain; charset=utf-8'];
|
||||
my $wantauthn = 'Basic ' . encode_base64('username:password', '');
|
||||
return [401, [@$headers, 'www-authenticate' => 'Basic realm="realm", charset="UTF-8"'],
|
||||
['authentication required']] if ($req->header('authorization') // '') ne $wantauthn;
|
||||
return [400, $headers, ['invalid method: ' . $req->method()]] if $req->method() ne 'GET';
|
||||
return [400, $headers, ['unexpected request: ' . $req->uri() . "\n",
|
||||
'want: ' . $req->header('want-req')]]
|
||||
if $req->uri() ne $req->header('want-req');
|
||||
return [200, $headers, [map("$_\n", $req->header('line'))]];
|
||||
});
|
||||
diag("started IPv4 HTTP server running at " . $httpd->endpoint());
|
||||
|
||||
{
|
||||
package Logger;
|
||||
BEGIN { push(our @ISA, qw(ddclient::Logger)); }
|
||||
sub new {
|
||||
my ($class, $parent) = @_;
|
||||
my $self = $class->SUPER::new(undef, $parent);
|
||||
$self->{logs} = [];
|
||||
return $self;
|
||||
}
|
||||
sub _log {
|
||||
my ($self, $args) = @_;
|
||||
push(@{$self->{logs}}, $args)
|
||||
if ($args->{label} // '') =~ qr/^(?:WARNING|FATAL|SUCCESS|FAILED)$/;
|
||||
return $self->SUPER::_log($args);
|
||||
}
|
||||
}
|
||||
|
||||
my @test_cases = (
|
||||
{
|
||||
desc => 'IPv4, single host, good',
|
||||
cfg => {h1 => {wantipv4 => '192.0.2.1'}},
|
||||
resp => ['good'],
|
||||
wantquery => 'hostname=h1&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv4/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'IPv4, single host, nochg',
|
||||
cfg => {h1 => {wantipv4 => '192.0.2.1'}},
|
||||
resp => ['nochg'],
|
||||
wantquery => 'hostname=h1&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'WARNING', ctx => ['h1'], msg => qr/nochg/},
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv4/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'IPv4, single host, bad',
|
||||
cfg => {h1 => {wantipv4 => '192.0.2.1'}},
|
||||
resp => ['nohost'],
|
||||
wantquery => 'hostname=h1&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'nohost'},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'FAILED', ctx => ['h1'], msg => qr/nohost/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'IPv4, single host, unexpected',
|
||||
cfg => {h1 => {wantipv4 => '192.0.2.1'}},
|
||||
resp => ['WAT'],
|
||||
wantquery => 'hostname=h1&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'WAT'},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'FAILED', ctx => ['h1'], msg => qr/unexpected.*WAT/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'IPv4, multiple hosts, multiple good',
|
||||
cfg => {
|
||||
h1 => {wantipv4 => '192.0.2.1'},
|
||||
h2 => {wantipv4 => '192.0.2.1'},
|
||||
},
|
||||
resp => [
|
||||
'good 192.0.2.1',
|
||||
'good',
|
||||
],
|
||||
wantquery => 'hostname=h1,h2&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
h2 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv4/},
|
||||
{label => 'SUCCESS', ctx => ['h2'], msg => qr/IPv4/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'IPv4, multiple hosts, mixed success',
|
||||
cfg => {
|
||||
h1 => {wantipv4 => '192.0.2.1'},
|
||||
h2 => {wantipv4 => '192.0.2.1'},
|
||||
h3 => {wantipv4 => '192.0.2.1'},
|
||||
},
|
||||
resp => [
|
||||
'good',
|
||||
'nochg',
|
||||
'dnserr',
|
||||
],
|
||||
wantquery => 'hostname=h1,h2,h3&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
h2 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
h3 => {'status-ipv4' => 'dnserr'},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv4/},
|
||||
{label => 'WARNING', ctx => ['h2'], msg => qr/nochg/},
|
||||
{label => 'SUCCESS', ctx => ['h2'], msg => qr/IPv4/},
|
||||
{label => 'FAILED', ctx => ['h3'], msg => qr/dnserr/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'IPv6, single host, good',
|
||||
cfg => {h1 => {wantipv6 => '2001:db8::1'}},
|
||||
resp => ['good'],
|
||||
wantquery => 'hostname=h1&myip=2001:db8::1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv6' => 'good', 'ipv6' => '2001:db8::1', 'mtime' => $ddclient::now},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv6/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'IPv4 and IPv6, single host, good',
|
||||
cfg => {h1 => {wantipv4 => '192.0.2.1', wantipv6 => '2001:db8::1'}},
|
||||
resp => ['good'],
|
||||
wantquery => 'hostname=h1&myip=192.0.2.1,2001:db8::1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1',
|
||||
'status-ipv6' => 'good', 'ipv6' => '2001:db8::1',
|
||||
'mtime' => $ddclient::now},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv4/},
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv6/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'excess status line',
|
||||
cfg => {
|
||||
h1 => {wantipv4 => '192.0.2.1'},
|
||||
h2 => {wantipv4 => '192.0.2.1'},
|
||||
},
|
||||
resp => [
|
||||
'good',
|
||||
'good',
|
||||
'WAT',
|
||||
],
|
||||
wantquery => 'hostname=h1,h2&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
h2 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv4/},
|
||||
{label => 'SUCCESS', ctx => ['h2'], msg => qr/IPv4/},
|
||||
{label => 'WARNING', ctx => ['h1,h2'], msg => qr/unexpected.*\nWAT$/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'multiple hosts, single failure',
|
||||
cfg => {
|
||||
h1 => {wantipv4 => '192.0.2.1'},
|
||||
h2 => {wantipv4 => '192.0.2.1'},
|
||||
},
|
||||
resp => ['abuse'],
|
||||
wantquery => 'hostname=h1,h2&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'abuse'},
|
||||
h2 => {'status-ipv4' => 'abuse'},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'FAILED', ctx => ['h1'], msg => qr/abuse/},
|
||||
{label => 'FAILED', ctx => ['h2'], msg => qr/abuse/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'multiple hosts, single success',
|
||||
cfg => {
|
||||
h1 => {wantipv4 => '192.0.2.1'},
|
||||
h2 => {wantipv4 => '192.0.2.1'},
|
||||
},
|
||||
resp => ['good'],
|
||||
wantquery => 'hostname=h1,h2&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
h2 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'WARNING', ctx => ['h1,h2'], msg => qr//},
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv4/},
|
||||
{label => 'SUCCESS', ctx => ['h2'], msg => qr/IPv4/},
|
||||
],
|
||||
},
|
||||
{
|
||||
desc => 'multiple hosts, fewer results',
|
||||
cfg => {
|
||||
h1 => {wantipv4 => '192.0.2.1'},
|
||||
h2 => {wantipv4 => '192.0.2.1'},
|
||||
h3 => {wantipv4 => '192.0.2.1'},
|
||||
},
|
||||
resp => [
|
||||
'good',
|
||||
'nochg',
|
||||
],
|
||||
wantquery => 'hostname=h1,h2,h3&myip=192.0.2.1',
|
||||
wantstatus => {
|
||||
h1 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
h2 => {'status-ipv4' => 'good', 'ipv4' => '192.0.2.1', 'mtime' => $ddclient::now},
|
||||
h3 => {'status-ipv4' => 'unknown'},
|
||||
},
|
||||
wantlogs => [
|
||||
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv4/},
|
||||
{label => 'WARNING', ctx => ['h2'], msg => qr/nochg/},
|
||||
{label => 'SUCCESS', ctx => ['h2'], msg => qr/IPv4/},
|
||||
{label => 'FAILED', ctx => ['h3'], msg => qr/assuming failure/},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
for my $tc (@test_cases) {
|
||||
diag('==============================================================================');
|
||||
diag("Starting test: $tc->{desc}");
|
||||
diag('==============================================================================');
|
||||
local $ddclient::globals{debug} = 1;
|
||||
local $ddclient::globals{verbose} = 1;
|
||||
my $l = Logger->new($ddclient::_l);
|
||||
local %ddclient::config;
|
||||
$ddclient::config{$_} = {
|
||||
login => 'username',
|
||||
password => 'password',
|
||||
server => $httpd->endpoint(),
|
||||
script => '/nic/update',
|
||||
%{$tc->{cfg}{$_}},
|
||||
} for keys(%{$tc->{cfg}});
|
||||
{
|
||||
local @ddclient::_test_headers = (
|
||||
"want-req: /nic/update?$tc->{wantquery}",
|
||||
map("line: $_", @{$tc->{resp}}),
|
||||
);
|
||||
local $ddclient::_l = $l;
|
||||
ddclient::nic_dyndns2_update(sort(keys(%{$tc->{cfg}})));
|
||||
}
|
||||
# These are the properties in %ddclient::config to check against $tc->{wantstatus}.
|
||||
my %statuskeys = map(($_ => undef), qw(atime ip ipv4 ipv6 mtime status status-ipv4 status-ipv6
|
||||
wantip wantipv4 wantipv6 wtime));
|
||||
my %gotstatus;
|
||||
for my $h (keys(%ddclient::config)) {
|
||||
$gotstatus{$h} = {map(($_ => $ddclient::config{$h}{$_}),
|
||||
grep(exists($statuskeys{$_}), keys(%{$ddclient::config{$h}})))};
|
||||
}
|
||||
is_deeply(\%gotstatus, $tc->{wantstatus}, "$tc->{desc}: status")
|
||||
or diag(ddclient::repr(\%ddclient::config, Names => ['*ddclient::config']));
|
||||
$tc->{wantlogs} //= [];
|
||||
subtest("$tc->{desc}: logs" => sub {
|
||||
my @got = @{$l->{logs}};
|
||||
my @want = @{$tc->{wantlogs}};
|
||||
for my $i (0..$#want) {
|
||||
last if $i >= @got;
|
||||
my $got = $got[$i];
|
||||
my $want = $want[$i];
|
||||
subtest("log $i" => sub {
|
||||
is($got->{label}, $want->{label}, "label matches");
|
||||
is_deeply($got->{ctx}, $want->{ctx}, "context matches");
|
||||
like($got->{msg}, $want->{msg}, "message matches");
|
||||
}) or diag(ddclient::repr(Values => [$got, $want], Names => ['*got', '*want']));
|
||||
}
|
||||
my @unexpected = @got[@want..$#got];
|
||||
ok(@unexpected == 0, "no unexpected logs")
|
||||
or diag(ddclient::repr(\@unexpected, Names => ['*unexpected']));
|
||||
my @missing = @want[@got..$#want];
|
||||
ok(@missing == 0, "no missing logs")
|
||||
or diag(ddclient::repr(\@missing, Names => ['*missing']));
|
||||
});
|
||||
}
|
||||
|
||||
done_testing();
|
Loading…
Reference in a new issue