App-Netdisco

 view release on metacpan or  search on metacpan

xt/195-arpnip-retire-unvouched.t  view on Meta::CPAN

Dancer::Logger->init('console', $CONFIG);

config->{'device_auth'} = [{driver => 'snmp'}];
config->{'dns'}->{'no'} = ['0.0.0.0/0'];

my (@ran, $txns, @stored_vrfs);

package FakeDbh;
sub new { return bless {}, $_[0] }
sub do  { my ($self, $sql, undef, @binds) = @_; push @ran, [$sql, @binds]; return 3 }
sub selectcol_arrayref {
  my ($self, $sql, undef, @binds) = @_;
  push @ran, [$sql, @binds];
  # as PostgreSQL would apply the named-only predicate
  return [ grep { $sql !~ m/vrf <> ''/ or $_ ne '' } @stored_vrfs ];
}

package FakeSchema;
sub new     { return bless { dbh => FakeDbh->new }, $_[0] }
sub txn_do  { ++$txns; return $_[1]->() }
sub storage { return $_[0] }
sub dbh_do  { my ($self, $code) = @_; return $code->($self, $self->{dbh}) }

package main;

{ no warnings 'redefine'; *App::Netdisco::Util::Node::schema = sub { FakeSchema->new }; }

my $router = '198.51.100.1';

# the unit tests below run with collection on unless they say otherwise
set store_vrfs => 1;
my $now = 'to_timestamp(1.5)::timestamp';

sub device {
  my $device = App::Netdisco::DB->resultset('Device')->new_result({
    ip => $router, name => 'router1', layers => '00000100', snmp_ver => 2,
    snmp_class => 'SNMP::Info::Layer3' });
  $device->in_storage(1);
  return $device;
}

sub with_settings {
  my ($code, %settings) = @_;
  my %saved = map { $_ => setting($_) } keys %settings;
  set($_ => $settings{$_}) for keys %settings;
  my @result = eval { $code->() };
  my $error = $@;
  set($_ => $saved{$_}) for keys %saved;
  die $error if $error;
  return @result;
}

# stored: the VRFs this router's active rows sit in
sub run_retire {
  my ($stored, @args) = @_;
  @ran = (); $txns = 0; @stored_vrfs = @$stored;
  my $count = retire_unvouched_arps(device(), @args);
  return ($count, @ran);
}

sub updates_of { return grep { $_->[0] =~ m/^\s*UPDATE node_ip/ } @_ }
sub update_of { return (updates_of(@_))[0] }

# the named VRFs' UPDATE binds its list; the default table's names it in the SQL
sub retired_vrfs {
  my @updates = updates_of(@_) or return undef;
  my @vrfs = map { ($_->[0] =~ m/ANY/ ? @{ $_->[-1] } : ($_->[0] =~ m/vrf = ''\s*$/ ? '' : ())) } @updates;
  return [ sort @vrfs ];
}

subtest 'retire__switch_on__complete_vrfs_and_uncovered_vrfs_retired' => sub {
  my ($count, @stmts) = run_retire(['', 'gone', 'red'], $now, { '' => 1, red => 1 }, 1);
  is_deeply retired_vrfs(@stmts), ['', 'gone', 'red'],
    'default and red read whole, gone no longer listed';
  is $count, 6, 'returns the rows deactivated by both UPDATEs';
};

subtest 'retire__switch_on__incomplete_vrf_excluded' => sub {
  my (undef, @stmts) = run_retire(['', 'red'], $now, { '' => 1, red => 0 }, 1);
  is_deeply retired_vrfs(@stmts), [''], 'red read short, so red kept';
  my (undef, @none) = run_retire(['', 'red'], $now, { '' => 0, red => 0 }, 1);
  ok !update_of(@none), 'no UPDATE when nothing qualifies, the default table included';
};

subtest 'retire__switch_on__short_vrf_list__uncovered_vrfs_kept' => sub {
  my (undef, @stmts) = run_retire(['', 'gone', 'red'], $now, { '' => 1, red => 1 }, 0);
  is_deeply retired_vrfs(@stmts), ['', 'red'], 'gone may only be missing from a short list';
  my (undef, @absent) = run_retire(['', 'gone'], $now, { '' => 1 });
  is_deeply retired_vrfs(@absent), [''], 'an absent list completeness reads as short';
};

subtest 'retire__switch_on__skipped_vrf__retired_even_with_short_list' => sub {
  my (undef, @by_name) = with_settings(sub {
    run_retire(['', 'blue', 'gone'], $now, { '' => 1 }, 0) }, vrf_no_devicevrfs => { 'group:__ANY__' => 'vrf:blue' });
  is_deeply retired_vrfs(@by_name), ['', 'blue'], 'vrf_no_devicevrfs for every device';
  my (undef, @by_device) = with_settings(sub {
    run_retire(['', 'blue', 'gone'], $now, { '' => 1 }, 0) }, vrf_no_devicevrfs => { $router => 'vrf:gone' });
  is_deeply retired_vrfs(@by_device), ['', 'gone'], 'vrf_no_devicevrfs';
};

sub check_named_only {
  my ($count, @stmts) = @_;
  is scalar @stmts, 2, 'lock and one UPDATE, no VRF query';
  my $update = update_of(@stmts);
  like $update->[0], qr/AND vrf <> ''\s*$/, 'never the default table';
  unlike $update->[0], qr/ANY/, 'no VRF list';
  is_deeply [@$update[1 .. $#$update]], [$router], 'router is the only bind';
  is $count, 3, 'returns the rows deactivated';
}

subtest 'retire__switch_off__named_vrfs_only' => sub {
  check_named_only(with_settings(sub { run_retire(['', 'red'], $now, undef, undef) }, store_vrfs => 0));
};

subtest 'retire__switch_on_device_in_skip_vrf_devices__named_vrfs_only' => sub {
  check_named_only(with_settings(sub { run_retire(['', 'red'], $now, undef, undef) },
    skip_vrf_devices => [$router]));
  my (undef, @stmts) = with_settings(sub { run_retire(['', 'red'], $now, { '' => 1 }, 1) },
    skip_vrf_devices => [$router]);
  like update_of(@stmts)->[0], qr/AND vrf <> ''\s*$/, 'a stale vrf_read does not override the device skip';
};



( run in 1.893 second using v1.01-cache-2.11-cpan-145b269f2f3 )