tests: update_nics: Support multiple hosts
This will be used in a future commit to test deduplication of `use*` strategies.
This commit is contained in:
parent
f32f7fc29a
commit
b6ac0e6d05
1 changed files with 97 additions and 75 deletions
172
t/update_nics.pl
172
t/update_nics.pl
|
@ -1,6 +1,7 @@
|
||||||
use Test::More;
|
use Test::More;
|
||||||
use File::Temp;
|
use File::Temp;
|
||||||
use List::Util qw(max);
|
use List::Util qw(max);
|
||||||
|
use Scalar::Util qw(refaddr);
|
||||||
eval { require ddclient::Test::Fake::HTTPD; } or plan(skip_all => $@);
|
eval { require ddclient::Test::Fake::HTTPD; } or plan(skip_all => $@);
|
||||||
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
|
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
|
||||||
eval { require 'ddclient'; } or BAIL_OUT($@);
|
eval { require 'ddclient'; } or BAIL_OUT($@);
|
||||||
|
@ -39,6 +40,28 @@ local %ddclient::builtinweb = (
|
||||||
defined($httpd{'6'}) ? (v6 => {url => "" . $httpd{'6'}->endpoint()}) : (),
|
defined($httpd{'6'}) ? (v6 => {url => "" . $httpd{'6'}->endpoint()}) : (),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# Sentinel value used by `mergecfg` that means "this hash entry should be deleted if it exists."
|
||||||
|
my $DOES_NOT_EXIST = [];
|
||||||
|
|
||||||
|
sub mergecfg {
|
||||||
|
my %ret;
|
||||||
|
for my $cfg (@_) {
|
||||||
|
next if !defined($cfg);
|
||||||
|
for my $h (keys(%$cfg)) {
|
||||||
|
if (refaddr($cfg->{$h}) == refaddr($DOES_NOT_EXIST)) {
|
||||||
|
delete($ret{$h});
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
$ret{$h} = {%{$ret{$h} // {}}, %{$cfg->{$h}}};
|
||||||
|
for my $k (keys(%{$ret{$h}})) {
|
||||||
|
my $a = refaddr($ret{$h}{$k});
|
||||||
|
delete($ret{$h}{$k}) if defined($a) && $a == refaddr($DOES_NOT_EXIST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return \%ret;
|
||||||
|
}
|
||||||
|
|
||||||
local $ddclient::globals{debug} = 1;
|
local $ddclient::globals{debug} = 1;
|
||||||
local $ddclient::globals{verbose} = 1;
|
local $ddclient::globals{verbose} = 1;
|
||||||
local $ddclient::now = 1000;
|
local $ddclient::now = 1000;
|
||||||
|
@ -70,82 +93,82 @@ my @test_cases = (
|
||||||
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
||||||
{
|
{
|
||||||
desc => "legacy, fresh, $desc",
|
desc => "legacy, fresh, $desc",
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
%cfg,
|
%cfg,
|
||||||
},
|
}},
|
||||||
want_update => 1,
|
want_updates => [['host']],
|
||||||
want_recap_changes => {
|
want_recap_changes => {host => {
|
||||||
'atime' => $ddclient::now,
|
'atime' => $ddclient::now,
|
||||||
'ipv4' => '192.0.2.1',
|
'ipv4' => '192.0.2.1',
|
||||||
'mtime' => $ddclient::now,
|
'mtime' => $ddclient::now,
|
||||||
'status-ipv4' => 'good',
|
'status-ipv4' => 'good',
|
||||||
},
|
}},
|
||||||
%$_,
|
%$_,
|
||||||
};
|
};
|
||||||
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
||||||
{
|
{
|
||||||
desc => 'legacy, fresh, use=web (IPv6)',
|
desc => 'legacy, fresh, use=web (IPv6)',
|
||||||
ipv6 => 1,
|
ipv6 => 1,
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
'use' => 'web',
|
'use' => 'web',
|
||||||
'web' => 'v6',
|
'web' => 'v6',
|
||||||
},
|
}},
|
||||||
want_update => 1,
|
want_updates => [['host']],
|
||||||
want_recap_changes => {
|
want_recap_changes => {host => {
|
||||||
'atime' => $ddclient::now,
|
'atime' => $ddclient::now,
|
||||||
'ipv6' => '2001:db8::1',
|
'ipv6' => '2001:db8::1',
|
||||||
'mtime' => $ddclient::now,
|
'mtime' => $ddclient::now,
|
||||||
'status-ipv6' => 'good',
|
'status-ipv6' => 'good',
|
||||||
},
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc => 'legacy, fresh, usev6=webv6',
|
desc => 'legacy, fresh, usev6=webv6',
|
||||||
ipv6 => 1,
|
ipv6 => 1,
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
'usev6' => 'webv6',
|
'usev6' => 'webv6',
|
||||||
},
|
}},
|
||||||
want_update => 1,
|
want_updates => [['host']],
|
||||||
want_recap_changes => {
|
want_recap_changes => {host => {
|
||||||
'atime' => $ddclient::now,
|
'atime' => $ddclient::now,
|
||||||
'ipv6' => '2001:db8::1',
|
'ipv6' => '2001:db8::1',
|
||||||
'mtime' => $ddclient::now,
|
'mtime' => $ddclient::now,
|
||||||
'status-ipv6' => 'good',
|
'status-ipv6' => 'good',
|
||||||
},
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc => 'legacy, fresh, usev4=webv4 usev6=webv6',
|
desc => 'legacy, fresh, usev4=webv4 usev6=webv6',
|
||||||
ipv6 => 1,
|
ipv6 => 1,
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
'usev4' => 'webv4',
|
'usev4' => 'webv4',
|
||||||
'usev6' => 'webv6',
|
'usev6' => 'webv6',
|
||||||
},
|
}},
|
||||||
want_update => 1,
|
want_updates => [['host']],
|
||||||
want_recap_changes => {
|
want_recap_changes => {host => {
|
||||||
'atime' => $ddclient::now,
|
'atime' => $ddclient::now,
|
||||||
'ipv4' => '192.0.2.1',
|
'ipv4' => '192.0.2.1',
|
||||||
'mtime' => $ddclient::now,
|
'mtime' => $ddclient::now,
|
||||||
'status-ipv4' => 'good',
|
'status-ipv4' => 'good',
|
||||||
},
|
}},
|
||||||
},
|
},
|
||||||
map({
|
map({
|
||||||
my %cfg = %{delete($_->{cfg})};
|
my %cfg = %{delete($_->{cfg})};
|
||||||
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
||||||
{
|
{
|
||||||
desc => "legacy, no change, not yet time, $desc",
|
desc => "legacy, no change, not yet time, $desc",
|
||||||
recap => {
|
recap => {host => {
|
||||||
'atime' => $ddclient::now - ddclient::opt('min-interval'),
|
'atime' => $ddclient::now - ddclient::opt('min-interval'),
|
||||||
'ipv4' => '192.0.2.1',
|
'ipv4' => '192.0.2.1',
|
||||||
'mtime' => $ddclient::now - ddclient::opt('min-interval'),
|
'mtime' => $ddclient::now - ddclient::opt('min-interval'),
|
||||||
'status-ipv4' => 'good',
|
'status-ipv4' => 'good',
|
||||||
},
|
}},
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
%cfg,
|
%cfg,
|
||||||
},
|
}},
|
||||||
%$_,
|
%$_,
|
||||||
};
|
};
|
||||||
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
||||||
|
@ -154,16 +177,16 @@ my @test_cases = (
|
||||||
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
||||||
{
|
{
|
||||||
desc => "legacy, min-interval elapsed but no change, $desc",
|
desc => "legacy, min-interval elapsed but no change, $desc",
|
||||||
recap => {
|
recap => {host => {
|
||||||
'atime' => $ddclient::now - ddclient::opt('min-interval') - 1,
|
'atime' => $ddclient::now - ddclient::opt('min-interval') - 1,
|
||||||
'ipv4' => '192.0.2.1',
|
'ipv4' => '192.0.2.1',
|
||||||
'mtime' => $ddclient::now - ddclient::opt('min-interval') - 1,
|
'mtime' => $ddclient::now - ddclient::opt('min-interval') - 1,
|
||||||
'status-ipv4' => 'good',
|
'status-ipv4' => 'good',
|
||||||
},
|
}},
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
%cfg,
|
%cfg,
|
||||||
},
|
}},
|
||||||
%$_,
|
%$_,
|
||||||
};
|
};
|
||||||
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
||||||
|
@ -172,19 +195,19 @@ my @test_cases = (
|
||||||
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
||||||
{
|
{
|
||||||
desc => "legacy, needs update, not yet time, $desc",
|
desc => "legacy, needs update, not yet time, $desc",
|
||||||
recap => {
|
recap => {host => {
|
||||||
'atime' => $ddclient::now - ddclient::opt('min-interval'),
|
'atime' => $ddclient::now - ddclient::opt('min-interval'),
|
||||||
'ipv4' => '192.0.2.2',
|
'ipv4' => '192.0.2.2',
|
||||||
'mtime' => $ddclient::now - ddclient::opt('min-interval'),
|
'mtime' => $ddclient::now - ddclient::opt('min-interval'),
|
||||||
'status-ipv4' => 'good',
|
'status-ipv4' => 'good',
|
||||||
},
|
}},
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
%cfg,
|
%cfg,
|
||||||
},
|
}},
|
||||||
want_recap_changes => {
|
want_recap_changes => {host => {
|
||||||
'warned-min-interval' => $ddclient::now,
|
'warned-min-interval' => $ddclient::now,
|
||||||
},
|
}},
|
||||||
%$_,
|
%$_,
|
||||||
};
|
};
|
||||||
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
||||||
|
@ -193,22 +216,22 @@ my @test_cases = (
|
||||||
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
||||||
{
|
{
|
||||||
desc => "legacy, min-interval elapsed, needs update, $desc",
|
desc => "legacy, min-interval elapsed, needs update, $desc",
|
||||||
recap => {
|
recap => {host => {
|
||||||
'atime' => $ddclient::now - ddclient::opt('min-interval') - 1,
|
'atime' => $ddclient::now - ddclient::opt('min-interval') - 1,
|
||||||
'ipv4' => '192.0.2.2',
|
'ipv4' => '192.0.2.2',
|
||||||
'mtime' => $ddclient::now - ddclient::opt('min-interval') - 1,
|
'mtime' => $ddclient::now - ddclient::opt('min-interval') - 1,
|
||||||
'status-ipv4' => 'good',
|
'status-ipv4' => 'good',
|
||||||
},
|
}},
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
%cfg,
|
%cfg,
|
||||||
},
|
}},
|
||||||
want_update => 1,
|
want_updates => [['host']],
|
||||||
want_recap_changes => {
|
want_recap_changes => {host => {
|
||||||
'atime' => $ddclient::now,
|
'atime' => $ddclient::now,
|
||||||
'ipv4' => '192.0.2.1',
|
'ipv4' => '192.0.2.1',
|
||||||
'mtime' => $ddclient::now,
|
'mtime' => $ddclient::now,
|
||||||
},
|
}},
|
||||||
%$_,
|
%$_,
|
||||||
};
|
};
|
||||||
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
||||||
|
@ -217,20 +240,20 @@ my @test_cases = (
|
||||||
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
||||||
{
|
{
|
||||||
desc => "legacy, previous failed update, not yet time to retry, $desc",
|
desc => "legacy, previous failed update, not yet time to retry, $desc",
|
||||||
recap => {
|
recap => {host => {
|
||||||
'atime' => $ddclient::now - ddclient::opt('min-error-interval'),
|
'atime' => $ddclient::now - ddclient::opt('min-error-interval'),
|
||||||
'ipv4' => '192.0.2.2',
|
'ipv4' => '192.0.2.2',
|
||||||
'mtime' => $ddclient::now - max(ddclient::opt('min-error-interval'),
|
'mtime' => $ddclient::now - max(ddclient::opt('min-error-interval'),
|
||||||
ddclient::opt('min-interval')) - 1,
|
ddclient::opt('min-interval')) - 1,
|
||||||
'status-ipv4' => 'failed',
|
'status-ipv4' => 'failed',
|
||||||
},
|
}},
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
%cfg,
|
%cfg,
|
||||||
},
|
}},
|
||||||
want_recap_changes => {
|
want_recap_changes => {host => {
|
||||||
'warned-min-error-interval' => $ddclient::now,
|
'warned-min-error-interval' => $ddclient::now,
|
||||||
},
|
}},
|
||||||
%$_,
|
%$_,
|
||||||
};
|
};
|
||||||
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
||||||
|
@ -239,23 +262,23 @@ my @test_cases = (
|
||||||
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
my $desc = join(' ', map("$_=$cfg{$_}", keys(%cfg)));
|
||||||
{
|
{
|
||||||
desc => "legacy, previous failed update, time to retry, $desc",
|
desc => "legacy, previous failed update, time to retry, $desc",
|
||||||
recap => {
|
recap => {host => {
|
||||||
'atime' => $ddclient::now - ddclient::opt('min-error-interval') - 1,
|
'atime' => $ddclient::now - ddclient::opt('min-error-interval') - 1,
|
||||||
'ipv4' => '192.0.2.2',
|
'ipv4' => '192.0.2.2',
|
||||||
'mtime' => $ddclient::now - ddclient::opt('min-error-interval') - 2,
|
'mtime' => $ddclient::now - ddclient::opt('min-error-interval') - 2,
|
||||||
'status-ipv4' => 'failed',
|
'status-ipv4' => 'failed',
|
||||||
},
|
}},
|
||||||
cfg => {
|
cfg => {host => {
|
||||||
'protocol' => 'legacy',
|
'protocol' => 'legacy',
|
||||||
%cfg,
|
%cfg,
|
||||||
},
|
}},
|
||||||
want_update => 1,
|
want_updates => [['host']],
|
||||||
want_recap_changes => {
|
want_recap_changes => {host => {
|
||||||
'atime' => $ddclient::now,
|
'atime' => $ddclient::now,
|
||||||
'ipv4' => '192.0.2.1',
|
'ipv4' => '192.0.2.1',
|
||||||
'mtime' => $ddclient::now,
|
'mtime' => $ddclient::now,
|
||||||
'status-ipv4' => 'good',
|
'status-ipv4' => 'good',
|
||||||
},
|
}},
|
||||||
%$_,
|
%$_,
|
||||||
};
|
};
|
||||||
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
} {cfg => {use => 'web'}}, {cfg => {usev4 => 'webv4'}}),
|
||||||
|
@ -268,42 +291,41 @@ for my $tc (@test_cases) {
|
||||||
if $tc->{ipv6} && !$http_daemon_supports_ipv6;
|
if $tc->{ipv6} && !$http_daemon_supports_ipv6;
|
||||||
subtest($tc->{desc} => sub {
|
subtest($tc->{desc} => sub {
|
||||||
local $ddclient::_l = ddclient::pushlogctx($tc->{desc});
|
local $ddclient::_l = ddclient::pushlogctx($tc->{desc});
|
||||||
# Copy %{$tc->{recap}} so that updates to $recap{$h} don't update %{$tc->{recap}}.
|
$tc->{recap}{$_}{host} //= $_ for keys(%{$tc->{recap} // {}});
|
||||||
local %ddclient::recap = (host => {%{$tc->{recap} // {}}});
|
# Deep copy `%{$tc->{recap}}` so that updates to `%ddclient::recap` don't mutate it.
|
||||||
|
local %ddclient::recap = %{mergecfg($tc->{recap})};
|
||||||
my $cachef = File::Temp->new();
|
my $cachef = File::Temp->new();
|
||||||
# $cachef is an object that stringifies to a filename.
|
# $cachef is an object that stringifies to a filename.
|
||||||
local $ddclient::globals{cache} = "$cachef";
|
local $ddclient::globals{cache} = "$cachef";
|
||||||
my %cfg = (
|
$tc->{cfg} = {map({
|
||||||
web => 'v4',
|
($_ => {
|
||||||
webv4 => 'v4',
|
host => $_,
|
||||||
webv6 => 'v6',
|
web => 'v4',
|
||||||
%{$tc->{cfg} // {}},
|
webv4 => 'v4',
|
||||||
);
|
webv6 => 'v6',
|
||||||
# Copy %cfg so that updates to $config{$h} don't update %cfg.
|
%{$tc->{cfg}{$_}},
|
||||||
local %ddclient::config = (host => {%cfg});
|
});
|
||||||
|
} keys(%{$tc->{cfg} // {}}))};
|
||||||
|
# Deep copy `%{$tc->{cfg}}` so that updates to `%ddclient::config` don't mutate it.
|
||||||
|
local %ddclient::config = %{mergecfg($tc->{cfg})};
|
||||||
local @updates;
|
local @updates;
|
||||||
|
|
||||||
ddclient::update_nics();
|
ddclient::update_nics();
|
||||||
|
|
||||||
TODO: {
|
TODO: {
|
||||||
local $TODO = $tc->{want_update_TODO};
|
local $TODO = $tc->{want_updates_TODO};
|
||||||
is_deeply(\@updates, [(['host']) x ($tc->{want_update} ? 1 : 0)],
|
is_deeply(\@updates, $tc->{want_updates} // [], 'got expected updates')
|
||||||
'got expected update');
|
or diag(ddclient::repr(Values => [\@updates, $tc->{want_updates}],
|
||||||
|
Names => ['*got', '*want']));
|
||||||
}
|
}
|
||||||
my %want_recap = (host => {
|
my %want_recap = %{mergecfg($tc->{recap}, $tc->{want_recap_changes})};
|
||||||
%{$tc->{recap} // {}},
|
|
||||||
%{$tc->{want_recap_changes} // {}},
|
|
||||||
});
|
|
||||||
TODO: {
|
TODO: {
|
||||||
local $TODO = $tc->{want_recap_changes_TODO};
|
local $TODO = $tc->{want_recap_changes_TODO};
|
||||||
is_deeply(\%ddclient::recap, \%want_recap, 'recap matches')
|
is_deeply(\%ddclient::recap, \%want_recap, 'recap matches')
|
||||||
or diag(ddclient::repr(Values => [\%ddclient::recap, \%want_recap],
|
or diag(ddclient::repr(Values => [\%ddclient::recap, \%want_recap],
|
||||||
Names => ['*got', '*want']));
|
Names => ['*got', '*want']));
|
||||||
}
|
}
|
||||||
my %want_cfg = (host => {
|
my %want_cfg = %{mergecfg($tc->{cfg}, $tc->{want_cfg_changes})};
|
||||||
%cfg,
|
|
||||||
%{$tc->{want_cfg_changes} // {}},
|
|
||||||
});
|
|
||||||
TODO: {
|
TODO: {
|
||||||
local $TODO = $tc->{want_cfg_changes_TODO};
|
local $TODO = $tc->{want_cfg_changes_TODO};
|
||||||
is_deeply(\%ddclient::config, \%want_cfg, 'config matches')
|
is_deeply(\%ddclient::config, \%want_cfg, 'config matches')
|
||||||
|
|
Loading…
Reference in a new issue