# frozen_string_literal: true require 'spec_helper' RSpec.describe 'Query.runners', feature_category: :runner_fleet do include GraphqlHelpers let_it_be(:current_user) { create_default(:user, :admin) } describe 'Query.runners' do let_it_be(:project) { create(:project, :repository, :public) } let_it_be(:instance_runner) { create(:ci_runner, :instance, version: 'abc', revision: '123', description: 'Instance runner', ip_address: '127.0.0.1') } let_it_be(:project_runner) { create(:ci_runner, :project, active: false, version: 'def', revision: '456', description: 'Project runner', projects: [project], ip_address: '127.0.0.1') } let(:runners_graphql_data) { graphql_data_at(:runners) } let(:params) { {} } let(:fields) do <<~QUERY nodes { #{all_graphql_fields_for('CiRunner', excluded: %w[createdBy ownerProject])} createdBy { username webPath webUrl } ownerProject { id path fullPath webUrl } } QUERY end let(:query) do %( query { runners(type:#{runner_type},status:#{status}) { #{fields} } } ) end before do allow_next_instance_of(::Gitlab::Ci::RunnerUpgradeCheck) do |instance| allow(instance).to receive(:check_runner_upgrade_suggestion) end post_graphql(query, current_user: current_user) end shared_examples 'a working graphql query returning expected runner' do it_behaves_like 'a working graphql query' it 'returns expected runner' do expect(runners_graphql_data['nodes']).to contain_exactly(a_graphql_entity_for(expected_runner)) end it 'does not execute more queries per runner', :aggregate_failures do # warm-up license cache and so on: personal_access_token = create(:personal_access_token, user: current_user) args = { current_user: current_user, token: { personal_access_token: personal_access_token } } post_graphql(query, **args) expect(graphql_data_at(:runners, :nodes)).not_to be_empty admin2 = create(:admin) personal_access_token = create(:personal_access_token, user: admin2) args = { current_user: admin2, token: { personal_access_token: personal_access_token } } control = ActiveRecord::QueryRecorder.new { post_graphql(query, **args) } create(:ci_runner, :instance, version: '14.0.0', tag_list: %w[tag5 tag6], creator: admin2) create(:ci_runner, :project, version: '14.0.1', projects: [project], tag_list: %w[tag3 tag8], creator: current_user) expect { post_graphql(query, **args) }.not_to exceed_query_limit(control) end end context 'runner_type is INSTANCE_TYPE and status is ACTIVE' do let(:runner_type) { 'INSTANCE_TYPE' } let(:status) { 'ACTIVE' } let!(:expected_runner) { instance_runner } it_behaves_like 'a working graphql query returning expected runner' end context 'runner_type is PROJECT_TYPE and status is NEVER_CONTACTED' do let(:runner_type) { 'PROJECT_TYPE' } let(:status) { 'NEVER_CONTACTED' } let!(:expected_runner) { project_runner } it_behaves_like 'a working graphql query returning expected runner' end end describe 'pagination' do let(:data_path) { [:runners] } def pagination_query(params) graphql_query_for(:runners, params, "#{page_info} nodes { id }") end def pagination_results_data(runners) runners.map { |runner| GitlabSchema.parse_gid(runner['id'], expected_type: ::Ci::Runner).model_id.to_i } end let_it_be(:runners) do common_args = { version: 'abc', revision: '123', ip_address: '127.0.0.1' } [ create(:ci_runner, :instance, created_at: 4.days.ago, contacted_at: 3.days.ago, **common_args), create(:ci_runner, :instance, created_at: 30.hours.ago, contacted_at: 1.day.ago, **common_args), create(:ci_runner, :instance, created_at: 1.day.ago, contacted_at: 1.hour.ago, **common_args), create(:ci_runner, :instance, created_at: 2.days.ago, contacted_at: 2.days.ago, **common_args), create(:ci_runner, :instance, created_at: 3.days.ago, contacted_at: 1.second.ago, **common_args) ] end context 'when sorted by contacted_at ascending' do let(:ordered_runners) { runners.sort_by(&:contacted_at) } it_behaves_like 'sorted paginated query' do let(:sort_param) { :CONTACTED_ASC } let(:first_param) { 2 } let(:all_records) { ordered_runners.map(&:id) } end end context 'when sorted by created_at' do let(:ordered_runners) { runners.sort_by(&:created_at).reverse } it_behaves_like 'sorted paginated query' do let(:sort_param) { :CREATED_DESC } let(:first_param) { 2 } let(:all_records) { ordered_runners.map(&:id) } end end end end RSpec.describe 'Group.runners' do include GraphqlHelpers let_it_be(:group) { create(:group) } let_it_be(:group_owner) { create_default(:user) } before do group.add_owner(group_owner) end describe 'edges' do let_it_be(:runner) do create(:ci_runner, :group, active: false, version: 'def', revision: '456', description: 'Project runner', groups: [group], ip_address: '127.0.0.1') end let(:query) do %( query($path: ID!) { group(fullPath: $path) { runners { edges { webUrl editUrl node { #{all_graphql_fields_for('CiRunner')} } } } } } ) end it 'contains custom edge information' do r = GitlabSchema.execute(query, context: { current_user: group_owner }, variables: { path: group.full_path }) edges = graphql_dig_at(r.to_h, :data, :group, :runners, :edges) expect(edges).to contain_exactly(a_graphql_entity_for(web_url: be_present, edit_url: be_present)) end end end