diff options
Diffstat (limited to 'spec/graphql')
25 files changed, 404 insertions, 154 deletions
diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index 60b3edfc279..b5c2d4da9ac 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -232,11 +232,7 @@ RSpec.describe GitlabSchema do end end - describe '.parse_gid' do - let_it_be(:global_id) { 'gid://gitlab/TestOne/2147483647' } - - subject(:parse_gid) { described_class.parse_gid(global_id) } - + context 'for gid parsing' do before do test_base = Class.new test_one = Class.new(test_base) @@ -249,66 +245,85 @@ RSpec.describe GitlabSchema do stub_const('TestThree', test_three) end - it 'parses the gid' do - gid = parse_gid + describe '.parse_gid' do + let_it_be(:global_id) { 'gid://gitlab/TestOne/2147483647' } - expect(gid.model_id).to eq '2147483647' - expect(gid.model_class).to eq TestOne - end + subject(:parse_gid) { described_class.parse_gid(global_id) } - context 'when gid is malformed' do - let_it_be(:global_id) { 'malformed://gitlab/TestOne/2147483647' } + it 'parses the gid' do + gid = parse_gid - it 'raises an error' do - expect { parse_gid } - .to raise_error(Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid GitLab ID.") + expect(gid.model_id).to eq '2147483647' + expect(gid.model_class).to eq TestOne end - end - context 'when using expected_type' do - it 'accepts a single type' do - gid = described_class.parse_gid(global_id, expected_type: TestOne) + context 'when gid is malformed' do + let_it_be(:global_id) { 'malformed://gitlab/TestOne/2147483647' } - expect(gid.model_class).to eq TestOne + it 'raises an error' do + expect { parse_gid } + .to raise_error(Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid GitLab ID.") + end end - it 'accepts an ancestor type' do - gid = described_class.parse_gid(global_id, expected_type: TestBase) + context 'when using expected_type' do + it 'accepts a single type' do + gid = described_class.parse_gid(global_id, expected_type: TestOne) - expect(gid.model_class).to eq TestOne - end + expect(gid.model_class).to eq TestOne + end - it 'rejects an unknown type' do - expect { described_class.parse_gid(global_id, expected_type: TestTwo) } - .to raise_error(Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid ID for TestTwo.") - end + it 'accepts an ancestor type' do + gid = described_class.parse_gid(global_id, expected_type: TestBase) - context 'when expected_type is an array' do - subject(:parse_gid) { described_class.parse_gid(global_id, expected_type: [TestOne, TestTwo]) } + expect(gid.model_class).to eq TestOne + end - context 'when global_id is of type TestOne' do - it 'returns an object of an expected type' do - expect(parse_gid.model_class).to eq TestOne - end + it 'rejects an unknown type' do + expect { described_class.parse_gid(global_id, expected_type: TestTwo) } + .to raise_error(Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid ID for TestTwo.") end - context 'when global_id is of type TestTwo' do - let_it_be(:global_id) { 'gid://gitlab/TestTwo/2147483647' } + context 'when expected_type is an array' do + subject(:parse_gid) { described_class.parse_gid(global_id, expected_type: [TestOne, TestTwo]) } - it 'returns an object of an expected type' do - expect(parse_gid.model_class).to eq TestTwo + context 'when global_id is of type TestOne' do + it 'returns an object of an expected type' do + expect(parse_gid.model_class).to eq TestOne + end + end + + context 'when global_id is of type TestTwo' do + let_it_be(:global_id) { 'gid://gitlab/TestTwo/2147483647' } + + it 'returns an object of an expected type' do + expect(parse_gid.model_class).to eq TestTwo + end end - end - context 'when global_id is of type TestThree' do - let_it_be(:global_id) { 'gid://gitlab/TestThree/2147483647' } + context 'when global_id is of type TestThree' do + let_it_be(:global_id) { 'gid://gitlab/TestThree/2147483647' } - it 'rejects an unknown type' do - expect { parse_gid } - .to raise_error(Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid ID for TestOne, TestTwo.") + it 'rejects an unknown type' do + expect { parse_gid } + .to raise_error(Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid ID for TestOne, TestTwo.") + end end end end end + + describe '.parse_gids' do + let_it_be(:global_ids) { %w[gid://gitlab/TestOne/123 gid://gitlab/TestTwo/456] } + + subject(:parse_gids) { described_class.parse_gids(global_ids, expected_type: [TestOne, TestTwo]) } + + it 'parses the gids' do + expect(described_class).to receive(:parse_gid).with('gid://gitlab/TestOne/123', { expected_type: [TestOne, TestTwo] }).and_call_original + expect(described_class).to receive(:parse_gid).with('gid://gitlab/TestTwo/456', { expected_type: [TestOne, TestTwo] }).and_call_original + expect(parse_gids.map(&:model_id)).to eq %w[123 456] + expect(parse_gids.map(&:model_class)).to eq [TestOne, TestTwo] + end + end end end diff --git a/spec/graphql/graphql_triggers_spec.rb b/spec/graphql/graphql_triggers_spec.rb index 5e2ab74a0e5..a4a643582f5 100644 --- a/spec/graphql/graphql_triggers_spec.rb +++ b/spec/graphql/graphql_triggers_spec.rb @@ -32,6 +32,20 @@ RSpec.describe GraphqlTriggers do end end + describe '.issuable_description_updated' do + it 'triggers the issuableDescriptionUpdated subscription' do + work_item = create(:work_item) + + expect(GitlabSchema.subscriptions).to receive(:trigger).with( + 'issuableDescriptionUpdated', + { issuable_id: work_item.to_gid }, + work_item + ).and_call_original + + GraphqlTriggers.issuable_description_updated(work_item) + end + end + describe '.issuable_labels_updated' do it 'triggers the issuableLabelsUpdated subscription' do project = create(:project) @@ -61,4 +75,32 @@ RSpec.describe GraphqlTriggers do GraphqlTriggers.issuable_dates_updated(work_item) end end + + describe '.merge_request_reviewers_updated' do + it 'triggers the mergeRequestReviewersUpdated subscription' do + merge_request = build_stubbed(:merge_request) + + expect(GitlabSchema.subscriptions).to receive(:trigger).with( + 'mergeRequestReviewersUpdated', + { issuable_id: merge_request.to_gid }, + merge_request + ).and_call_original + + GraphqlTriggers.merge_request_reviewers_updated(merge_request) + end + end + + describe '.merge_request_merge_status_updated' do + it 'triggers the mergeRequestMergeStatusUpdated subscription' do + merge_request = build_stubbed(:merge_request) + + expect(GitlabSchema.subscriptions).to receive(:trigger).with( + 'mergeRequestMergeStatusUpdated', + { issuable_id: merge_request.to_gid }, + merge_request + ).and_call_original + + GraphqlTriggers.merge_request_merge_status_updated(merge_request) + end + end end diff --git a/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb b/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb index 412be5f16a4..727db7e2361 100644 --- a/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb +++ b/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Mutations::Ci::JobTokenScope::AddProject do describe '#resolve' do let_it_be(:project) do - create(:project, ci_job_token_scope_enabled: true).tap(&:save!) + create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) end let_it_be(:target_project) { create(:project) } diff --git a/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb b/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb index 0e706ea6e0c..d399e73f394 100644 --- a/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb +++ b/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Mutations::Ci::JobTokenScope::RemoveProject do end describe '#resolve' do - let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } let_it_be(:target_project) { create(:project) } let_it_be(:link) do diff --git a/spec/graphql/mutations/ci/runner/update_spec.rb b/spec/graphql/mutations/ci/runner/update_spec.rb index 39fe2a53a68..ee65be1e085 100644 --- a/spec/graphql/mutations/ci/runner/update_spec.rb +++ b/spec/graphql/mutations/ci/runner/update_spec.rb @@ -45,7 +45,8 @@ RSpec.describe Mutations::Ci::Runner::Update do end context 'when user can update runner', :enable_admin_mode do - let(:admin_user) { create(:user, :admin) } + let_it_be(:admin_user) { create(:user, :admin) } + let(:current_ctx) { { current_user: admin_user } } context 'with valid arguments' do @@ -134,8 +135,7 @@ RSpec.describe Mutations::Ci::Runner::Update do response expect(response[:errors]).to match_array(['user not allowed to assign runner']) - expect(response[:runner]).to be_an_instance_of(Ci::Runner) - expect(response[:runner]).not_to have_attributes(expected_attributes) + expect(response[:runner]).to be_nil expect(runner.reload).not_to have_attributes(expected_attributes) expect(runner.projects).to match_array([project1]) end @@ -164,7 +164,7 @@ RSpec.describe Mutations::Ci::Runner::Update do let(:mutation_params) do { id: runner.to_global_id, - associated_projects: ['gid://gitlab/Project/-1'] + associated_projects: ["gid://gitlab/Project/#{non_existing_record_id}"] } end @@ -191,6 +191,7 @@ RSpec.describe Mutations::Ci::Runner::Update do end it 'returns a descriptive error' do + expect(response[:runner]).to be_nil expect(response[:errors]).to contain_exactly( 'Maximum timeout needs to be at least 10 minutes', 'Tags list can not be empty when runner is not allowed to pick untagged jobs' @@ -202,6 +203,7 @@ RSpec.describe Mutations::Ci::Runner::Update do it 'returns a descriptive error' do mutation_params[:maintenance_note] = '1' * 1025 + expect(response[:runner]).to be_nil expect(response[:errors]).to contain_exactly( 'Maintenance note is too long (maximum is 1024 characters)' ) diff --git a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb index ea74e427dd6..9254d84b29c 100644 --- a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb +++ b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb @@ -37,7 +37,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do let(:args) { {} } it_behaves_like 'responding with an incident timeline errors', - errors: ["Occurred at can't be blank, Note can't be blank, and Note html can't be blank"] + errors: ["Occurred at can't be blank and Timeline text can't be blank"] end end diff --git a/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb index 102d33378c6..7081fb7117e 100644 --- a/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb +++ b/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb @@ -65,7 +65,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do end it 'responds with error' do - expect(resolve).to eq(timeline_event: nil, errors: ["Note can't be blank"]) + expect(resolve).to eq(timeline_event: nil, errors: ["Timeline text can't be blank"]) end end diff --git a/spec/graphql/mutations/namespace/package_settings/update_spec.rb b/spec/graphql/mutations/namespace/package_settings/update_spec.rb index 631e02ff3dc..09ac1c99b10 100644 --- a/spec/graphql/mutations/namespace/package_settings/update_spec.rb +++ b/spec/graphql/mutations/namespace/package_settings/update_spec.rb @@ -26,8 +26,29 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update do RSpec.shared_examples 'updating the namespace package setting' do it_behaves_like 'updating the namespace package setting attributes', - from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT', generic_duplicates_allowed: true, generic_duplicate_exception_regex: 'foo' }, - to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE', generic_duplicates_allowed: false, generic_duplicate_exception_regex: 'bar' } + from: { + maven_duplicates_allowed: true, + maven_duplicate_exception_regex: 'SNAPSHOT', + generic_duplicates_allowed: true, + generic_duplicate_exception_regex: 'foo', + maven_package_requests_forwarding: nil, + lock_maven_package_requests_forwarding: false, + npm_package_requests_forwarding: nil, + lock_npm_package_requests_forwarding: false, + pypi_package_requests_forwarding: nil, + lock_pypi_package_requests_forwarding: false + }, to: { + maven_duplicates_allowed: false, + maven_duplicate_exception_regex: 'RELEASE', + generic_duplicates_allowed: false, + generic_duplicate_exception_regex: 'bar', + maven_package_requests_forwarding: true, + lock_maven_package_requests_forwarding: true, + npm_package_requests_forwarding: true, + lock_npm_package_requests_forwarding: true, + pypi_package_requests_forwarding: true, + lock_pypi_package_requests_forwarding: true + } it_behaves_like 'returning a success' @@ -59,11 +80,19 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update do context 'with existing namespace package setting' do let_it_be(:package_settings) { create(:namespace_package_setting, namespace: namespace) } let_it_be(:params) do - { namespace_path: namespace.full_path, + { + namespace_path: namespace.full_path, maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE', generic_duplicates_allowed: false, - generic_duplicate_exception_regex: 'bar' } + generic_duplicate_exception_regex: 'bar', + maven_package_requests_forwarding: true, + lock_maven_package_requests_forwarding: true, + npm_package_requests_forwarding: true, + lock_npm_package_requests_forwarding: true, + pypi_package_requests_forwarding: true, + lock_pypi_package_requests_forwarding: true + } end where(:user_role, :shared_examples_name) do diff --git a/spec/graphql/mutations/work_items/update_widgets_spec.rb b/spec/graphql/mutations/work_items/update_widgets_spec.rb deleted file mode 100644 index 2e54b81b5c7..00000000000 --- a/spec/graphql/mutations/work_items/update_widgets_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Mutations::WorkItems::UpdateWidgets do - include GraphqlHelpers - - let_it_be(:project) { create(:project) } - let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } } - - let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) } - - describe '#resolve' do - before do - stub_spam_services - end - - context 'when no work item matches the given id' do - let(:current_user) { developer } - let(:gid) { global_id_of(id: non_existing_record_id, model_name: WorkItem.name) } - - it 'raises an error' do - expect { mutation.resolve(id: gid, resolve: true) }.to raise_error( - Gitlab::Graphql::Errors::ResourceNotAvailable, - Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR - ) - end - end - - context 'when user can access the requested work item', :aggregate_failures do - let(:current_user) { developer } - let(:args) { {} } - - let_it_be(:work_item) { create(:work_item, project: project) } - - subject { mutation.resolve(id: work_item.to_global_id, **args) } - - context 'when `:work_items` is disabled for a project' do - let_it_be(:project2) { create(:project) } - - it 'returns an error' do - stub_feature_flags(work_items: project2) # only enable `work_item` for project2 - - expect(subject[:errors]).to contain_exactly('`work_items` feature flag disabled for this project') - end - end - - context 'when resolved with an input for description widget' do - let(:args) { { description_widget: { description: "updated description" } } } - - it 'returns the updated work item' do - expect(subject[:work_item].description).to eq("updated description") - expect(subject[:errors]).to be_empty - end - end - end - end -end diff --git a/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb new file mode 100644 index 00000000000..2a7d0a8171b --- /dev/null +++ b/spec/graphql/resolvers/ci/all_jobs_resolver_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Ci::AllJobsResolver do + include GraphqlHelpers + + let_it_be(:successful_job) { create(:ci_build, :success, name: 'Job One') } + let_it_be(:successful_job_two) { create(:ci_build, :success, name: 'Job Two') } + let_it_be(:failed_job) { create(:ci_build, :failed, name: 'Job Three') } + let_it_be(:pending_job) { create(:ci_build, :pending, name: 'Job Three') } + + let(:args) { {} } + let(:current_user) { create(:admin) } + + subject { resolve_jobs(args) } + + describe '#resolve' do + context 'with authorized user' do + context 'with statuses argument' do + let(:args) { { statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS')] } } + + it { is_expected.to contain_exactly(successful_job, successful_job_two) } + end + + context 'with multiple statuses' do + let(:args) do + { statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS'), + Types::Ci::JobStatusEnum.coerce_isolated_input('FAILED')] } + end + + it { is_expected.to contain_exactly(successful_job, successful_job_two, failed_job) } + end + + context 'without statuses argument' do + it { is_expected.to contain_exactly(successful_job, successful_job_two, failed_job, pending_job) } + end + end + + context 'with unauthorized user' do + let(:current_user) { nil } + + 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 diff --git a/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb b/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb index 1bfd6fbf6b9..59ece15b745 100644 --- a/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do include GraphqlHelpers let_it_be(:current_user) { create(:user) } - let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } specify do expect(described_class).to have_nullable_graphql_type(::Types::Ci::JobTokenScopeType) @@ -21,7 +21,7 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do end it 'returns the same project in the allow list of projects for the Ci Job Token when scope is not enabled' do - allow(project).to receive(:ci_job_token_scope_enabled?).and_return(false) + allow(project).to receive(:ci_outbound_job_token_scope_enabled?).and_return(false) expect(resolve_scope.all_projects).to contain_exactly(project) end @@ -40,7 +40,7 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do context 'when job token scope is disabled' do before do - project.update!(ci_job_token_scope_enabled: false) + project.update!(ci_outbound_job_token_scope_enabled: false) end it 'resolves projects' do diff --git a/spec/graphql/resolvers/project_pipeline_schedules_resolver_spec.rb b/spec/graphql/resolvers/project_pipeline_schedules_resolver_spec.rb new file mode 100644 index 00000000000..159335adf79 --- /dev/null +++ b/spec/graphql/resolvers/project_pipeline_schedules_resolver_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::ProjectPipelineSchedulesResolver do + include GraphqlHelpers + + let_it_be(:developer) { create(:user) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository, public_builds: false) } + + before do + project.add_owner(user) + end + + describe 'With filters' do + let(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: developer) } + + before do + pipeline_schedule.pipelines << build(:ci_pipeline, project: project) + end + + it 'shows active pipeline schedules' do + schedules = resolve_pipeline_schedules + + expect(schedules).to contain_exactly(pipeline_schedule) + end + + it 'shows the inactive pipeline schedules' do + schedules = resolve_pipeline_schedules(args: + { status: ::Types::Ci::PipelineScheduleStatusEnum.values['INACTIVE'].value }) + + expect(schedules).to be_empty + end + end + + def resolve_pipeline_schedules(args: {}) + resolve(described_class, obj: project, ctx: { current_user: user }, args: args) + end +end diff --git a/spec/graphql/resolvers/users/participants_resolver_spec.rb b/spec/graphql/resolvers/users/participants_resolver_spec.rb index 3f04d157410..eb2418b63f4 100644 --- a/spec/graphql/resolvers/users/participants_resolver_spec.rb +++ b/spec/graphql/resolvers/users/participants_resolver_spec.rb @@ -10,18 +10,31 @@ RSpec.describe Resolvers::Users::ParticipantsResolver do let_it_be(:guest) { create(:user) } let_it_be(:project) { create(:project, :public) } let_it_be(:issue) { create(:issue, project: project) } - let_it_be(:note) do - create( - :note, - :system, - :confidential, - project: project, - noteable: issue, - author: create(:user) - ) - end - let_it_be(:note_metadata) { create(:system_note_metadata, note: note) } + let_it_be(:public_note_author) { create(:user) } + let_it_be(:public_reply_author) { create(:user) } + let_it_be(:internal_note_author) { create(:user) } + let_it_be(:internal_reply_author) { create(:user) } + + let_it_be(:public_note) { create(:note, project: project, noteable: issue, author: public_note_author) } + let_it_be(:internal_note) { create(:note, :confidential, project: project, noteable: issue, author: internal_note_author) } + + let_it_be(:public_reply) { create(:note, noteable: issue, in_reply_to: public_note, project: project, author: public_reply_author) } + let_it_be(:internal_reply) { create(:note, :confidential, noteable: issue, in_reply_to: internal_note, project: project, author: internal_reply_author) } + + let_it_be(:note_metadata2) { create(:system_note_metadata, note: public_note) } + + let_it_be(:issue_emoji) { create(:award_emoji, name: 'thumbsup', awardable: issue) } + let_it_be(:note_emoji1) { create(:award_emoji, name: 'thumbsup', awardable: public_note) } + let_it_be(:note_emoji2) { create(:award_emoji, name: 'thumbsup', awardable: internal_note) } + let_it_be(:note_emoji3) { create(:award_emoji, name: 'thumbsup', awardable: public_reply) } + let_it_be(:note_emoji4) { create(:award_emoji, name: 'thumbsup', awardable: internal_reply) } + + let_it_be(:issue_emoji_author) { issue_emoji.user } + let_it_be(:public_note_emoji_author) { note_emoji1.user } + let_it_be(:internal_note_emoji_author) { note_emoji2.user } + let_it_be(:public_reply_emoji_author) { note_emoji3.user } + let_it_be(:internal_reply_emoji_author) { note_emoji4.user } subject(:resolved_items) { resolve(described_class, args: {}, ctx: { current_user: current_user }, obj: issue)&.items } @@ -34,7 +47,16 @@ RSpec.describe Resolvers::Users::ParticipantsResolver do let(:current_user) { nil } it 'returns only publicly visible participants for this user' do - is_expected.to match_array([issue.author]) + is_expected.to match_array( + [ + issue.author, + issue_emoji_author, + public_note_author, + public_note_emoji_author, + public_reply_author, + public_reply_emoji_author + ] + ) end end @@ -42,15 +64,37 @@ RSpec.describe Resolvers::Users::ParticipantsResolver do let(:current_user) { guest } it 'returns only publicly visible participants for this user' do - is_expected.to match_array([issue.author]) + is_expected.to match_array( + [ + issue.author, + issue_emoji_author, + public_note_author, + public_note_emoji_author, + public_reply_author, + public_reply_emoji_author + ] + ) end end - context 'when current user has access to confidential notes' do + context 'when current user has access to internal notes' do let(:current_user) { user } it 'returns all participants for this user' do - is_expected.to match_array([issue.author, note.author]) + is_expected.to match_array( + [ + issue.author, + issue_emoji_author, + public_note_author, + public_note_emoji_author, + public_reply_author, + internal_note_author, + internal_note_emoji_author, + internal_reply_author, + public_reply_emoji_author, + internal_reply_emoji_author + ] + ) end context 'N+1 queries' do @@ -64,9 +108,14 @@ RSpec.describe Resolvers::Users::ParticipantsResolver do it 'does not execute N+1 for project relation' do control_count = ActiveRecord::QueryRecorder.new { query.call } - create(:note, :confidential, project: project, noteable: issue, author: create(:user)) + create(:award_emoji, :upvote, awardable: issue) + internal_note = create(:note, :confidential, project: project, noteable: issue, author: create(:user)) + create(:award_emoji, name: 'thumbsup', awardable: internal_note) + public_note = create(:note, project: project, noteable: issue, author: create(:user)) + create(:award_emoji, name: 'thumbsup', awardable: public_note) - expect { query.call }.not_to exceed_query_limit(control_count) + # 1 extra query per source (3 emojis + 2 notes) to fetch participables collection + expect { query.call }.not_to exceed_query_limit(control_count).with_threshold(5) end it 'does not execute N+1 for system note metadata relation' do diff --git a/spec/graphql/types/ci/job_token_scope_type_spec.rb b/spec/graphql/types/ci/job_token_scope_type_spec.rb index 18f4d762d1e..569b59d6c70 100644 --- a/spec/graphql/types/ci/job_token_scope_type_spec.rb +++ b/spec/graphql/types/ci/job_token_scope_type_spec.rb @@ -12,7 +12,7 @@ RSpec.describe GitlabSchema.types['CiJobTokenScopeType'] do end describe 'query' do - let(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } let_it_be(:current_user) { create(:user) } let(:query) do diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb index b3dee082d1f..ce1558c4097 100644 --- a/spec/graphql/types/ci/job_type_spec.rb +++ b/spec/graphql/types/ci/job_type_spec.rb @@ -13,6 +13,7 @@ RSpec.describe Types::Ci::JobType do active allow_failure artifacts + browse_artifacts_path cancelable commitPath coverage @@ -64,4 +65,16 @@ RSpec.describe Types::Ci::JobType do is_expected.to eq("/#{project.full_path}/-/jobs/#{build.id}") end end + + describe '#browse_artifacts_path' do + subject { resolve_field(:browse_artifacts_path, build, current_user: user, object_type: described_class) } + + let_it_be(:project) { create(:project) } + let(:user) { create(:user) } + let(:build) { create(:ci_build, :artifacts, project: project, user: user) } + + it 'returns the path to browse the artifacts of the job' do + is_expected.to eq("/#{project.full_path}/-/jobs/#{build.id}/artifacts/browse") + end + end end diff --git a/spec/graphql/types/ci/pipeline_schedule_status_enum_spec.rb b/spec/graphql/types/ci/pipeline_schedule_status_enum_spec.rb new file mode 100644 index 00000000000..d271e72b17f --- /dev/null +++ b/spec/graphql/types/ci/pipeline_schedule_status_enum_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::Ci::PipelineScheduleStatusEnum do + specify { expect(described_class.graphql_name ).to eq('PipelineScheduleStatus') } + + it 'exposes the status of a pipeline schedule' do + expect(described_class.values.keys).to match_array(%w[ACTIVE INACTIVE]) + end +end diff --git a/spec/graphql/types/ci/pipeline_schedule_type_spec.rb b/spec/graphql/types/ci/pipeline_schedule_type_spec.rb new file mode 100644 index 00000000000..bf1413ef657 --- /dev/null +++ b/spec/graphql/types/ci/pipeline_schedule_type_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::Ci::PipelineScheduleType do + include GraphqlHelpers + + specify { expect(described_class.graphql_name).to eq('PipelineSchedule') } + specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Ci::PipelineSchedules) } + + it 'exposes the expected fields' do + expected_fields = %i[ + id + description + owner + active + lastPipeline + refForDisplay + refPath + forTag + nextRunAt + realNextRun + cron + cronTimezone + userPermissions + ] + + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/environment_type_spec.rb b/spec/graphql/types/environment_type_spec.rb index ae58fe00af7..2605beac95a 100644 --- a/spec/graphql/types/environment_type_spec.rb +++ b/spec/graphql/types/environment_type_spec.rb @@ -5,13 +5,13 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['Environment'] do specify { expect(described_class.graphql_name).to eq('Environment') } - it 'has the expected fields' do + it 'includes the expected fields' do expected_fields = %w[ name id state metrics_dashboard latest_opened_most_severe_alert path external_url deployments slug createdAt updatedAt autoStopAt autoDeleteAt tier environmentType lastDeployment ] - expect(described_class).to have_graphql_fields(*expected_fields) + expect(described_class).to include_graphql_fields(*expected_fields) end specify { expect(described_class).to require_graphql_authorizations(:read_environment) } diff --git a/spec/graphql/types/namespace/package_settings_type_spec.rb b/spec/graphql/types/namespace/package_settings_type_spec.rb index f63a0a7010f..5039f2d6153 100644 --- a/spec/graphql/types/namespace/package_settings_type_spec.rb +++ b/spec/graphql/types/namespace/package_settings_type_spec.rb @@ -14,4 +14,24 @@ RSpec.describe GitlabSchema.types['PackageSettings'] do it { is_expected.to have_graphql_type(Types::UntrustedRegexp) } end + + it 'includes package setting fields' do + expected_fields = %w[ + maven_duplicates_allowed + maven_duplicate_exception_regex + generic_duplicates_allowed + generic_duplicate_exception_regex + maven_package_requests_forwarding + lock_maven_package_requests_forwarding + npm_package_requests_forwarding + lock_npm_package_requests_forwarding + pypi_package_requests_forwarding + lock_pypi_package_requests_forwarding + maven_package_requests_forwarding_locked + npm_package_requests_forwarding_locked + pypi_package_requests_forwarding_locked + ] + + expect(described_class).to include_graphql_fields(*expected_fields) + end end diff --git a/spec/graphql/types/packages/tag_type_spec.rb b/spec/graphql/types/packages/tag_type_spec.rb index 83b705157d8..ea0801c7a0f 100644 --- a/spec/graphql/types/packages/tag_type_spec.rb +++ b/spec/graphql/types/packages/tag_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['PackageTag'] do it 'includes all the package tag fields' do expected_fields = %w[ - id name created_at updated_at + id name created_at updated_at ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/permission_types/ci/pipeline_schedule_type_spec.rb b/spec/graphql/types/permission_types/ci/pipeline_schedule_type_spec.rb new file mode 100644 index 00000000000..5929d22b202 --- /dev/null +++ b/spec/graphql/types/permission_types/ci/pipeline_schedule_type_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::Ci::PipelineScheduleType do + specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Ci::PipelineSchedules) } +end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 617cbdb07fe..b435f3ed5ff 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -36,7 +36,7 @@ RSpec.describe GitlabSchema.types['Project'] do cluster_agent cluster_agents agent_configurations ci_template timelogs merge_commit_template squash_commit_template work_item_types recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables - timelog_categories fork_targets branch_rules ci_config_variables + timelog_categories fork_targets branch_rules ci_config_variables pipeline_schedules ] expect(described_class).to include_graphql_fields(*expected_fields) @@ -508,20 +508,6 @@ RSpec.describe GitlabSchema.types['Project'] do it { is_expected.to have_graphql_resolver(Resolvers::Ci::JobTokenScopeResolver) } end - describe 'branch_rules field' do - subject { described_class.fields['branchRules'] } - - let(:br_resolver) { Resolvers::Projects::BranchRulesResolver } - - specify do - is_expected.to have_graphql_type( - Types::Projects::BranchRuleType.connection_type - ) - end - - specify { is_expected.to have_graphql_resolver(br_resolver) } - end - describe 'agent_configurations' do let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } diff --git a/spec/graphql/types/branch_rule_type_spec.rb b/spec/graphql/types/projects/branch_rule_type_spec.rb index 277901f00bf..119ecf8a097 100644 --- a/spec/graphql/types/branch_rule_type_spec.rb +++ b/spec/graphql/types/projects/branch_rule_type_spec.rb @@ -10,6 +10,7 @@ RSpec.describe GitlabSchema.types['BranchRule'] do let(:fields) do %i[ name + isDefault branch_protection created_at updated_at @@ -18,5 +19,5 @@ RSpec.describe GitlabSchema.types['BranchRule'] do specify { is_expected.to require_graphql_authorizations(:read_protected_branch) } - specify { is_expected.to have_graphql_fields(fields) } + specify { is_expected.to have_graphql_fields(fields).at_least } end diff --git a/spec/graphql/types/subscription_type_spec.rb b/spec/graphql/types/subscription_type_spec.rb index 860cbbf0c15..c23a14deaf3 100644 --- a/spec/graphql/types/subscription_type_spec.rb +++ b/spec/graphql/types/subscription_type_spec.rb @@ -8,9 +8,11 @@ RSpec.describe GitlabSchema.types['Subscription'] do issuable_assignees_updated issue_crm_contacts_updated issuable_title_updated + issuable_description_updated issuable_labels_updated issuable_dates_updated merge_request_reviewers_updated + merge_request_merge_status_updated ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/work_items/widgets/labels_update_input_type_spec.rb b/spec/graphql/types/work_items/widgets/labels_update_input_type_spec.rb new file mode 100644 index 00000000000..3e5f40bde02 --- /dev/null +++ b/spec/graphql/types/work_items/widgets/labels_update_input_type_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Types::WorkItems::Widgets::LabelsUpdateInputType do + it { expect(described_class.graphql_name).to eq('WorkItemWidgetLabelsUpdateInput') } + + it { expect(described_class.arguments.keys).to contain_exactly('addLabelIds', 'removeLabelIds') } +end |