The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;

use Test::More;
use Test::Identity;
use Test::Refcount;

use t::MockConnection;
use Socket qw( pack_sockaddr_in inet_aton );

use Net::Async::CassandraCQL;
use Protocol::CassandraCQL::Result 0.06;

# Mock the ->_connect_node method
no warnings 'redefine';
my %conns;
local *Net::Async::CassandraCQL::_connect_node = sub {
   my $self = shift;
   my ( $connect_host, $connect_service ) = @_;
   $conns{$connect_host} = my $conn = t::MockConnection->new( $connect_host );
   $self->add_child( $conn );
   return Future->new->done( $conn );
};

my $cass = Net::Async::CassandraCQL->new(
   host => "10.0.0.1",
   primaries => 3,
   prefer_dc => "DC1",
);

my $f = $cass->connect;

ok( my $c = $conns{"10.0.0.1"}, 'Connected to 10.0.0.1' );

# Initial nodelist query
$c->send_nodelist(
   local => { dc => "DC1", rack => "rack1" },
   peers => {
      "10.0.0.2" => { dc => "DC1", rack => "rack1" },
      "10.0.0.3" => { dc => "DC1", rack => "rack1" },
      "10.0.1.1" => { dc => "DC2", rack => "rack1" },
      "10.0.1.2" => { dc => "DC2", rack => "rack1" },
      "10.0.1.3" => { dc => "DC2", rack => "rack1" },
   },
);

$f->get;

ok( defined $conns{"10.0.0.2"}, 'Connected to 10.0.0.2' );
ok( defined $conns{"10.0.0.3"}, 'Connected to 10.0.0.3' );

ok( !defined $conns{"10.0.1.1"} &&
    !defined $conns{"10.0.1.2"} &&
    !defined $conns{"10.0.1.3"}, 'Not connected to DC2 hosts' );

# Only a preference; should still fall back to other DCs if one of these fails

# Fake closure
undef $conns{"10.0.0.3"};
$cass->_closed_node( "10.0.0.3" );

is( defined($conns{"10.0.1.1"}) + defined($conns{"10.0.1.2"}) + defined($conns{"10.0.1.3"}),
    1, 'One of the DC2 hosts now connected after DC1 failure' );

# Find a connection that is registered for events
my $event_c;
$_->is_registered and $event_c = $_, last for @conns{qw( 10.0.0.1 10.0.0.2 10.0.0.3 )};

# 10.0.0.3 is up again, we should now switch to it
# ->close_when_idle needs an IO::Async::Loop for the future
{
   require IO::Async::Loop;
   my $loop = IO::Async::Loop->new;
   $loop->add( $cass );

   $event_c->invoke_event(
      on_status_change => UP => pack_sockaddr_in( 0, inet_aton( "10.0.0.3" ) ),
   );

   ok( defined $conns{"10.0.0.3"}, '10.0.0.3 now connected again' );

   $loop->remove( $cass );
}

done_testing;