Zonemaster-Backend/t/test01.t
use strict;
use warnings;
use 5.14.2;
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 TestAgent );
use Data::Dumper;
use File::Temp qw[tempdir];
use Test::Exception;
use Test::More; # see done_testing()
use Zonemaster::Engine;
use Zonemaster::Backend::Config;
my $db_backend = TestUtil::db_backend();
my $datafile = "$t_path/test01.data";
TestUtil::restore_datafile( $datafile );
my $tempdir = tempdir( CLEANUP => 1 );
my $config = Zonemaster::Backend::Config->parse( <<EOF );
[DB]
engine = $db_backend
[MYSQL]
host = localhost
user = travis_zm
password = travis_zonemaster
database = travis_zonemaster
[POSTGRESQL]
host = localhost
user = travis_zonemaster
password = travis_zonemaster
database = travis_zonemaster
[SQLITE]
database_file = $tempdir/zonemaster.sqlite
[LANGUAGE]
locale = en_US
[PUBLIC PROFILES]
test_profile=$t_path/test_profile.json
EOF
my $rpcapi = TestUtil::create_rpcapi( $config );
my $dbh = $rpcapi->{db}->dbh;
# Create the agent
my $agent = TestUtil::create_testagent( $config );
# define the default properties for the tests
my $params = {
client_id => 'Unit Test',
client_version => '1.0',
domain => 'afnic.fr',
ipv4 => JSON::PP::true,
ipv6 => JSON::PP::true,
profile => 'test_profile',
nameservers => [
{ ns => 'ns1.nic.fr' },
{ ns => 'ns2.nic.fr', ip => '192.134.4.1' }
],
ds_info => [
{
keytag => 11627,
algorithm => 8,
digtype => 2,
digest => 'a6cca9e6027ecc80ba0f6d747923127f1d69005fe4f0ec0461bd633482595448'
}
]
};
my $hash_id;
# This is the first test added to the DB, its 'id' is 1
my $test_id = 1;
subtest 'add a first test' => sub {
$hash_id = $rpcapi->start_domain_test( $params );
ok( $hash_id, "API start_domain_test OK" );
is( length($hash_id), 16, "Test has a 16 characters length hash ID (hash_id=$hash_id)" );
my ( $test_id_db, $hash_id_db ) = $dbh->selectrow_array( "SELECT id, hash_id FROM test_results WHERE id=?", undef, $test_id );
is( $test_id_db, $test_id , 'API start_domain_test -> Test inserted in the DB' );
is( $hash_id_db, $hash_id , 'Correct hash_id in database' );
# test test_progress API
my $progress = $rpcapi->test_progress( { test_id => $hash_id } );
is( $progress, 0 , 'Test has been created, its progress is 0' );
};
subtest 'get and run test' => sub {
my ( $hash_id_from_db ) = $rpcapi->{db}->get_test_request();
is( $hash_id_from_db, $hash_id, 'Get correct test to run' );
my $progress = $rpcapi->test_progress( { test_id => $hash_id } );
is( $progress, 1, 'Test has been picked, its progress is 1' );
diag "running the agent on test $hash_id";
$agent->run( $hash_id ); # blocking call
$progress = $rpcapi->test_progress( { test_id => $hash_id } );
is( $progress, 100 , 'Test has finished, its progress is 100' );
};
subtest 'API calls' => sub {
subtest 'get_test_results' => sub {
local $@ = undef;
my $res = eval { $rpcapi->get_test_results( { id => $hash_id, language => 'en' } ) };
if ( $@ ) {
fail 'Crashed while fetching job results: ' . Dumper( $@ );
}
ok( ! defined $res->{id}, 'Do not expose primary key' );
is( $res->{hash_id}, $hash_id, 'Retrieve the correct "hash_id"' );
ok( defined $res->{params}, 'Value "params" properly defined' );
ok( ! exists $res->{creation_time}, 'Key "creation_time" should be missing' );
ok( defined $res->{created_at}, 'Value "created_at" properly defined' );
ok( defined $res->{results}, 'Value "results" properly defined' );
if ( @{ $res->{results} } > 1 ) {
pass 'The test has some results';
}
else {
fail 'The test has some results: ' . Dumper( $res->{results} );
}
};
subtest 'get_test_params' => sub {
my $res = $rpcapi->get_test_params( { test_id => $hash_id } );
is( $res->{domain}, $params->{domain}, 'Retrieve the correct "domain" value' );
is( $res->{profile}, $params->{profile}, 'Retrieve the correct "profile" value' );
is( $res->{client_id}, $params->{client_id}, 'Retrieve the correct "client_id" value' );
is( $res->{client_version}, $params->{client_version}, 'Retrieve the correct "client_version" value' );
is( $res->{ipv4}, $params->{ipv4}, 'Retrieve the correct "ipv4" value' );
is( $res->{ipv6}, $params->{ipv6}, 'Retrieve the correct "ipv6" value' );
is_deeply( $res->{nameservers}, $params->{nameservers}, 'Retrieve the correct "nameservers" value' );
is_deeply( $res->{ds_info}, $params->{ds_info}, 'Retrieve the correct "ds_info" value' );
};
subtest 'add_api_user' => sub {
my $res;
eval {
$res = $rpcapi->add_api_user( { username => "zonemaster_test", api_key => "zonemaster_test's api key" } );
};
is( $res, 1, 'API add_api_user success');
my $user_check_query = q/SELECT * FROM users WHERE username = 'zonemaster_test'/;
is( scalar( $dbh->selectrow_array( $user_check_query ) ), 1 ,'API add_api_user user created' );
};
subtest 'version_info' => sub {
my $res = $rpcapi->version_info();
ok( defined( $res->{zonemaster_ldns} ), 'Has a "zonemaster_ldns" key' );
ok( defined( $res->{zonemaster_engine} ), 'Has a "zonemaster_engine" key' );
ok( defined( $res->{zonemaster_backend} ), 'Has a "zonemaster_backend" key' );
};
subtest 'profile_names' => sub {
my $res = $rpcapi->profile_names();
is( scalar( @$res ), 2, 'There are exactly 2 public profiles' );
ok( grep( /default/, @$res ), 'The profile "default" is defined' );
ok( grep( /test_profile/, @$res ), 'The profile "test_profile" is defined' );
};
subtest 'get_data_from_parent_zone' => sub {
my $res = $rpcapi->get_data_from_parent_zone( { domain => "fr" } );
note explain( $res );
ok( defined( $res->{ns_list} ), 'Has a list of nameservers' );
ok( defined( $res->{ds_list} ), 'Has a list of DS records' );
my @ns_list = map { $_->{ns} } @{ $res->{ns_list} };
ok( grep( /d\.nic\.fr/, @ns_list ), 'Has "d.nic.fr" nameserver' );
ok( grep( /e\.ext\.nic\.fr/, @ns_list ), 'Has "e.ext.nic.fr" nameserver' );
ok( grep( /f\.ext\.nic\.fr/, @ns_list ), 'Has "f.ext.nic.fr" nameserver' );
ok( grep( /g\.ext\.nic\.fr/, @ns_list ), 'Has "g.ext.nic.fr" nameserver' );
my @ip_list = map { $_->{ip} } @{ $res->{ns_list} };
ok( grep( /194\.0\.9\.1/, @ip_list ), 'Has "194.0.9.1" ip' ); # d.nic.fr
ok( grep( /2001:678:c::1/, @ip_list ), 'Has "2001:678:c::1" ip' );
ok( grep( /194\.0\.36\.1/, @ip_list ), 'Has "194.0.36.1" ip' ); # g.ext.nic.fr
ok( grep( /2001:678:4c::1/, @ip_list ), 'Has "2001:678:4c::1" ip' );
ok( grep( /193\.176\.144\.22/, @ip_list ), 'Has "193.176.144.22" ip' ); # e.ext.nic.fr
ok( grep( /2a00:d78:0:102:193:176:144:22/, @ip_list ), 'Has "2a00:d78:0:102:193:176:144:22" ip' );
ok( grep( /194\.146\.106\.46/, @ip_list ), 'Has "194.146.106.46" ip' ); # f.ext.nic.fr
ok( grep( /2001:67c:1010:11::53/, @ip_list ), 'Has "2001:67c:1010:11::53" ip' );
my $ds_value = {
'algorithm' => 13,
'digest' => '1b3386864d30ccc8f4541b985bf2ca320e4f52c57c53353f6d29c9ad58a5671f',
'digtype' => 2,
'keytag' => 51508
};
is( scalar( @{ $res->{ds_list} } ), 1, 'Has only one DS set' );
is_deeply( $res->{ds_list}[0], $ds_value, 'Has correct DS values' );
};
};
# start a second test with IPv6 disabled
$params->{ipv6} = 0;
$hash_id = $rpcapi->start_domain_test( $params );
$rpcapi->{db}->claim_test( $hash_id )
or BAIL_OUT( "test needs to be claimed before calling run()" );
diag "running the agent on test $hash_id";
$agent->run($hash_id);
subtest 'second test has IPv6 disabled' => sub {
my $res = $rpcapi->get_test_params( { test_id => $hash_id } );
is( $res->{ipv4}, $params->{ipv4}, 'Retrieve the correct "ipv4" value' );
is( $res->{ipv6}, $params->{ipv6}, 'Retrieve the correct "ipv6" value' );
$res = $rpcapi->get_test_results( { id => $hash_id, language => 'en' } );
my @msgs = map { $_->{message} } @{ $res->{results} };
ok( grep( /IPv6 is disabled/, @msgs ), 'Results contain an "IPv6 is disabled" message' );
};
my $test_history;
subtest 'get_test_history' => sub {
my $offset = 0;
my $limit = 10;
my $method_params = {
frontend_params => { domain => $params->{domain} },
offset => $offset,
limit => $limit
};
$test_history = $rpcapi->get_test_history( $method_params );
note explain( $test_history );
is( scalar( @$test_history ), 2, 'Two tests created' );
foreach my $res (@$test_history) {
is( length($res->{id}), 16, 'Test has 16 characters length hash ID' );
is( $res->{undelegated}, JSON::PP::true, 'Test is undelegated' );
ok( ! exists $res->{creation_time}, 'Key "creation_time" should be missing' );
ok( defined $res->{created_at}, 'Value "created_at" properly defined' );
ok( defined $res->{overall_result}, 'Value "overall_result" properly defined' );
}
subtest 'include finished tests only' => sub {
# start a thirs test with IPv4 disabled
$params->{ipv6} = 1;
$params->{ipv4} = 0;
# create the test, retrieve its id but we don't run it
$rpcapi->start_domain_test( $params );
( $hash_id ) = $rpcapi->{db}->get_test_request();
$test_history = $rpcapi->get_test_history( $method_params );
note explain( $test_history );
is( scalar( @$test_history ), 2, 'Only 2 tests should be retrieved' );
# now run the test
diag "running the agent on test $hash_id";
$agent->run( $hash_id );
$test_history = $rpcapi->get_test_history( $method_params );
is( scalar( @$test_history ), 3, 'Now 3 tests should be retrieved' );
}
};
subtest 'mock another client (i.e. reuse a previous test)' => sub {
$params->{client_id} = 'Another Client';
$params->{client_version} = '0.1';
my $new_hash_id = $rpcapi->start_domain_test( $params );
is( $new_hash_id, $hash_id, 'Has the same hash than previous test' );
subtest 'check test_params values' => sub {
my $res = $rpcapi->get_test_params( { test_id => "$hash_id" } );
# the following values are part of the fingerprint
is( $res->{domain}, $params->{domain}, 'Retrieve the correct "domain" value' );
is( $res->{profile}, $params->{profile}, 'Retrieve the correct "profile" value' );
is( $res->{ipv4}, $params->{ipv4}, 'Retrieve the correct "ipv4" value' );
is( $res->{ipv6}, $params->{ipv6}, 'Retrieve the correct "ipv6" value' );
is_deeply( $res->{nameservers}, $params->{nameservers}, 'Retrieve the correct "nameservers" value' );
is_deeply( $res->{ds_info}, $params->{ds_info}, 'Retrieve the correct "ds_info" value' );
# both client_id and client_version are different since an old test has been reused
isnt( $res->{client_id}, $params->{client_id}, 'The "client_id" value is not the same (which is fine)' );
isnt( $res->{client_version}, $params->{client_version}, 'The "client_version" value is not the same (which is fine)' );
};
};
subtest 'check historic tests' => sub {
# Verifies that delegated and undelegated tests are coded correctly when started
# and that the filter option in "get_test_history" works correctly
my $domain = 'xa';
# Non-batch for "start_domain_test":
my $params_un1 = { # undelegated, non-batch
domain => $domain,
nameservers => [
{ ns => 'ns2.nic.fr', ip => '192.134.4.1' },
],
};
my $params_un2 = { # undelegated, non-batch
domain => $domain,
ds_info => [
{ keytag => 11627, algorithm => 8, digtype => 2, digest => 'a6cca9e6027ecc80ba0f6d747923127f1d69005fe4f0ec0461bd633482595448' },
],
};
my $params_dn1 = { # delegated, non-batch
domain => $domain,
};
# Batch for "add_batch_job"
my $domain2 = 'xb';
my $params_ub1 = { # undelegated, batch
domains => [ $domain, $domain2 ],
test_params => {
nameservers => [
{ ns => 'ns2.nic.fr', ip => '192.134.4.1' },
],
},
};
my $params_ub2 = { # undelegated, batch
domains => [ $domain, $domain2 ],
test_params => {
ds_info => [
{ keytag => 11627, algorithm => 8, digtype => 2, digest => 'a6cca9e6027ecc80ba0f6d747923127f1d69005fe4f0ec0461bd633482595448' },
],
},
};
my $params_db1 = { # delegated, batch
domains => [ $domain, $domain2 ],
};
# The batch jobs, $params_ub1, $params_ub2 and $params_db1, cannot be run from here due to limitation in the API. See issue #827.
foreach my $param ($params_un1, $params_un2, $params_dn1) {
my $testid = $rpcapi->start_domain_test( $param );
ok( $testid, "API start_domain_test ID OK" );
$rpcapi->{db}->claim_test( $testid )
or BAIL_OUT( "test needs to be claimed before calling run()" );
diag "running the agent on test $testid";
$agent->run( $testid );
is( $rpcapi->test_progress( { test_id => $testid } ), 100 , 'API test_progress -> Test finished' );
};
my $test_history_delegated = $rpcapi->get_test_history(
{
filter => 'delegated',
frontend_params => {
domain => $domain,
}
} );
my $test_history_undelegated = $rpcapi->get_test_history(
{
filter => 'undelegated',
frontend_params => {
domain => $domain,
}
} );
note explain( $test_history_delegated );
is( scalar( @$test_history_delegated ), 1, 'One delegated test created' );
note explain( $test_history_undelegated );
is( scalar( @$test_history_undelegated ), 2, 'Two undelegated tests created' );
subtest 'domain is case and trailing dot insensitive' => sub {
my $test_history_delegated = $rpcapi->get_test_history(
{
filter => 'delegated',
frontend_params => {
domain => $domain . '.',
}
} );
my $test_history_undelegated = $rpcapi->get_test_history(
{
filter => 'undelegated',
frontend_params => {
domain => ucfirst( $domain ),
}
} );
is( scalar( @$test_history_delegated ), 1, 'One delegated test created' );
is( scalar( @$test_history_undelegated ), 2, 'Two undelegated tests created' );
};
};
subtest 'normalize "domain" column' => sub {
my %domains_to_test = (
"aFnIc.Fr" => "afnic.fr",
"afnic.fr." => "afnic.fr",
"aFnic.Fr." => "afnic.fr"
);
my $test_params = {
client_id => 'Unit Test',
client_version => '1.0',
};
while ( my ($domain, $expected) = each (%domains_to_test) ) {
$test_params->{domain} = $domain;
$hash_id = $rpcapi->start_domain_test( $test_params );
my ( $db_domain ) = $dbh->selectrow_array( "SELECT domain FROM test_results WHERE hash_id=?", undef, $hash_id );
is( $db_domain, $expected, 'stored domain name is normalized' );
}
};
TestUtil::save_datafile( $datafile );
done_testing();