spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb (159 lines of code) (raw):
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::Ci::AllJobsResolver, feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:instance_runner) { create(:ci_runner, :instance) }
let_it_be(:successful_job) { create(:ci_build, :success, name: 'successful_job') }
let_it_be(:successful_job_two) { create(:ci_build, :success, name: 'successful_job_two') }
let_it_be(:failed_job) { create(:ci_build, :failed, name: 'failed_job') }
let_it_be(:pending_job) { create(:ci_build, :pending, name: 'pending_job') }
let(:args) { {} }
describe '#resolve' do
subject(:request) { resolve_jobs(args) }
context 'when current user is an admin' do
let_it_be(:current_user) { create(:admin) }
shared_examples 'executes as admin' do
context "with argument `statuses`" do
using RSpec::Parameterized::TableSyntax
where(:statuses, :expected_jobs) do
nil | lazy { [successful_job, successful_job_two, failed_job, pending_job] }
%w[SUCCESS] | lazy { [successful_job, successful_job_two] }
%w[SUCCESS FAILED] | lazy { [successful_job, successful_job_two, failed_job] }
%w[CANCELED] | lazy { [] }
end
with_them do
let(:args) do
{ statuses: statuses&.map { |status| Types::Ci::JobStatusEnum.coerce_isolated_input(status) } }
end
it { is_expected.to contain_exactly(*expected_jobs) }
end
end
context "with argument `runner_types`" do
let_it_be(:successful_job_with_instance_runner) do
create(:ci_build, :success, name: 'successful_job_with_instance_runner', runner: instance_runner)
end
context 'with feature flag :admin_jobs_filter_runner_type enabled' do
using RSpec::Parameterized::TableSyntax
where(:runner_types, :expected_jobs) do
nil | lazy do
[
successful_job,
successful_job_two,
failed_job,
pending_job,
successful_job_with_instance_runner
]
end
%w[INSTANCE_TYPE] | lazy { [successful_job_with_instance_runner] }
%w[INSTANCE_TYPE GROUP_TYPE] | lazy { [successful_job_with_instance_runner] }
%w[PROJECT_TYPE] | lazy { [] }
end
with_them do
let(:args) do
{
runner_types: runner_types&.map { |type| Types::Ci::RunnerTypeEnum.coerce_isolated_input(type) }
}
end
it { is_expected.to match_array(expected_jobs) }
end
end
end
context "with argument combination" do
let_it_be(:successful_job_with_instance_runner) do
create(
:ci_build,
:success,
name: 'successful_job_with_instance_runner',
runner: instance_runner
)
end
let_it_be(:group) { create(:group) }
let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) }
let_it_be(:running_job_with_group_runner) do
create(:ci_build, :running, name: 'running_job_with_instance_runner', runner: group_runner)
end
context 'with feature flag :admin_jobs_filter_runner_type enabled' do
using RSpec::Parameterized::TableSyntax
where(:statuses, :runner_types, :expected_jobs) do
%w[SUCCESS] | %w[INSTANCE_TYPE] | lazy { [successful_job_with_instance_runner] }
%w[CANCELED] | %w[INSTANCE_TYPE] | lazy { [] }
%w[SUCCESS RUNNING] | %w[INSTANCE_TYPE GROUP_TYPE] | lazy do
[
successful_job_with_instance_runner,
running_job_with_group_runner
]
end
end
with_them do
let(:args) do
{
statuses: statuses&.map { |status| Types::Ci::JobStatusEnum.coerce_isolated_input(status) },
runner_types: runner_types&.map { |type| Types::Ci::RunnerTypeEnum.coerce_isolated_input(type) }
}
end
it { is_expected.to contain_exactly(*expected_jobs) }
end
end
end
end
context 'when admin mode setting is disabled', :do_not_mock_admin_mode_setting do
it_behaves_like 'executes as admin'
context 'when compatible runner id is specified' do
let_it_be(:runner) { create(:ci_runner) }
let_it_be(:expected_jobs) { create_list(:ci_build, 2, runner: runner) }
let(:args) do
{
statuses: Types::Ci::JobStatusEnum.coerce_isolated_input('PENDING'),
compatible_runner_id: runner.to_global_id
}
end
it 'calls the finder and returns the pending jobs' do
expect_next_instance_of(
Ci::JobsFinder,
current_user: current_user,
runner: runner,
params: a_hash_including(scope: ['pending'], match_compatible_runner_only: true)
) do |finder|
expect(finder).to receive(:execute).and_return(Ci::Build.id_in(expected_jobs))
end
expect(request).to match_array(expected_jobs)
end
context 'when statuses is not pending' do
let(:args) do
{
statuses: Types::Ci::JobStatusEnum.coerce_isolated_input('RUNNING'),
compatible_runner_id: runner.to_global_id
}
end
it 'does not call the finder and returns error' do
allow_next_instance_of(Ci::JobsFinder) do |finder|
expect(finder).not_to receive(:execute)
end
expect_graphql_error_to_be_created(
Gitlab::Graphql::Errors::ArgumentError, described_class::COMPATIBLE_RUNNER_ERROR_MESSAGE
) do
request
end
end
end
end
end
context 'when admin mode setting is enabled' do
context 'when in admin mode', :enable_admin_mode do
it_behaves_like 'executes as admin'
end
context 'when not in admin mode' do
it { is_expected.to be_empty }
end
end
end
context 'with unauthorized user' do
let_it_be(:unauth_user) { create(:user) }
let(:current_user) { unauth_user }
it { is_expected.to be_empty }
end
end
private
def resolve_jobs(args = {}, context = { current_user: current_user })
resolve(described_class, args: args, ctx: context)
end
end