Merge pull request #942 from DeterminateSystems/port-api-test-master
Port api-test to yath
This commit is contained in:
commit
7017d41117
39
flake.nix
39
flake.nix
|
@ -456,45 +456,6 @@
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
tests.api.x86_64-linux =
|
|
||||||
with import (nixpkgs + "/nixos/lib/testing-python.nix") { system = "x86_64-linux"; };
|
|
||||||
simpleTest {
|
|
||||||
machine = { pkgs, ... }: {
|
|
||||||
imports = [ hydraServer ];
|
|
||||||
# No caching for PathInput plugin, otherwise we get wrong values
|
|
||||||
# (as it has a 30s window where no changes to the file are considered).
|
|
||||||
services.hydra-dev.extraConfig = ''
|
|
||||||
path_input_cache_validity_seconds = 0
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
testScript =
|
|
||||||
let dbi = "dbi:Pg:dbname=hydra;user=root;"; in
|
|
||||||
''
|
|
||||||
machine.wait_for_job("hydra-init")
|
|
||||||
|
|
||||||
# Create an admin account and some other state.
|
|
||||||
machine.succeed(
|
|
||||||
"""
|
|
||||||
su - hydra -c "hydra-create-user root --email-address 'alice@example.org' --password foobar --role admin"
|
|
||||||
mkdir /run/jobset /tmp/nix
|
|
||||||
chmod 755 /run/jobset /tmp/nix
|
|
||||||
cp ${./t/api-test.nix} /run/jobset/default.nix
|
|
||||||
chmod 644 /run/jobset/default.nix
|
|
||||||
chown -R hydra /run/jobset /tmp/nix
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
machine.succeed("systemctl stop hydra-evaluator hydra-queue-runner")
|
|
||||||
machine.wait_for_job("hydra-server")
|
|
||||||
machine.wait_for_open_port("3000")
|
|
||||||
|
|
||||||
# Run the API tests.
|
|
||||||
machine.succeed(
|
|
||||||
"su - hydra -c 'perl -I ${pkgs.hydra.perlDeps}/lib/perl5/site_perl ${./t/api-test.pl}' >&2"
|
|
||||||
)
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
tests.notifications.x86_64-linux =
|
tests.notifications.x86_64-linux =
|
||||||
with import (nixpkgs + "/nixos/lib/testing-python.nix") { system = "x86_64-linux"; };
|
with import (nixpkgs + "/nixos/lib/testing-python.nix") { system = "x86_64-linux"; };
|
||||||
simpleTest {
|
simpleTest {
|
||||||
|
|
|
@ -40,7 +40,7 @@ sub login_POST {
|
||||||
$self->status_found(
|
$self->status_found(
|
||||||
$c,
|
$c,
|
||||||
location => $c->uri_for("current-user"),
|
location => $c->uri_for("current-user"),
|
||||||
entity => {}
|
entity => $c->model("DB::Users")->find($c->user->username)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
use LWP::UserAgent;
|
|
||||||
use JSON;
|
|
||||||
use Test::Simple tests => 20;
|
|
||||||
|
|
||||||
my $ua = LWP::UserAgent->new;
|
|
||||||
$ua->cookie_jar({});
|
|
||||||
|
|
||||||
sub request_json {
|
|
||||||
my ($opts) = @_;
|
|
||||||
my $req = HTTP::Request->new;
|
|
||||||
$req->method($opts->{method} or "GET");
|
|
||||||
$req->uri("http://localhost:3000$opts->{uri}");
|
|
||||||
$req->header(Accept => "application/json");
|
|
||||||
$req->header(Referer => "http://localhost:3000/") if $opts->{method} eq "POST";
|
|
||||||
$req->content(encode_json($opts->{data})) if defined $opts->{data};
|
|
||||||
my $res = $ua->request($req);
|
|
||||||
print $res->as_string();
|
|
||||||
return $res;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $result = request_json({ uri => "/login", method => "POST", data => { username => "root", password => "wrong" } });
|
|
||||||
ok($result->code() == 403, "Incorrect password rejected.");
|
|
||||||
|
|
||||||
my $result = request_json({ uri => "/login", method => "POST", data => { username => "root", password => "foobar" } });
|
|
||||||
|
|
||||||
my $user = decode_json($result->content());
|
|
||||||
|
|
||||||
ok($user->{username} eq "root", "The root user is named root");
|
|
||||||
ok($user->{userroles}->[0] eq "admin", "The root user is an admin");
|
|
||||||
|
|
||||||
$user = decode_json(request_json({ uri => "/current-user" })->content());
|
|
||||||
ok($user->{username} eq "root", "The current user is named root");
|
|
||||||
ok($user->{userroles}->[0] eq "admin", "The current user is an admin");
|
|
||||||
|
|
||||||
ok(request_json({ uri => '/project/sample' })->code() == 404, "Non-existent projects don't exist");
|
|
||||||
|
|
||||||
$result = request_json({ uri => '/project/sample', method => 'PUT', data => { displayname => "Sample", enabled => "1", visible => "1", } });
|
|
||||||
ok($result->code() == 201, "PUTting a new project creates it");
|
|
||||||
|
|
||||||
my $project = decode_json(request_json({ uri => '/project/sample' })->content());
|
|
||||||
|
|
||||||
ok((not @{$project->{jobsets}}), "A new project has no jobsets");
|
|
||||||
|
|
||||||
$result = request_json({ uri => '/jobset/sample/default', method => 'PUT', data => { nixexprpath => "default.nix", nixexprinput => "my-src", inputs => { "my-src" => { type => "path", value => "/run/jobset" } }, enabled => "1", visible => "1", checkinterval => "3600"} });
|
|
||||||
ok($result->code() == 201, "PUTting a new jobset creates it");
|
|
||||||
|
|
||||||
my $jobset = decode_json(request_json({ uri => '/jobset/sample/default' })->content());
|
|
||||||
|
|
||||||
ok(exists $jobset->{jobsetinputs}->{"my-src"}, "The new jobset has a 'my-src' input");
|
|
||||||
|
|
||||||
ok($jobset->{jobsetinputs}->{"my-src"}->{jobsetinputalts}->[0] eq "/run/jobset", "The 'my-src' input is in /run/jobset");
|
|
||||||
|
|
||||||
system("hydra-eval-jobset sample default");
|
|
||||||
$result = request_json({ uri => '/jobset/sample/default/evals' });
|
|
||||||
ok($result->code() == 200, "Can get evals of a jobset");
|
|
||||||
my $evals = decode_json($result->content())->{evals};
|
|
||||||
my $eval = $evals->[0];
|
|
||||||
ok($eval->{hasnewbuilds} == 1, "The first eval of a jobset has new builds");
|
|
||||||
|
|
||||||
system("echo >> /run/jobset/default.nix; hydra-eval-jobset sample default");
|
|
||||||
my $evals = decode_json(request_json({ uri => '/jobset/sample/default/evals' })->content())->{evals};
|
|
||||||
ok(scalar(@$evals) == 2, "Changing a jobset source creates the second evaluation");
|
|
||||||
ok($evals->[0]->{jobsetevalinputs}->{"my-src"}->{revision} != $evals->[1]->{jobsetevalinputs}->{"my-src"}->{revision}, "Changing a jobset source changes its revision");
|
|
||||||
|
|
||||||
my $build = decode_json(request_json({ uri => "/build/" . $evals->[0]->{builds}->[0] })->content());
|
|
||||||
ok($build->{job} eq "job", "The build's job name is job");
|
|
||||||
ok($build->{finished} == 0, "The build isn't finished yet");
|
|
||||||
ok($build->{buildoutputs}->{out}->{path} =~ /^\/nix\/store\/[a-zA-Z0-9]{32}-job$/, "The build's outpath is in the Nix store and named 'job'");
|
|
||||||
|
|
||||||
|
|
||||||
my $search_project = decode_json(request_json({ uri => "/search/?query=sample" })->content());
|
|
||||||
ok($search_project->{projects}[0]->{name} == "sample", "Search for project 'sample' works");
|
|
||||||
|
|
||||||
my $search_build = decode_json(request_json({ uri => "/search/?query=" . $build->{buildoutputs}->{out}->{path} })->content());
|
|
||||||
ok($search_build->{builds}[0]->{buildoutputs}->{out}->{path} == $build->{buildoutputs}->{out}->{path}, "Search for builds work");
|
|
131
t/api-test.t
Normal file
131
t/api-test.t
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
use strict;
|
||||||
|
use Setup;
|
||||||
|
use JSON;
|
||||||
|
use File::Copy;
|
||||||
|
|
||||||
|
my %ctx = test_init(
|
||||||
|
hydra_config => q|
|
||||||
|
# No caching for PathInput plugin, otherwise we get wrong values
|
||||||
|
# (as it has a 30s window where no changes to the file are considered).
|
||||||
|
path_input_cache_validity_seconds = 0
|
||||||
|
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
my $jobsetdir = $ctx{tmpdir} . '/jobset';
|
||||||
|
mkdir($jobsetdir);
|
||||||
|
copy($ctx{jobsdir} . '/api-test.nix', "$jobsetdir/default.nix");
|
||||||
|
|
||||||
|
require Hydra::Schema;
|
||||||
|
use HTTP::Request::Common;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
require Catalyst::Test;
|
||||||
|
Catalyst::Test->import('Hydra');
|
||||||
|
|
||||||
|
my $db = Hydra::Model::DB->new;
|
||||||
|
hydra_setup($db);
|
||||||
|
|
||||||
|
{
|
||||||
|
my $user = $db->resultset('Users')->find({ username => 'root' });
|
||||||
|
$user->setPassword('foobar');
|
||||||
|
$user->userroles->update_or_create({ role => 'admin' });
|
||||||
|
}
|
||||||
|
|
||||||
|
my $cookie = "";
|
||||||
|
|
||||||
|
sub request_json {
|
||||||
|
my ($opts) = @_;
|
||||||
|
my $req = HTTP::Request->new;
|
||||||
|
$req->method($opts->{method} or "GET");
|
||||||
|
$req->uri("http://localhost$opts->{uri}");
|
||||||
|
$req->header(Accept => "application/json");
|
||||||
|
$req->header(Content_Type => "application/json");
|
||||||
|
$req->header(Origin => "http://localhost/") if $opts->{method} eq "POST";
|
||||||
|
$req->header(Cookie => $cookie);
|
||||||
|
|
||||||
|
$req->content(encode_json($opts->{data})) if defined $opts->{data};
|
||||||
|
my $res = request($req);
|
||||||
|
print $res->as_string();
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
subtest "login" => sub {
|
||||||
|
subtest "bad password" => sub {
|
||||||
|
my $result = request_json({ uri => "/login", method => "POST", data => { username => "root", password => "wrong" } });
|
||||||
|
is($result->code(), 403, "Incorrect password rejected.");
|
||||||
|
};
|
||||||
|
|
||||||
|
my $result = request_json({ uri => "/login", method => "POST", data => { username => "root", password => "foobar" } });
|
||||||
|
|
||||||
|
my $user = decode_json($result->content());
|
||||||
|
|
||||||
|
is($user->{username}, "root", "The root user is named root");
|
||||||
|
is($user->{userroles}->[0], "admin", "The root user is an admin");
|
||||||
|
$cookie = $result->header("set-cookie");
|
||||||
|
|
||||||
|
$user = decode_json(request_json({ uri => "/current-user" })->content());
|
||||||
|
is($user->{username}, "root", "The current user is named root");
|
||||||
|
is($user->{userroles}->[0], "admin", "The current user is an admin");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "projects" => sub {
|
||||||
|
is(request_json({ uri => '/project/sample' })->code(), 404, "Non-existent projects don't exist");
|
||||||
|
|
||||||
|
my $result = request_json({ uri => '/project/sample', method => 'PUT', data => { displayname => "Sample", enabled => "1", visible => "1", } });
|
||||||
|
is($result->code(), 201, "PUTting a new project creates it");
|
||||||
|
|
||||||
|
my $project = decode_json(request_json({ uri => '/project/sample' })->content());
|
||||||
|
|
||||||
|
ok((not @{$project->{jobsets}}), "A new project has no jobsets");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "jobsets" => sub {
|
||||||
|
my $result = request_json({ uri => '/jobset/sample/default', method => 'PUT', data => { nixexprpath => "default.nix", nixexprinput => "my-src", jobsetinputs => { "my-src" => { type => "path", value => $jobsetdir } }, enabled => "1", visible => "1", checkinterval => "3600"} });
|
||||||
|
is($result->code(), 201, "PUTting a new jobset creates it");
|
||||||
|
|
||||||
|
my $jobset = decode_json(request_json({ uri => '/jobset/sample/default' })->content());
|
||||||
|
|
||||||
|
ok(exists $jobset->{jobsetinputs}->{"my-src"}, "The new jobset has a 'my-src' input");
|
||||||
|
|
||||||
|
is($jobset->{jobsetinputs}->{"my-src"}->{"jobsetinputalts"}->[0], $jobsetdir, "The 'my-src' input is in $jobsetdir");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "evaluation" => sub {
|
||||||
|
subtest "initial evaluaton" => sub {
|
||||||
|
ok(evalSucceeds($db->resultset('Jobsets')->find({ name => 'default' })), "Evaluating should exit with return code 0");
|
||||||
|
|
||||||
|
my $result = request_json({ uri => '/jobset/sample/default/evals' });
|
||||||
|
is($result->code(), 200, "Can get evals of a jobset");
|
||||||
|
my $evals = decode_json($result->content())->{evals};
|
||||||
|
my $eval = $evals->[0];
|
||||||
|
is($eval->{hasnewbuilds}, 1, "The first eval of a jobset has new builds");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "second evaluation" => sub {
|
||||||
|
open(my $fh, ">>", "${jobsetdir}/default.nix") or die "didn't open?";
|
||||||
|
say $fh "\n";
|
||||||
|
close $fh;
|
||||||
|
ok(evalSucceeds($db->resultset('Jobsets')->find({ name => 'default' })), "Evaluating should exit with return code 0");
|
||||||
|
|
||||||
|
my $evals = decode_json(request_json({ uri => '/jobset/sample/default/evals' })->content())->{evals};
|
||||||
|
is(scalar(@$evals), 2, "Changing a jobset source creates the second evaluation");
|
||||||
|
isnt($evals->[0]->{jobsetevalinputs}->{"my-src"}->{revision}, $evals->[1]->{jobsetevalinputs}->{"my-src"}->{revision}, "Changing a jobset source changes its revision");
|
||||||
|
|
||||||
|
my $build = decode_json(request_json({ uri => "/build/" . $evals->[0]->{builds}->[0] })->content());
|
||||||
|
is($build->{job}, "job", "The build's job name is job");
|
||||||
|
is($build->{finished}, 0, "The build isn't finished yet");
|
||||||
|
ok($build->{buildoutputs}->{out}->{path} =~ /\/nix\/store\/[a-zA-Z0-9]{32}-job$/, "The build's outpath is in the Nix store and named 'job'");
|
||||||
|
|
||||||
|
subtest "search" => sub {
|
||||||
|
my $search_project = decode_json(request_json({ uri => "/search/?query=sample" })->content());
|
||||||
|
is($search_project->{projects}[0]->{name}, "sample", "Search for project 'sample' works");
|
||||||
|
|
||||||
|
my $search_build = decode_json(request_json({ uri => "/search/?query=" . $build->{buildoutputs}->{out}->{path} })->content());
|
||||||
|
is($search_build->{builds}[0]->{buildoutputs}->{out}->{path}, $build->{buildoutputs}->{out}->{path}, "Search for builds work");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
done_testing;
|
Loading…
Reference in a new issue