use strict; use warnings; use 5.14.2; use Data::Dumper; use File::Temp qw[tempdir]; use POSIX qw( strftime ); use Time::Local qw( timelocal_modern ); use Test::Exception; use Test::More; # see done_testing() use Test::Differences; my $t_path; BEGIN { use File::Spec::Functions qw( rel2abs ); use File::Basename qw( dirname ); $t_path = dirname( rel2abs( $0 ) ); } use lib $t_path; use TestUtil qw( RPCAPI ); use Zonemaster::Backend::Config; my $db_backend = TestUtil::db_backend(); my $tempdir = tempdir( CLEANUP => 1 ); my $config = < 'user', api_key => 'key' }; # define the default properties for the tests my $params = { client_id => 'Unit Test', client_version => '1.0', ipv4 => JSON::PP::true, ipv6 => JSON::PP::true, profile => 'test_profile', }; # Create Zonemaster::Backend::RPCAPI object sub init_backend { my ( $config ) = @_; my $rpcapi = TestUtil::create_rpcapi( $config ); # create a user $rpcapi->add_api_user( $user ); return $rpcapi; } sub to_timestamp { my ( $date ) = @_; my ( $year, $month, $day, $hour, $min, $sec ) = split( /[\s:-]+/, $date ); my $time = timelocal_modern( $sec, $min, $hour, $day, $month-1, $year ); return $time; } sub check_tolerance { my ( $ref_time, $msg ) = @_; my $current_time = strftime "%Y-%m-%d %H:%M:%S", gmtime( time() ); my $delta = abs( to_timestamp($current_time) - to_timestamp($ref_time) ); my $tolerance = 60; # 1 minute is tolerable between ret_time and current_time cmp_ok( $delta, '<=', $tolerance, $msg); } subtest 'RPCAPI add_batch_job' => sub { my $config = Zonemaster::Backend::Config->parse( $config ); my $rpcapi = init_backend( $config ); my $dbh = $rpcapi->{db}->dbh; my @domains = ( 'afnic.fr' ); my $res = $rpcapi->add_batch_job( { %$user, domains => \@domains, test_params => $params } ); is( $res, 1, 'correct batch job id returned' ); subtest 'table "batch_jobs" contains an entry' => sub { my ( $count ) = $dbh->selectrow_array( q[ SELECT count(*) FROM batch_jobs ] ); is( $count, 1, 'one row in table' ); my ( $id, $username, $created_at ) = $dbh->selectrow_array( q[ SELECT * FROM batch_jobs ]); is( $id, 1, 'first batch id is 1' ); is( $username, $user->{username}, 'correct batch user' ); ok( $created_at, 'defined creation time' ); check_tolerance( $created_at, 'creation time in tolerance zone' ); }; subtest 'table "test_results" contains an entry' => sub { my ( $count ) = $dbh->selectrow_array( q[ SELECT count(*) FROM test_results ] ); is( $count, 1, 'one row in table' ); my ( $hash_id, $domain, $batch_id, $created_at, $started_at, $ended_at, $params ) = $dbh->selectrow_array( q[ SELECT hash_id, domain, batch_id, created_at, started_at, ended_at, params FROM test_results ] ); is( length($hash_id), 16, 'correct hash_id length' ); is( $domain, $domains[0], 'correct domain' ); is( $batch_id, 1, 'correct batch_id' ); ok( $created_at, 'defined creation time' ); check_tolerance( $created_at, 'creation time in tolerance zone' ); ok( ! defined $started_at, 'undefined start time' ); ok( ! defined $ended_at, 'undefined end time' ); }; }; subtest 'RPCAPI batch_status' => sub { my $config = Zonemaster::Backend::Config->parse( $config ); my $rpcapi = init_backend( $config ); subtest 'batch job exists' => sub { my @domains = ( 'afnic.fr' ); my $batch_id = $rpcapi->add_batch_job( { %$user, domains => \@domains, test_params => $params } ); is( $batch_id, 1, 'correct batch job id returned' ); my $res = $rpcapi->batch_status( { batch_id => $batch_id } ); is( $res->{waiting_count}, scalar @domains, 'correct number of runninng tests' ); is( $res->{running_count}, 0, 'correct number of finished tests' ); is( $res->{finished_count}, 0, 'correct number of finished tests' ); ok( !exists $res->{waiting_tests}, 'list of waiting tests expected to be absent' ); ok( !exists $res->{running_tests}, 'list of running tests expected to be absent' ); ok( !exists $res->{finished_tests}, 'list of finished tests to be absent' ); }; subtest 'unknown batch (batch_status)' => sub { my $unknown_batch = 10; dies_ok { $rpcapi->batch_status( { batch_id => $unknown_batch } ); } 'getting results for an unknown batch_id should die'; my $res = $@; is( $res->{error}, 'Zonemaster::Backend::Error::ResourceNotFound', 'correct error type' ); is( $res->{message}, 'Unknown batch', 'correct error message' ); is( $res->{data}->{batch_id}, $unknown_batch, 'correct data type returned' ); }; }; subtest 'batch with several domains' => sub { my $config = Zonemaster::Backend::Config->parse( $config ); my $rpcapi = init_backend( $config ); my $dbh = $rpcapi->{db}->dbh; my @domains = sort( 'afnic.fr', 'iis.se' ); my $res = $rpcapi->add_batch_job( { %$user, domains => \@domains, test_params => $params } ); is( $res, 1, 'correct batch job id returned' ); # No lists of test IDs requested $res = $rpcapi->batch_status( { batch_id => 1 } ); is( $res->{waiting_count}, scalar @domains, 'correct number of running tests' ); is( $res->{running_count}, 0, 'correct number of finished tests' ); is( $res->{finished_count}, 0, 'correct number of finished tests' ); ok( !exists $res->{waiting_tests}, 'list of waiting tests expected to be absent' ); ok( !exists $res->{running_tests}, 'list of running tests expected to be absent' ); ok( !exists $res->{finished_tests}, 'list of finished tests expected to be absent' ); # List of waiting test IDs requested $res = $rpcapi->batch_status( { batch_id => 1, list_waiting_tests => 1 } ); is( $res->{waiting_count}, scalar @domains, 'correct number of runninng tests' ); is( $res->{running_count}, 0, 'correct number of finished tests' ); is( $res->{finished_count}, 0, 'correct number of finished tests' ); is( scalar @{ $res->{waiting_tests} }, scalar @domains, 'correct number of elements in waiting_tests' ); ok( !exists $res->{running_tests}, 'list of running tests expected to be absent' ); ok( !exists $res->{finished_tests}, 'list of finished tests expected to be absent' ); subtest 'table "test_results" contains 2 entries' => sub { my ( $count ) = $dbh->selectrow_array( q[ SELECT count(*) FROM test_results ] ); is( $count, @domains, 'two rows in table' ); my $rows = $dbh->selectall_hashref( q[ SELECT hash_id, domain, batch_id, created_at, started_at, ended_at, params FROM test_results ], 'domain' ); my @keys = sort keys %$rows; is_deeply( \@keys, \@domains, 'correct domains' ); foreach my $domain ( @keys ) { is( length($rows->{$domain}->{hash_id}), 16, "[$domain] correct hash_id length" ); is( $rows->{$domain}->{batch_id}, 1, "[$domain] correct batch_id" ); ok( $rows->{$domain}->{created_at}, "[$domain] defined creation time" ); check_tolerance( $rows->{$domain}->{created_at}, "[$domain] creation time in tolerance zone" ); ok( ! defined $rows->{$domain}->{started_at}, "[$domain] undefined start time" ); ok( ! defined $rows->{$domain}->{ended_at}, "[$domain] undefined end time" ); } }; }; subtest 'batch job still running' => sub { my $config = Zonemaster::Backend::Config->parse( $config ); my $rpcapi = init_backend( $config ); my $dbh = $rpcapi->{db}->dbh; my @domains = ( 'afnic.fr' ); my $batch_id = $rpcapi->add_batch_job( { %$user, domains => \@domains, test_params => $params } ); is( $batch_id, 1, 'correct batch job id returned' ); subtest 'a batch is already running for the user, new batch creation should not fail' => sub { my $batch_id = $rpcapi->add_batch_job( { %$user, domains => \@domains, test_params => $params } ); is( $batch_id, 2, 'same user can create another batch' ); }; subtest 'use another user' => sub { my $another_user = { username => 'another', api_key => 'token' }; $rpcapi->add_api_user( $another_user ); my $batch_id = $rpcapi->add_batch_job( { %$another_user, domains => \@domains, test_params => $params } ); is( $batch_id, 3, 'another_user can create another batch' ); }; }; subtest 'duplicate user should fail' => sub { my $config = Zonemaster::Backend::Config->parse( $config ); my $rpcapi = init_backend( $config ); # do not output any error message my $printerror_before = $rpcapi->{db}->dbh->{PrintError}; $rpcapi->{db}->dbh->{PrintError} = 0; dies_ok { $rpcapi->add_api_user( { username => $user->{username}, api_key => "another api key" } ); } 'a user with the same username already exists, add_api_user should die'; my $res = $@; is( $res->{error}, 'Zonemaster::Backend::Error::Conflict', 'correct error type' ); is( $res->{message}, 'User already exists', 'correct error message' ); is( $res->{data}->{username}, $user->{username}, 'correct data type returned' ); # reset attribute value $rpcapi->{db}->dbh->{PrintError} = $printerror_before; }; subtest 'normalize "domain" column' => sub { my $config = Zonemaster::Backend::Config->parse( $config ); my $rpcapi = init_backend( $config ); my $dbh = $rpcapi->{db}->dbh; my %domains_to_test = ( "aFnIc.Fr" => "afnic.fr", "afnic.fr." => "afnic.fr", "aFnic.Fr." => "afnic.fr" ); my @domains = keys %domains_to_test; my $batch_id = $rpcapi->add_batch_job( { %$user, domains => \@domains, test_params => $params } ); my @db_domain = map { $$_[0] } $dbh->selectall_array( "SELECT domain FROM test_results WHERE batch_id=?", undef, $batch_id ); is( @db_domain, 3, '3 tests created' ); my @expected = values %domains_to_test; is_deeply( \@db_domain, \@expected, 'domains are normalized' ); }; # TODO: create an agent and run batch tests ## Create the agent #use_ok( 'Zonemaster::Backend::TestAgent' ); #my $agent = Zonemaster::Backend::TestAgent->new( { dbtype => "$db_backend", config => $config } ); #isa_ok($agent, 'Zonemaster::Backend::TestAgent', 'agent'); done_testing();