# frozen_string_literal: true require 'spec_helper' RSpec.describe 'RunnerCreate', feature_category: :fleet_visibility do include GraphqlHelpers let_it_be(:user) { create(:user) } let_it_be(:group_owner) { create(:user) } let_it_be(:admin) { create(:admin) } let_it_be(:group) { create(:group) } let_it_be(:other_group) { create(:group) } let(:mutation_params) do { description: 'create description', maintenance_note: 'create maintenance note', maximum_timeout: 900, access_level: 'REF_PROTECTED', paused: true, run_untagged: false, tag_list: %w[tag1 tag2] }.deep_merge(mutation_scope_params) end let(:mutation) do variables = { **mutation_params } graphql_mutation( :runner_create, variables, <<-QL runner { ephemeralAuthenticationToken runnerType description maintenanceNote paused tagList accessLevel locked maximumTimeout runUntagged } errors QL ) end let(:mutation_response) { graphql_mutation_response(:runner_create) } before do group.add_owner(group_owner) end shared_context 'when model is invalid returns error' do let(:mutation_params) do { description: '', maintenanceNote: '', paused: true, accessLevel: 'NOT_PROTECTED', runUntagged: false, tagList: [], maximumTimeout: 1 }.deep_merge(mutation_scope_params) end it do post_graphql_mutation(mutation, current_user: current_user) expect(response).to have_gitlab_http_status(:success) expect(mutation_response['errors']).to contain_exactly( 'Tags list can not be empty when runner is not allowed to pick untagged jobs', 'Maximum timeout needs to be at least 10 minutes' ) end end shared_context 'when user does not have permissions' do let(:current_user) { user } it 'returns an error' do post_graphql_mutation(mutation, current_user: current_user) expect_graphql_errors_to_include( 'The resource that you are attempting to access does not exist ' \ "or you don't have permission to perform this action" ) end end shared_examples 'when runner is created successfully' do it do expected_args = { user: current_user, params: anything } expect_next_instance_of(::Ci::Runners::CreateRunnerService, expected_args) do |service| expect(service).to receive(:execute).and_call_original end post_graphql_mutation(mutation, current_user: current_user) expect(response).to have_gitlab_http_status(:success) expect(mutation_response['errors']).to eq([]) expect(mutation_response['runner']).not_to be_nil mutation_params.except(:group_id, :project_id).each_key do |key| expect(mutation_response['runner'][key.to_s.camelize(:lower)]).to eq mutation_params[key] end expect(mutation_response['runner']['ephemeralAuthenticationToken']) .to start_with Ci::Runner::CREATED_RUNNER_TOKEN_PREFIX end end context 'when runnerType is INSTANCE_TYPE' do let(:mutation_scope_params) do { runner_type: 'INSTANCE_TYPE' } end it_behaves_like 'when user does not have permissions' context 'when user has permissions', :enable_admin_mode do let(:current_user) { admin } it_behaves_like 'when runner is created successfully' it_behaves_like 'when model is invalid returns error' end end context 'when runnerType is GROUP_TYPE' do let(:mutation_scope_params) do { runner_type: 'GROUP_TYPE', group_id: group.to_global_id } end it_behaves_like 'when user does not have permissions' context 'when user has permissions' do context 'when user is group owner' do let(:current_user) { group_owner } it_behaves_like 'when runner is created successfully' it_behaves_like 'when model is invalid returns error' context 'when group_id is missing' do let(:mutation_scope_params) do { runner_type: 'GROUP_TYPE' } end it 'returns an error' do post_graphql_mutation(mutation, current_user: current_user) expect_graphql_errors_to_include('`group_id` is missing') end end context 'when group_id is malformed' do let(:mutation_scope_params) do { runner_type: 'GROUP_TYPE', group_id: '' } end it 'returns an error' do post_graphql_mutation(mutation, current_user: current_user) expect_graphql_errors_to_include( "RunnerCreateInput! was provided invalid value for groupId" ) end end context 'when group_id does not exist' do let(:mutation_scope_params) do { runner_type: 'GROUP_TYPE', group_id: "gid://gitlab/Group/#{non_existing_record_id}" } end it 'returns an error' do post_graphql_mutation(mutation, current_user: current_user) expect(flattened_errors).not_to be_empty end end end context 'when user is admin in admin mode', :enable_admin_mode do let(:current_user) { admin } it_behaves_like 'when runner is created successfully' it_behaves_like 'when model is invalid returns error' end end end context 'when runnerType is PROJECT_TYPE' do let_it_be(:project) { create(:project, namespace: group) } let(:mutation_scope_params) do { runner_type: 'PROJECT_TYPE', project_id: project.to_global_id } end it_behaves_like 'when user does not have permissions' context 'when user has permissions' do context 'when user is group owner' do let(:current_user) { group_owner } it_behaves_like 'when runner is created successfully' it_behaves_like 'when model is invalid returns error' context 'when project_id is missing' do let(:mutation_scope_params) do { runner_type: 'PROJECT_TYPE' } end it 'returns an error' do post_graphql_mutation(mutation, current_user: current_user) expect_graphql_errors_to_include('`project_id` is missing') end end context 'when project_id is malformed' do let(:mutation_scope_params) do { runner_type: 'PROJECT_TYPE', project_id: '' } end it 'returns an error' do post_graphql_mutation(mutation, current_user: current_user) expect_graphql_errors_to_include( "RunnerCreateInput! was provided invalid value for projectId" ) end end context 'when project_id does not exist' do let(:mutation_scope_params) do { runner_type: 'PROJECT_TYPE', project_id: "gid://gitlab/Project/#{non_existing_record_id}" } end it 'returns an error' do post_graphql_mutation(mutation, current_user: current_user) expect_graphql_errors_to_include( 'The resource that you are attempting to access does not exist ' \ "or you don't have permission to perform this action" ) end end end context 'when user is admin in admin mode', :enable_admin_mode do let(:current_user) { admin } it_behaves_like 'when runner is created successfully' it_behaves_like 'when model is invalid returns error' end end end end