Tests: restructure to more closely mirror the sources
t/ had lots of directories and files mirroring src/lib/Hydra. This moves those files under t/Hydra
This commit is contained in:
139
t/Hydra/Schema/Result/RunCommandLogs.t
Normal file
139
t/Hydra/Schema/Result/RunCommandLogs.t
Normal file
@ -0,0 +1,139 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use Setup;
|
||||
use Test2::V0;
|
||||
|
||||
my $ctx = test_context();
|
||||
my $db = $ctx->db();
|
||||
|
||||
my $builds = $ctx->makeAndEvaluateJobset(
|
||||
expression => "basic.nix",
|
||||
);
|
||||
|
||||
my $build = $builds->{"empty_dir"};
|
||||
|
||||
sub new_run_log {
|
||||
return $db->resultset('RunCommandLogs')->create({
|
||||
job_matcher => "*:*:*",
|
||||
build_id => $build->get_column('id'),
|
||||
command => "bogus",
|
||||
});
|
||||
}
|
||||
|
||||
subtest "Not yet started" => sub {
|
||||
my $runlog = new_run_log();
|
||||
|
||||
is($runlog->start_time, undef, "The start time is undefined.");
|
||||
is($runlog->end_time, undef, "The start time is undefined.");
|
||||
is($runlog->exit_code, undef, "The exit code is undefined.");
|
||||
is($runlog->signal, undef, "The signal is undefined.");
|
||||
is($runlog->core_dumped, undef, "The core dump status is undefined.");
|
||||
};
|
||||
|
||||
subtest "Completing a process before it is started is invalid" => sub {
|
||||
my $runlog = new_run_log();
|
||||
|
||||
like(
|
||||
dies {
|
||||
$runlog->completed_with_child_error(0, 0);
|
||||
},
|
||||
qr/runcommandlogs_end_time_has_start_time/,
|
||||
"It is invalid to complete the process before it started"
|
||||
);
|
||||
};
|
||||
|
||||
subtest "Starting a process" => sub {
|
||||
my $runlog = new_run_log();
|
||||
$runlog->started();
|
||||
is($runlog->did_succeed(), undef, "The process has not yet succeeded.");
|
||||
ok($runlog->is_running(), "The process is running.");
|
||||
ok(!$runlog->did_fail_with_signal(), "The process was not killed by a signal.");
|
||||
ok(!$runlog->did_fail_with_exec_error(), "The process did not fail to start due to an exec error.");
|
||||
is($runlog->start_time, within(time() - 1, 2), "The start time is recent.");
|
||||
is($runlog->end_time, undef, "The end time is undefined.");
|
||||
is($runlog->exit_code, undef, "The exit code is undefined.");
|
||||
is($runlog->signal, undef, "The signal is undefined.");
|
||||
is($runlog->core_dumped, undef, "The core dump status is undefined.");
|
||||
};
|
||||
|
||||
subtest "The process completed (success)" => sub {
|
||||
my $runlog = new_run_log();
|
||||
$runlog->started();
|
||||
$runlog->completed_with_child_error(0, 123);
|
||||
ok($runlog->did_succeed(), "The process did succeed.");
|
||||
ok(!$runlog->is_running(), "The process is not running.");
|
||||
ok(!$runlog->did_fail_with_signal(), "The process was not killed by a signal.");
|
||||
ok(!$runlog->did_fail_with_exec_error(), "The process did not fail to start due to an exec error.");
|
||||
is($runlog->start_time, within(time() - 1, 2), "The start time is recent.");
|
||||
is($runlog->end_time, within(time() - 1, 2), "The end time is recent.");
|
||||
is($runlog->error_number, undef, "The error number is undefined");
|
||||
is($runlog->exit_code, 0, "The exit code is 0.");
|
||||
is($runlog->signal, undef, "The signal is undefined.");
|
||||
is($runlog->core_dumped, undef, "The core dump is undefined.");
|
||||
};
|
||||
|
||||
subtest "The process completed (errored)" => sub {
|
||||
my $runlog = new_run_log();
|
||||
$runlog->started();
|
||||
$runlog->completed_with_child_error(21760, 123);
|
||||
ok(!$runlog->did_succeed(), "The process did not succeed.");
|
||||
ok(!$runlog->is_running(), "The process is not running.");
|
||||
ok(!$runlog->did_fail_with_signal(), "The process was not killed by a signal.");
|
||||
ok(!$runlog->did_fail_with_exec_error(), "The process did not fail to start due to an exec error.");
|
||||
is($runlog->start_time, within(time() - 1, 2), "The start time is recent.");
|
||||
is($runlog->end_time, within(time() - 1, 2), "The end time is recent.");
|
||||
is($runlog->error_number, undef, "The error number is undefined");
|
||||
is($runlog->exit_code, 85, "The exit code is 85.");
|
||||
is($runlog->signal, undef, "The signal is undefined.");
|
||||
is($runlog->core_dumped, undef, "The core dump is undefined.");
|
||||
};
|
||||
|
||||
subtest "The process completed (status 15, child error 0)" => sub {
|
||||
my $runlog = new_run_log();
|
||||
$runlog->started();
|
||||
$runlog->completed_with_child_error(15, 0);
|
||||
ok(!$runlog->did_succeed(), "The process did not succeed.");
|
||||
ok(!$runlog->is_running(), "The process is not running.");
|
||||
ok($runlog->did_fail_with_signal(), "The process was killed by a signal.");
|
||||
ok(!$runlog->did_fail_with_exec_error(), "The process did not fail to start due to an exec error.");
|
||||
is($runlog->start_time, within(time() - 1, 2), "The start time is recent.");
|
||||
is($runlog->end_time, within(time() - 1, 2), "The end time is recent.");
|
||||
is($runlog->error_number, undef, "The error number is undefined");
|
||||
is($runlog->exit_code, undef, "The exit code is undefined.");
|
||||
is($runlog->signal, 15, "Signal 15 was sent.");
|
||||
is($runlog->core_dumped, 0, "There was no core dump.");
|
||||
};
|
||||
|
||||
subtest "The process completed (signaled)" => sub {
|
||||
my $runlog = new_run_log();
|
||||
$runlog->started();
|
||||
$runlog->completed_with_child_error(393, 234);
|
||||
ok(!$runlog->did_succeed(), "The process did not succeed.");
|
||||
ok(!$runlog->is_running(), "The process is not running.");
|
||||
ok($runlog->did_fail_with_signal(), "The process was killed by a signal.");
|
||||
ok(!$runlog->did_fail_with_exec_error(), "The process did not fail to start due to an exec error.");
|
||||
is($runlog->start_time, within(time() - 1, 2), "The start time is recent.");
|
||||
is($runlog->end_time, within(time() - 1, 2), "The end time is recent.");
|
||||
is($runlog->error_number, undef, "The error number is undefined");
|
||||
is($runlog->exit_code, undef, "The exit code is undefined.");
|
||||
is($runlog->signal, 9, "The signal is 9.");
|
||||
is($runlog->core_dumped, 1, "The core dumped.");
|
||||
};
|
||||
|
||||
subtest "The process failed to start" => sub {
|
||||
my $runlog = new_run_log();
|
||||
$runlog->started();
|
||||
$runlog->completed_with_child_error(-1, 2);
|
||||
ok(!$runlog->did_succeed(), "The process did not succeed.");
|
||||
ok(!$runlog->is_running(), "The process is running.");
|
||||
ok(!$runlog->did_fail_with_signal(), "The process was not killed by a signal.");
|
||||
ok($runlog->did_fail_with_exec_error(), "The process failed to start due to an exec error.");
|
||||
is($runlog->start_time, within(time() - 1, 2), "The start time is recent.");
|
||||
is($runlog->end_time, within(time() - 1, 2), "The end time is recent.");
|
||||
is($runlog->error_number, 2, "The error number is saved");
|
||||
is($runlog->exit_code, undef, "The exit code is undefined.");
|
||||
is($runlog->signal, undef, "The signal is undefined.");
|
||||
is($runlog->core_dumped, undef, "The core dumped is not defined.");
|
||||
};
|
||||
|
||||
done_testing;
|
35
t/Hydra/Schema/Result/TaskRetries.t
Normal file
35
t/Hydra/Schema/Result/TaskRetries.t
Normal file
@ -0,0 +1,35 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use Setup;
|
||||
|
||||
my %ctx = test_init();
|
||||
|
||||
require Hydra::Schema;
|
||||
require Hydra::Model::DB;
|
||||
|
||||
use Test2::V0;
|
||||
|
||||
my $db = Hydra::Model::DB->new;
|
||||
hydra_setup($db);
|
||||
|
||||
my $taskretries = $db->resultset('TaskRetries');
|
||||
|
||||
subtest "requeue" => sub {
|
||||
my $task = $taskretries->create({
|
||||
channel => "bogus",
|
||||
pluginname => "bogus",
|
||||
payload => "bogus",
|
||||
attempts => 1,
|
||||
retry_at => time(),
|
||||
});
|
||||
|
||||
$task->requeue();
|
||||
is($task->attempts, 2, "We should have stored a second retry");
|
||||
is($task->retry_at, within(time() + 4, 2), "Delayed two exponential backoff step");
|
||||
|
||||
$task->requeue();
|
||||
is($task->attempts, 3, "We should have stored a third retry");
|
||||
is($task->retry_at, within(time() + 8, 2), "Delayed a third exponential backoff step");
|
||||
};
|
||||
|
||||
done_testing;
|
107
t/Hydra/Schema/ResultSet/TaskRetries.t
Normal file
107
t/Hydra/Schema/ResultSet/TaskRetries.t
Normal file
@ -0,0 +1,107 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use Setup;
|
||||
|
||||
my %ctx = test_init();
|
||||
|
||||
use Hydra::Event;
|
||||
use Hydra::Task;
|
||||
require Hydra::Schema;
|
||||
require Hydra::Model::DB;
|
||||
|
||||
use Test2::V0;
|
||||
|
||||
my $db = Hydra::Model::DB->new;
|
||||
hydra_setup($db);
|
||||
|
||||
my $taskretries = $db->resultset('TaskRetries');
|
||||
|
||||
subtest "get_seconds_to_next_retry" => sub {
|
||||
subtest "Without any records in the database" => sub {
|
||||
is($taskretries->get_seconds_to_next_retry(), undef, "Without any records our next retry moment is forever away.");
|
||||
};
|
||||
|
||||
subtest "With only tasks whose retry timestamps are in the future" => sub {
|
||||
$taskretries->create({
|
||||
channel => "bogus",
|
||||
pluginname => "bogus",
|
||||
payload => "bogus",
|
||||
attempts => 1,
|
||||
retry_at => time() + 100,
|
||||
});
|
||||
is($taskretries->get_seconds_to_next_retry(), within(100, 2), "We should retry in roughly 100 seconds");
|
||||
};
|
||||
|
||||
subtest "With tasks whose retry timestamp are in the past" => sub {
|
||||
$taskretries->create({
|
||||
channel => "bogus",
|
||||
pluginname => "bogus",
|
||||
payload => "bogus",
|
||||
attempts => 1,
|
||||
retry_at => time() - 100,
|
||||
});
|
||||
is($taskretries->get_seconds_to_next_retry(), 0, "We should retry immediately");
|
||||
};
|
||||
|
||||
$taskretries->delete_all();
|
||||
};
|
||||
|
||||
subtest "get_retryable_taskretries_row" => sub {
|
||||
subtest "Without any records in the database" => sub {
|
||||
is($taskretries->get_retryable_taskretries_row(), undef, "Without any records we have no tasks to retry.");
|
||||
is($taskretries->get_retryable_task(), undef, "Without any records we have no tasks to retry.");
|
||||
};
|
||||
|
||||
subtest "With only tasks whose retry timestamps are in the future" => sub {
|
||||
$taskretries->create({
|
||||
channel => "bogus",
|
||||
pluginname => "bogus",
|
||||
payload => "bogus",
|
||||
attempts => 1,
|
||||
retry_at => time() + 100,
|
||||
});
|
||||
is($taskretries->get_retryable_taskretries_row(), undef, "We still have nothing to do");
|
||||
is($taskretries->get_retryable_task(), undef, "We still have nothing to do");
|
||||
};
|
||||
|
||||
subtest "With tasks whose retry timestamp are in the past" => sub {
|
||||
$taskretries->create({
|
||||
channel => "build_started",
|
||||
pluginname => "bogus plugin",
|
||||
payload => "123",
|
||||
attempts => 1,
|
||||
retry_at => time() - 100,
|
||||
});
|
||||
|
||||
my $row = $taskretries->get_retryable_taskretries_row();
|
||||
isnt($row, undef, "We should retry immediately");
|
||||
is($row->channel, "build_started", "Channel name should match");
|
||||
is($row->pluginname, "bogus plugin", "Plugin name should match");
|
||||
is($row->payload, "123", "Payload should match");
|
||||
is($row->attempts, 1, "We've had one attempt");
|
||||
|
||||
my $task = $taskretries->get_retryable_task();
|
||||
is($task->{"event"}->{"channel_name"}, "build_started");
|
||||
is($task->{"plugin_name"}, "bogus plugin");
|
||||
is($task->{"event"}->{"payload"}, "123");
|
||||
is($task->{"record"}->get_column("id"), $row->get_column("id"));
|
||||
};
|
||||
};
|
||||
|
||||
subtest "save_task" => sub {
|
||||
my $event = Hydra::Event->new_event("build_started", "1");
|
||||
my $task = Hydra::Task->new(
|
||||
$event,
|
||||
"FooPluginName",
|
||||
);
|
||||
|
||||
my $retry = $taskretries->save_task($task);
|
||||
|
||||
is($retry->channel, "build_started", "Channel name should match");
|
||||
is($retry->pluginname, "FooPluginName", "Plugin name should match");
|
||||
is($retry->payload, "1", "Payload should match");
|
||||
is($retry->attempts, 1, "We've had one attempt");
|
||||
is($retry->retry_at, within(time() + 1, 2), "The retry at should be approximately one second away");
|
||||
};
|
||||
|
||||
done_testing;
|
73
t/Hydra/Schema/Users.t
Normal file
73
t/Hydra/Schema/Users.t
Normal file
@ -0,0 +1,73 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use Setup;
|
||||
|
||||
my %ctx = test_init();
|
||||
|
||||
require Hydra::Schema;
|
||||
require Hydra::Model::DB;
|
||||
|
||||
use Test2::V0;
|
||||
|
||||
my $db = Hydra::Model::DB->new;
|
||||
hydra_setup($db);
|
||||
|
||||
# Hydra used to store passwords, by default, as plain unsalted sha1 hashes.
|
||||
# We now upgrade these badly stored passwords with much stronger algorithms
|
||||
# when the user logs in. Implementing this meant reimplementing our password
|
||||
# checking ourselves, so also ensure that basic password checking works.
|
||||
#
|
||||
# This test:
|
||||
#
|
||||
# 1. creates a user with the legacy password
|
||||
# 2. validates that the wrong password is not considered valid
|
||||
# 3. validates that the correct password is valid
|
||||
# 4. checks that the checking of the correct password transparently upgraded
|
||||
# the password's storage to a more secure algorithm.
|
||||
|
||||
# Starting the user with an unsalted sha1 password
|
||||
my $user = $db->resultset('Users')->create({
|
||||
"username" => "alice",
|
||||
"emailaddress" => 'alice@nixos.org',
|
||||
"password" => "8843d7f92416211de9ebb963ff4ce28125932878" # SHA1 of "foobar"
|
||||
});
|
||||
isnt($user, undef, "My user was created.");
|
||||
|
||||
ok(!$user->check_password("barbaz"), "Checking the password, barbaz, is not right");
|
||||
|
||||
is($user->password, "8843d7f92416211de9ebb963ff4ce28125932878", "The unsalted sha1 is in the database.");
|
||||
ok($user->check_password("foobar"), "Checking the password, foobar, is right");
|
||||
isnt($user->password, "8843d7f92416211de9ebb963ff4ce28125932878", "The user has had their password rehashed.");
|
||||
ok($user->check_password("foobar"), "Checking the password, foobar, is still right");
|
||||
|
||||
# All sha1 passwords will be upgraded when `hydra-init` is run, by passing the sha1 through
|
||||
# Argon2. Verify a rehashed sha1 validates too. This removes very weak password hashes
|
||||
# from the database without requiring users to log in.
|
||||
subtest "Hashing their sha1 as Argon2 still lets them log in with their password" => sub {
|
||||
$user->setPassword("8843d7f92416211de9ebb963ff4ce28125932878"); # SHA1 of "foobar"
|
||||
my $hashedHashPassword = $user->password;
|
||||
isnt($user->password, "8843d7f92416211de9ebb963ff4ce28125932878", "The user has had their password's hash rehashed.");
|
||||
ok($user->check_password("foobar"), "Checking the password, foobar, is still right");
|
||||
isnt($user->password, $hashedHashPassword, "The user's hashed hash was replaced with just Argon2.");
|
||||
};
|
||||
|
||||
|
||||
subtest "Setting the user's passwordHash to a sha1 stores the password as a hashed sha1" => sub {
|
||||
$user->setPasswordHash("8843d7f92416211de9ebb963ff4ce28125932878");
|
||||
isnt($user->password, "8843d7f92416211de9ebb963ff4ce28125932878", "The password was not saved in plain text.");
|
||||
|
||||
my $storedPassword = $user->password;
|
||||
ok($user->check_password("foobar"), "Their password validates");
|
||||
isnt($storedPassword, $user->password, "The password was upgraded.");
|
||||
};
|
||||
|
||||
subtest "Setting the user's passwordHash to an argon2 password stores the password as given" => sub {
|
||||
$user->setPasswordHash('$argon2id$v=19$m=262144,t=3,p=1$tMnV5paYjmIrUIb6hylaNA$M8/e0i3NGrjhOliVLa5LqQ');
|
||||
isnt($user->password, "8843d7f92416211de9ebb963ff4ce28125932878", "The password was not saved in plain text.");
|
||||
is($user->password, '$argon2id$v=19$m=262144,t=3,p=1$tMnV5paYjmIrUIb6hylaNA$M8/e0i3NGrjhOliVLa5LqQ', "The password was saved as-is.");
|
||||
|
||||
my $storedPassword = $user->password;
|
||||
ok($user->check_password("foobar"), "Their password validates");
|
||||
is($storedPassword, $user->password, "The password was not upgraded.");
|
||||
};
|
||||
done_testing;
|
Reference in New Issue
Block a user