tests: dnsexit2 Convert tests to table-driven

This makes the tests easier to read and extend.
This commit is contained in:
Richard Hansen 2025-01-09 19:14:26 -05:00
parent 6bb80cbdaa
commit 3b73350541

View file

@ -6,6 +6,9 @@ use ddclient::t::HTTPD;
httpd_required(); httpd_required();
local $ddclient::globals{debug} = 1;
local $ddclient::globals{verbose} = 1;
ddclient::load_json_support('dnsexit2'); ddclient::load_json_support('dnsexit2');
httpd()->run(sub { httpd()->run(sub {
@ -17,143 +20,160 @@ httpd()->run(sub {
})]]; })]];
}); });
local $ddclient::globals{verbose} = 1; sub cmp_update {
my ($a, $b) = @_;
sub decode_and_sort_array { return $a->{name} cmp $b->{name} || $a->{type} cmp $b->{type};
my ($data) = @_;
if (!ref $data) {
$data = decode_json($data);
}
@{$data->{update}} = sort { $a->{type} cmp $b->{type} } @{$data->{update}};
return $data;
} }
subtest 'Testing nic_dnsexit2_update' => sub { sub sort_updates {
httpd()->reset(); my ($req) = @_;
local %ddclient::config = ( return {
'host.my.example.com' => { %$req,
'usev4' => 'ipv4', update => [sort({ cmp_update($a, $b); } @{$req->{update}})],
'wantipv4' => '192.0.2.1', };
'usev6' => 'ipv6', }
'wantipv6' => '2001:db8::1',
'protocol' => 'dnsexit2', sub sort_reqs {
'password' => 'mytestingpassword', my @reqs = map(sort_updates($_), @_);
'zone' => 'my.example.com', my @sorted = sort({
'server' => httpd()->endpoint(), my $ret = $a->{domain} cmp $b->{domain};
'path' => '/update', $ret = @{$a->{update}} <=> @{$b->{update}} if !$ret;
'ttl' => 5 my $i = 0;
}); while (!$ret && $i < @{$a->{update}} && $i < @{$b->{update}}) {
ddclient::nic_dnsexit2_update(undef, 'host.my.example.com'); $ret = cmp_update($a->{update}[$i], $b->{update}[$i]);
my @requests = httpd()->reset(); }
is(scalar(@requests), 1, 'expected number of update requests'); return $ret;
my $req = shift(@requests); } @reqs);
is($req->method(), 'POST', 'Method is correct'); return @sorted;
is($req->uri()->as_string(), '/update', 'URI contains correct path'); }
is($req->header('content-type'), 'application/json', 'Content-Type header is correct');
is($req->header('accept'), 'application/json', 'Accept header is correct'); my @test_cases = (
my $got = decode_and_sort_array($req->content());
my $want = decode_and_sort_array({
'domain' => 'my.example.com',
'apikey' => 'mytestingpassword',
'update' => [
{ {
'type' => 'A', desc => 'both IPv4 and IPv6 are updated together',
'name' => 'host', cfg => {
'content' => '192.0.2.1', 'host.my.example.com' => {
'ttl' => 5, ttl => 5,
wantipv4 => '192.0.2.1',
wantipv6 => '2001:db8::1',
zone => 'my.example.com',
},
},
want => [{
apikey => 'key',
domain => 'my.example.com',
update => [
{
content => '192.0.2.1',
name => 'host',
ttl => 5,
type => 'A',
}, },
{ {
'type' => 'AAAA', content => '2001:db8::1',
'name' => 'host', name => 'host',
'content' => '2001:db8::1', ttl => 5,
'ttl' => 5, type => 'AAAA',
} },
] ],
}); }],
is_deeply($got, $want, 'Data is correct'); },
};
subtest 'Testing nic_dnsexit2_update without a zone set' => sub {
httpd()->reset();
local %ddclient::config = (
'myhost.example.com' => {
'usev4' => 'ipv4',
'wantipv4' => '192.0.2.1',
'protocol' => 'dnsexit2',
'password' => 'anotherpassword',
'server' => httpd()->endpoint(),
'path' => '/update-alt',
'ttl' => 10
});
ddclient::nic_dnsexit2_update(undef, 'myhost.example.com');
my @requests = httpd()->reset();
is(scalar(@requests), 1, 'expected number of update requests');
my $req = shift(@requests);
my $got = decode_and_sort_array($req->content());
my $want = decode_and_sort_array({
'domain' => 'myhost.example.com',
'apikey' => 'anotherpassword',
'update' => [
{ {
'type' => 'A', desc => 'zone defaults to host',
'name' => '', cfg => {
'content' => '192.0.2.1', 'host.my.example.com' => {
'ttl' => 10, ttl => 10,
} wantipv4 => '192.0.2.1',
] },
}); },
is_deeply($got, $want, 'Data is correct'); want => [{
}; apikey => 'key',
domain => 'host.my.example.com',
subtest 'Testing nic_dnsexit2_update with two hostnames, one with a zone and one without' => sub { update => [
httpd()->reset(); {
local %ddclient::config = ( content => '192.0.2.1',
name => '',
ttl => 10,
type => 'A',
},
],
}],
},
{
desc => 'two hosts, different zones',
cfg => {
'host1.example.com' => { 'host1.example.com' => {
'usev4' => 'ipv4', ttl => 5,
'wantipv4' => '192.0.2.1', wantipv4 => '192.0.2.1',
'protocol' => 'dnsexit2', # 'zone' intentionally not set, so it will default to 'host1.example.com'.
'password' => 'testingpassword',
'server' => httpd()->endpoint(),
'path' => '/update',
'ttl' => 5
}, },
'host2.example.com' => { 'host2.example.com' => {
'usev6' => 'ipv6', ttl => 10,
'wantipv6' => '2001:db8::1', wantipv6 => '2001:db8::1',
'protocol' => 'dnsexit2', zone => 'example.com',
'password' => 'testingpassword', },
'server' => httpd()->endpoint(), },
'path' => '/update', want => [
'ttl' => 10, {
'zone' => 'example.com' apikey => 'key',
} domain => 'host1.example.com',
update => [
{
content => '192.0.2.1',
name => '',
ttl => 5,
type => 'A',
},
],
},
{
apikey => 'key',
domain => 'example.com',
update => [
{
content => '2001:db8::1',
name => 'host2',
ttl => 10,
type => 'AAAA',
},
],
},
],
},
); );
ddclient::nic_dnsexit2_update(undef, 'host1.example.com', 'host2.example.com');
my @requests = httpd()->reset(); for my $tc (@test_cases) {
my @got = map(decode_and_sort_array($_->content()), @requests); subtest($tc->{desc} => sub {
my @want = ( local $ddclient::_l = ddclient::pushlogctx($tc->{desc});
decode_and_sort_array({ local %ddclient::config = ();
'domain' => 'host1.example.com', my @hosts = keys(%{$tc->{cfg}});
'apikey' => 'testingpassword', for my $h (@hosts) {
'update' => [{ $ddclient::config{$h} = {
'type' => 'A', password => 'key',
'name' => '', path => '/update',
'content' => '192.0.2.1', server => httpd()->endpoint(),
'ttl' => 5, %{$tc->{cfg}{$h}},
}],
}),
decode_and_sort_array({
'domain' => 'example.com',
'apikey' => 'testingpassword',
'update' => [{
'type' => 'AAAA',
'name' => 'host2',
'content' => '2001:db8::1',
'ttl' => 10,
}],
}),
);
is_deeply(\@got, \@want, 'data is correct');
}; };
}
ddclient::nic_dnsexit2_update(undef, @hosts);
my @requests = httpd()->reset();
my @got;
for (my $i = 0; $i < @requests; $i++) {
subtest("request $i" => sub {
my $req = $requests[$i];
is($req->method(), 'POST', 'method is POST');
is($req->uri()->as_string(), '/update', 'path is /update');
is($req->header('content-type'), 'application/json', 'Content-Type is JSON');
is($req->header('accept'), 'application/json', 'Accept is JSON');
my $got = decode_json($req->content());
is(ref($got), 'HASH', 'request content is a JSON object');
is(ref($got->{update}), 'ARRAY', 'JSON object has array "update" property');
push(@got, $got);
});
}
@got = sort_reqs(@got);
my @want = sort_reqs(@{$tc->{want}});
is_deeply(\@got, \@want, 'request objects match');
});
}
done_testing(); done_testing();