Merge pull request #726 from jeffrego/feature_provider_directnic
Added support for provider Directnic
This commit is contained in:
commit
598dee50ca
7 changed files with 334 additions and 1 deletions
|
@ -90,6 +90,8 @@ repository history](https://github.com/ddclient/ddclient/commits/master).
|
||||||
[#719](https://github.com/ddclient/ddclient/pull/719)
|
[#719](https://github.com/ddclient/ddclient/pull/719)
|
||||||
* `duckdns`: Multiple hosts with the same IP address are now updated together.
|
* `duckdns`: Multiple hosts with the same IP address are now updated together.
|
||||||
[#719](https://github.com/ddclient/ddclient/pull/719)
|
[#719](https://github.com/ddclient/ddclient/pull/719)
|
||||||
|
* `directnic`: Added support for updatng Directnic records.
|
||||||
|
[#726](https://github.com/ddclient/ddclient/pull/726)
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ AM_PL_LOG_FLAGS = -Mstrict -w \
|
||||||
-MDevel::Autoflush
|
-MDevel::Autoflush
|
||||||
handwritten_tests = \
|
handwritten_tests = \
|
||||||
t/builtinfw_query.pl \
|
t/builtinfw_query.pl \
|
||||||
|
t/check_value.pl \
|
||||||
t/get_ip_from_if.pl \
|
t/get_ip_from_if.pl \
|
||||||
t/geturl_connectivity.pl \
|
t/geturl_connectivity.pl \
|
||||||
t/geturl_response.pl \
|
t/geturl_response.pl \
|
||||||
|
@ -74,6 +75,7 @@ handwritten_tests = \
|
||||||
t/is-and-extract-ipv6-global.pl \
|
t/is-and-extract-ipv6-global.pl \
|
||||||
t/logmsg.pl \
|
t/logmsg.pl \
|
||||||
t/parse_assignments.pl \
|
t/parse_assignments.pl \
|
||||||
|
t/protocol_directnic.pl \
|
||||||
t/protocol_dnsexit2.pl \
|
t/protocol_dnsexit2.pl \
|
||||||
t/protocol_dyndns2.pl \
|
t/protocol_dyndns2.pl \
|
||||||
t/skip.pl \
|
t/skip.pl \
|
||||||
|
|
|
@ -20,6 +20,7 @@ Dynamic DNS services currently supported include:
|
||||||
* [DDNS.fm](https://www.ddns.fm/)
|
* [DDNS.fm](https://www.ddns.fm/)
|
||||||
* [DigitalOcean](https://www.digitalocean.com/)
|
* [DigitalOcean](https://www.digitalocean.com/)
|
||||||
* [dinahosting](https://dinahosting.com)
|
* [dinahosting](https://dinahosting.com)
|
||||||
|
* [Directnic](https://directnic.com)
|
||||||
* [DonDominio](https://www.dondominio.com)
|
* [DonDominio](https://www.dondominio.com)
|
||||||
* [DNS Made Easy](https://dnsmadeeasy.com)
|
* [DNS Made Easy](https://dnsmadeeasy.com)
|
||||||
* [DNSExit](https://dnsexit.com/dns/dns-api)
|
* [DNSExit](https://dnsexit.com/dns/dns-api)
|
||||||
|
|
|
@ -365,6 +365,14 @@ pid=@runstatedir@/ddclient.pid # record PID in file.
|
||||||
# password=api-token \
|
# password=api-token \
|
||||||
# example.com,sub.example.com
|
# example.com,sub.example.com
|
||||||
|
|
||||||
|
##
|
||||||
|
## Directnic (directnic.com)
|
||||||
|
##
|
||||||
|
# protocol=directnic,
|
||||||
|
# urlv4=https://directnic.com/dns/gateway/ipv4_token/
|
||||||
|
# urlv6=https://directnic.com/dns/gateway/ipv6_token/
|
||||||
|
# my-domain.com
|
||||||
|
|
||||||
##
|
##
|
||||||
## Infomaniak (www.infomaniak.com)
|
## Infomaniak (www.infomaniak.com)
|
||||||
##
|
##
|
||||||
|
|
83
ddclient.in
83
ddclient.in
|
@ -181,6 +181,7 @@ sub T_OFQDN { 'optional fully qualified host name' }
|
||||||
sub T_FILE { 'file name' }
|
sub T_FILE { 'file name' }
|
||||||
sub T_FQDNP { 'fully qualified host name and optional port number' }
|
sub T_FQDNP { 'fully qualified host name and optional port number' }
|
||||||
sub T_PROTO { 'protocol' }
|
sub T_PROTO { 'protocol' }
|
||||||
|
sub T_URL { 'url including fully qualified host name, optional port number, and path' }
|
||||||
sub T_USE { 'ip strategy' }
|
sub T_USE { 'ip strategy' }
|
||||||
sub T_USEV4 { 'ipv4 strategy' }
|
sub T_USEV4 { 'ipv4 strategy' }
|
||||||
sub T_USEV6 { 'ipv6 strategy' }
|
sub T_USEV6 { 'ipv6 strategy' }
|
||||||
|
@ -781,6 +782,17 @@ our %protocols = (
|
||||||
'server' => setv(T_FQDNP, 0, 0, 'dinahosting.com', undef),
|
'server' => setv(T_FQDNP, 0, 0, 'dinahosting.com', undef),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'directnic' => {
|
||||||
|
'update' => \&nic_directnic_update,
|
||||||
|
'examples' => \&nic_directnic_examples,
|
||||||
|
'variables' => {
|
||||||
|
%{$variables{'protocol-common-defaults'}},
|
||||||
|
'login' => undef,
|
||||||
|
'password' => undef,
|
||||||
|
'urlv4' => setv(T_URL, 0, 0, undef, undef),
|
||||||
|
'urlv6' => setv(T_URL, 0, 0, undef, undef),
|
||||||
|
},
|
||||||
|
},
|
||||||
'dnsmadeeasy' => {
|
'dnsmadeeasy' => {
|
||||||
'update' => \&nic_dnsmadeeasy_update,
|
'update' => \&nic_dnsmadeeasy_update,
|
||||||
'examples' => \&nic_dnsmadeeasy_examples,
|
'examples' => \&nic_dnsmadeeasy_examples,
|
||||||
|
@ -1995,7 +2007,7 @@ sub init_config {
|
||||||
for my $h (keys %config) {
|
for my $h (keys %config) {
|
||||||
my $proto = opt('protocol', $h);
|
my $proto = opt('protocol', $h);
|
||||||
load_sha1_support($proto) if (grep($_ eq $proto, ("freedns", "nfsn")));
|
load_sha1_support($proto) if (grep($_ eq $proto, ("freedns", "nfsn")));
|
||||||
load_json_support($proto) if (grep($_ eq $proto, ("1984", "cloudflare", "digitalocean", "gandi", "godaddy", "hetzner", "yandex", "nfsn", "njalla", "porkbun", "dnsexit2")));
|
load_json_support($proto) if (grep($_ eq $proto, ("1984", "cloudflare", "digitalocean", "directnic", "gandi", "godaddy", "hetzner", "yandex", "nfsn", "njalla", "porkbun", "dnsexit2")));
|
||||||
|
|
||||||
if (!exists($protocols{$proto})) {
|
if (!exists($protocols{$proto})) {
|
||||||
warning("skipping host: %s: unrecognized protocol '%s'", $h, $proto);
|
warning("skipping host: %s: unrecognized protocol '%s'", $h, $proto);
|
||||||
|
@ -2524,6 +2536,9 @@ sub check_value {
|
||||||
$value = lc $value;
|
$value = lc $value;
|
||||||
return undef if !exists $protocols{$value};
|
return undef if !exists $protocols{$value};
|
||||||
|
|
||||||
|
} elsif ($type eq T_URL) {
|
||||||
|
return undef if $value !~ qr{^(?i:https?://)?[^./]+(\.[^./]+)+(:\d+)?(/[^/]+)*/?$};
|
||||||
|
|
||||||
} elsif ($type eq T_USE) {
|
} elsif ($type eq T_USE) {
|
||||||
$value = lc $value;
|
$value = lc $value;
|
||||||
return undef if !exists $ip_strategies{$value};
|
return undef if !exists $ip_strategies{$value};
|
||||||
|
@ -6873,6 +6888,72 @@ sub nic_dinahosting_update {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
## nic_directnic_examples
|
||||||
|
######################################################################
|
||||||
|
sub nic_directnic_examples {
|
||||||
|
return <<"EoEXAMPLE";
|
||||||
|
o 'directnic'
|
||||||
|
|
||||||
|
The 'directnic' protocol is used by directnic (https://directnic.com).
|
||||||
|
Details about the API can be found at https://directnic.com/knowledge#/knowledge/article/3726.
|
||||||
|
|
||||||
|
You must specify at least one of the following variables:
|
||||||
|
* urlv4=https://directnic.com/dns/gateway/ad133743f001e318e455fdc05/ the URL to use to update the A record
|
||||||
|
* urlv6=https://directnic.com/dns/gateway/ad133743f001e318e455fdc06/ the URL to use to update the AAAA record
|
||||||
|
|
||||||
|
urlv4 is required when updating an IPv4 record, and urlv6 is required when updating an IPv6 record.
|
||||||
|
|
||||||
|
Example ${program}.conf file entry:
|
||||||
|
protocol=directnic, \\
|
||||||
|
urlv4=https://directnic.com/dns/gateway/ad133743f001e318e455fdc05/ \\
|
||||||
|
urlv6=https://directnic.com/dns/gateway/ad133743f001e318e455fdc06/ \\
|
||||||
|
myhost.mydomain.com
|
||||||
|
EoEXAMPLE
|
||||||
|
}
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
## nic_directnic_update
|
||||||
|
######################################################################
|
||||||
|
sub nic_directnic_update {
|
||||||
|
for my $h (@_) {
|
||||||
|
local $_l = pushlogctx($h);
|
||||||
|
for my $ipv ('4', '6') {
|
||||||
|
my $ip = delete $config{$h}{"wantipv$ipv"} or next;
|
||||||
|
info("setting IPv$ipv address to $ip");
|
||||||
|
|
||||||
|
my $url = $config{$h}{"urlv$ipv"};
|
||||||
|
if (!defined($url)) {
|
||||||
|
failed("missing urlv$ipv option");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url .= "?data=$ip";
|
||||||
|
my $reply = geturl(proxy => opt('proxy'), url => $url);
|
||||||
|
next if !header_ok($reply);
|
||||||
|
|
||||||
|
(my $body = $reply) =~ s/^.*?\n\n//s;
|
||||||
|
my $response = eval {decode_json($body)};
|
||||||
|
if (ref($response) ne 'HASH') {
|
||||||
|
$config{$h}{"status-ipv$ipv"} = 'bad';
|
||||||
|
failed("response is not a JSON object:\n$body");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($response->{'result'} ne 'success') {
|
||||||
|
$config{$h}{"status-ipv$ipv"} = 'failed';
|
||||||
|
failed("server said:\n$body");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$config{$h}{"ipv$ipv"} = $ip;
|
||||||
|
$config{$h}{"status-ipv$ipv"} = 'good';
|
||||||
|
$config{$h}{'mtime'} = $now;
|
||||||
|
success("IPv$ipv address set to $ip");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
## nic_gandi_examples
|
## nic_gandi_examples
|
||||||
## by Jimmy Thrasibule <dev@jimmy.lt>
|
## by Jimmy Thrasibule <dev@jimmy.lt>
|
||||||
|
|
47
t/check_value.pl
Normal file
47
t/check_value.pl
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use Test::More;
|
||||||
|
use strict;
|
||||||
|
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
|
||||||
|
eval { require 'ddclient'; } or BAIL_OUT($@);
|
||||||
|
|
||||||
|
my @test_cases = (
|
||||||
|
{
|
||||||
|
type => ddclient::T_FQDN(),
|
||||||
|
input => 'example.com',
|
||||||
|
want => 'example.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => ddclient::T_FQDN(),
|
||||||
|
input => 'example',
|
||||||
|
want => undef,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => ddclient::T_URL(),
|
||||||
|
input => 'https://www.example.com',
|
||||||
|
want => 'https://www.example.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => ddclient::T_URL(),
|
||||||
|
input => 'https://directnic.com/dns/gateway/ad133/',
|
||||||
|
want => 'https://directnic.com/dns/gateway/ad133/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => ddclient::T_URL(),
|
||||||
|
input => 'HTTPS://MixedCase.com/',
|
||||||
|
want => 'HTTPS://MixedCase.com/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => ddclient::T_URL(),
|
||||||
|
input => 'ftp://bad.protocol/',
|
||||||
|
want => undef,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => ddclient::T_URL(),
|
||||||
|
input => 'bad-url',
|
||||||
|
want => undef,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
for my $tc (@test_cases) {
|
||||||
|
my $got = ddclient::check_value($tc->{input}, ddclient::setv($tc->{type}, 0, 0, undef, undef));
|
||||||
|
is($got, $tc->{want}, "$tc->{type}: $tc->{input}");
|
||||||
|
}
|
||||||
|
done_testing();
|
192
t/protocol_directnic.pl
Normal file
192
t/protocol_directnic.pl
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
use Test::More;
|
||||||
|
eval { require JSON::PP; } or plan(skip_all => $@);
|
||||||
|
JSON::PP->import(qw(encode_json));
|
||||||
|
eval { require ddclient::Test::Fake::HTTPD; } or plan(skip_all => $@);
|
||||||
|
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
|
||||||
|
eval { require 'ddclient'; } or BAIL_OUT($@);
|
||||||
|
|
||||||
|
ddclient::load_json_support('directnic');
|
||||||
|
|
||||||
|
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'];
|
||||||
|
if ($req->uri->as_string =~ m/\/dns\/gateway\/(abc|def)\/\?data=([^&]*)/) {
|
||||||
|
return [200, ['Content-Type' => 'application/json'], [encode_json({
|
||||||
|
result => 'success',
|
||||||
|
message => "Your record was updated to $2",
|
||||||
|
})]];
|
||||||
|
} elsif ($req->uri->as_string =~ m/\/dns\/gateway\/bad_token\/\?data=([^&]*)/) {
|
||||||
|
return [200, ['Content-Type' => 'application/json'], [encode_json({
|
||||||
|
result => 'error',
|
||||||
|
message => "There was an error updating your record.",
|
||||||
|
})]];
|
||||||
|
} elsif ($req->uri->as_string =~ m/\/bad\/path\/\?data=([^&]*)/) {
|
||||||
|
return [200, ['Content-Type' => 'application/json'], ['unexpected response body']];
|
||||||
|
}
|
||||||
|
return [400, $headers, ['unexpected request: ' . $req->uri()]]
|
||||||
|
});
|
||||||
|
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 $hostname = $httpd->endpoint();
|
||||||
|
my @test_cases = (
|
||||||
|
{
|
||||||
|
desc => 'IPv4, good',
|
||||||
|
cfg => {h1 => {urlv4 => "$hostname/dns/gateway/abc/", wantipv4 => '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, failed',
|
||||||
|
cfg => {h1 => {urlv4 => "$hostname/dns/gateway/bad_token/", wantipv4 => '192.0.2.1'}},
|
||||||
|
wantstatus => {
|
||||||
|
h1 => {'status-ipv4' => 'failed'},
|
||||||
|
},
|
||||||
|
wantlogs => [
|
||||||
|
{label => 'FAILED', ctx => ['h1'], msg => qr/There was an error updating your record/},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc => 'IPv4, bad',
|
||||||
|
cfg => {h1 => {urlv4 => "$hostname/bad/path/", wantipv4 => '192.0.2.1'}},
|
||||||
|
wantstatus => {
|
||||||
|
h1 => {'status-ipv4' => 'bad'},
|
||||||
|
},
|
||||||
|
wantlogs => [
|
||||||
|
{label => 'FAILED', ctx => ['h1'], msg => qr/response is not a JSON object:\nunexpected response body/},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc => 'IPv4, unexpected response',
|
||||||
|
cfg => {h1 => {urlv4 => "$hostname/unexpected/path/", wantipv4 => '192.0.2.1'}},
|
||||||
|
wantstatus => {h1 => {}},
|
||||||
|
wantlogs => [
|
||||||
|
{label => 'FAILED', ctx => ['h1'], msg => qr/400 Bad Request/},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc => 'IPv4, no urlv4',
|
||||||
|
cfg => {h1 => {wantipv4 => '192.0.2.1'}},
|
||||||
|
wantstatus => {h1 => {}},
|
||||||
|
wantlogs => [
|
||||||
|
{label => 'FAILED', ctx => ['h1'], msg => qr/missing urlv4 option/},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc => 'IPv6, good',
|
||||||
|
cfg => {h1 => {urlv6 => "$hostname/dns/gateway/abc/", wantipv6 => '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, good',
|
||||||
|
cfg => {h1 => {
|
||||||
|
urlv4 => "$hostname/dns/gateway/abc/",
|
||||||
|
urlv6 => "$hostname/dns/gateway/def/",
|
||||||
|
wantipv4 => '192.0.2.1',
|
||||||
|
wantipv6 => '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 => 'IPv4 and IPv6, mixed success',
|
||||||
|
cfg => {h1 => {
|
||||||
|
urlv4 => "$hostname/dns/gateway/bad_token/",
|
||||||
|
urlv6 => "$hostname/dns/gateway/def/",
|
||||||
|
wantipv4 => '192.0.2.1',
|
||||||
|
wantipv6 => '2001:db8::1',
|
||||||
|
}},
|
||||||
|
wantips => {h1 => {wantipv4 => '192.0.2.1', wantipv6 => '2001:db8::1'}},
|
||||||
|
wantstatus => {
|
||||||
|
h1 => {'status-ipv4' => 'failed',
|
||||||
|
'status-ipv6' => 'good', 'ipv6' => '2001:db8::1',
|
||||||
|
'mtime' => $ddclient::now},
|
||||||
|
},
|
||||||
|
wantlogs => [
|
||||||
|
{label => 'FAILED', ctx => ['h1'], msg => qr/There was an error updating your record/},
|
||||||
|
{label => 'SUCCESS', ctx => ['h1'], msg => qr/IPv6/},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
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 = %{$tc->{cfg}};
|
||||||
|
{
|
||||||
|
local $ddclient::_l = $l;
|
||||||
|
ddclient::nic_directnic_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