diff options
Diffstat (limited to 'spec/graphql')
29 files changed, 535 insertions, 78 deletions
diff --git a/spec/graphql/features/authorization_spec.rb b/spec/graphql/features/authorization_spec.rb index faf19104731..514f63a6f5a 100644 --- a/spec/graphql/features/authorization_spec.rb +++ b/spec/graphql/features/authorization_spec.rb @@ -326,7 +326,7 @@ RSpec.describe 'DeclarativePolicy authorization in GraphQL ' do let!(:other_project) { create(:project, :private) } let!(:visible_issues) { create_list(:issue, 2, project: visible_project) } let!(:other_issues) { create_list(:issue, 2, project: other_project) } - let!(:user) { visible_project.owner } + let!(:user) { visible_project.first_owner } let(:issue_type) do type_factory do |type| diff --git a/spec/graphql/graphql_triggers_spec.rb b/spec/graphql/graphql_triggers_spec.rb index 0b53c633077..2d83edca363 100644 --- a/spec/graphql/graphql_triggers_spec.rb +++ b/spec/graphql/graphql_triggers_spec.rb @@ -17,4 +17,18 @@ RSpec.describe GraphqlTriggers do GraphqlTriggers.issuable_assignees_updated(issue) end end + + describe '.issuable_title_updated' do + it 'triggers the issuableTitleUpdated subscription' do + work_item = create(:work_item) + + expect(GitlabSchema.subscriptions).to receive(:trigger).with( + 'issuableTitleUpdated', + { issuable_id: work_item.to_gid }, + work_item + ).and_call_original + + GraphqlTriggers.issuable_title_updated(work_item) + end + end end diff --git a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb index 8ec99070c91..ea5e21ec4b8 100644 --- a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb +++ b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Mutations::AlertManagement::Alerts::Todo::Create do let_it_be(:alert) { create(:alert_management_alert) } let_it_be(:project) { alert.project } - let(:current_user) { project.owner } + let(:current_user) { project.first_owner } let(:args) { { project_path: project.full_path, iid: alert.iid } } diff --git a/spec/graphql/mutations/ci/runner/delete_spec.rb b/spec/graphql/mutations/ci/runner/delete_spec.rb index 9f30c95edd5..b53ee30f826 100644 --- a/spec/graphql/mutations/ci/runner/delete_spec.rb +++ b/spec/graphql/mutations/ci/runner/delete_spec.rb @@ -11,9 +11,7 @@ RSpec.describe Mutations::Ci::Runner::Delete do let(:current_ctx) { { current_user: user } } let(:mutation_params) do - { - id: runner.to_global_id - } + { id: runner.to_global_id } end specify { expect(described_class).to require_graphql_authorizations(:delete_runner) } @@ -57,6 +55,10 @@ RSpec.describe Mutations::Ci::Runner::Delete do it 'deletes runner' do mutation_params[:id] = project_runner.to_global_id + expect_next_instance_of(::Ci::UnregisterRunnerService, project_runner) do |service| + expect(service).to receive(:execute).once.and_call_original + end + expect { subject }.to change { Ci::Runner.count }.by(-1) expect(subject[:errors]).to be_empty end @@ -73,6 +75,9 @@ RSpec.describe Mutations::Ci::Runner::Delete do it 'does not delete project runner' do mutation_params[:id] = two_projects_runner.to_global_id + allow_next_instance_of(::Ci::UnregisterRunnerService) do |service| + expect(service).not_to receive(:execute).once + end expect { subject }.not_to change { Ci::Runner.count } expect(subject[:errors]).to contain_exactly("Runner #{two_projects_runner.to_global_id} associated with more than one project") end @@ -84,6 +89,10 @@ RSpec.describe Mutations::Ci::Runner::Delete do let(:current_ctx) { { current_user: admin_user } } it 'deletes runner' do + expect_next_instance_of(::Ci::UnregisterRunnerService, runner) do |service| + expect(service).to receive(:execute).once.and_call_original + end + expect { subject }.to change { Ci::Runner.count }.by(-1) expect(subject[:errors]).to be_empty end diff --git a/spec/graphql/mutations/issues/create_spec.rb b/spec/graphql/mutations/issues/create_spec.rb index 825d04ff827..e3094e84703 100644 --- a/spec/graphql/mutations/issues/create_spec.rb +++ b/spec/graphql/mutations/issues/create_spec.rb @@ -121,7 +121,7 @@ RSpec.describe Mutations::Issues::Create do end context 'when creating an issue as owner' do - let_it_be(:user) { project.owner } + let_it_be(:user) { project.first_owner } before do mutation_params.merge!(special_params) diff --git a/spec/graphql/resolvers/ci/project_pipeline_counts_resolver_spec.rb b/spec/graphql/resolvers/ci/project_pipeline_counts_resolver_spec.rb new file mode 100644 index 00000000000..07b4a5509b2 --- /dev/null +++ b/spec/graphql/resolvers/ci/project_pipeline_counts_resolver_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Ci::ProjectPipelineCountsResolver do + include GraphqlHelpers + + let(:current_user) { create(:user) } + + let_it_be(:project) { create(:project, :private) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) } + let_it_be(:success_pipeline) { create(:ci_pipeline, :success, project: project) } + let_it_be(:ref_pipeline) { create(:ci_pipeline, project: project, ref: 'awesome-feature') } + let_it_be(:sha_pipeline) { create(:ci_pipeline, :running, project: project, sha: 'deadbeef') } + let_it_be(:on_demand_dast_scan) { create(:ci_pipeline, :success, project: project, source: 'ondemand_dast_scan') } + + before do + project.add_developer(current_user) + end + + describe '#resolve' do + it 'counts pipelines' do + expect(resolve_pipeline_counts).to have_attributes( + all: 6, + finished: 3, + running: 1, + pending: 2 + ) + end + + it 'counts by ref' do + expect(resolve_pipeline_counts(ref: "awesome-feature")).to have_attributes( + all: 1, + finished: 0, + running: 0, + pending: 1 + ) + end + + it 'counts by sha' do + expect(resolve_pipeline_counts(sha: "deadbeef")).to have_attributes( + all: 1, + finished: 0, + running: 1, + pending: 0 + ) + end + + it 'counts by source' do + expect(resolve_pipeline_counts(source: "ondemand_dast_scan")).to have_attributes( + all: 1, + finished: 1, + running: 0, + pending: 0 + ) + end + end + + def resolve_pipeline_counts(args = {}, context = { current_user: current_user }) + resolve(described_class, obj: project, args: args, ctx: context) + end +end diff --git a/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb new file mode 100644 index 00000000000..53b673e255b --- /dev/null +++ b/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Ci::RunnerJobsResolver do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :repository) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be(:irrelevant_pipeline) { create(:ci_pipeline, project: project) } + + let!(:build_one) { create(:ci_build, :success, name: 'Build One', runner: runner, pipeline: pipeline) } + let!(:build_two) { create(:ci_build, :success, name: 'Build Two', runner: runner, pipeline: pipeline) } + let!(:build_three) { create(:ci_build, :failed, name: 'Build Three', runner: runner, pipeline: pipeline) } + let!(:irrelevant_build) { create(:ci_build, name: 'Irrelevant Build', pipeline: irrelevant_pipeline)} + + let(:args) { {} } + let(:runner) { create(:ci_runner, :project, projects: [project]) } + + subject { resolve_jobs(args) } + + describe '#resolve' do + context 'with authorized user', :enable_admin_mode do + let(:current_user) { create(:user, :admin) } + + context 'with statuses argument' do + let(:args) { { statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS')] } } + + it { is_expected.to contain_exactly(build_one, build_two) } + end + + context 'without statuses argument' do + it { is_expected.to contain_exactly(build_one, build_two, build_three) } + end + end + + context 'with unauthorized user' do + let(:current_user) { nil } + + it { is_expected.to be_nil } + end + end + + private + + def resolve_jobs(args = {}, context = { current_user: current_user }) + resolve(described_class, obj: runner, args: args, ctx: context) + end +end diff --git a/spec/graphql/resolvers/ci/runners_resolver_spec.rb b/spec/graphql/resolvers/ci/runners_resolver_spec.rb index df6490df915..9251fbf24d9 100644 --- a/spec/graphql/resolvers/ci/runners_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/runners_resolver_spec.rb @@ -43,34 +43,99 @@ RSpec.describe Resolvers::Ci::RunnersResolver do # Only thing we can do is to verify that args from the resolver is correctly transformed to params of the Finder and we return the Finder's result back. describe 'Allowed query arguments' do let(:finder) { instance_double(::Ci::RunnersFinder) } - let(:args) do - { - active: true, - status: 'active', - type: :instance_type, - tag_list: ['active_runner'], - search: 'abc', - sort: :contacted_asc - } + + context 'with active filter' do + let(:args) do + { + active: true, + status: 'active', + type: :instance_type, + tag_list: ['active_runner'], + search: 'abc', + sort: :contacted_asc + } + end + + let(:expected_params) do + { + active: true, + status_status: 'active', + type_type: :instance_type, + tag_name: ['active_runner'], + preload: { tag_name: nil }, + search: 'abc', + sort: 'contacted_asc' + } + end + + it 'calls RunnersFinder with expected arguments' do + expect(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder) + allow(finder).to receive(:execute).once.and_return([:execute_return_value]) + + expect(subject.items.to_a).to eq([:execute_return_value]) + end + end + + context 'with both active and paused filter' do + let(:args) do + { + active: true, + paused: true + } + end + + let(:expected_params) do + { + active: false, + preload: { tag_name: nil } + } + end + + it 'calls RunnersFinder with expected arguments' do + expect(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder) + allow(finder).to receive(:execute).once.and_return([:execute_return_value]) + + expect(subject.items.to_a).to eq([:execute_return_value]) + end end - let(:expected_params) do - { - active: true, - status_status: 'active', - type_type: :instance_type, - tag_name: ['active_runner'], - preload: { tag_name: nil }, - search: 'abc', - sort: 'contacted_asc' - } + context 'with paused filter' do + let(:args) do + { paused: true } + end + + let(:expected_params) do + { + active: false, + preload: { tag_name: nil } + } + end + + it 'calls RunnersFinder with expected arguments' do + expect(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder) + allow(finder).to receive(:execute).once.and_return([:execute_return_value]) + + expect(subject.items.to_a).to eq([:execute_return_value]) + end end - it 'calls RunnersFinder with expected arguments' do - allow(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder) - allow(finder).to receive(:execute).once.and_return([:execute_return_value]) + context 'with neither paused or active filters' do + let(:args) do + {} + end + + let(:expected_params) do + { + preload: { tag_name: nil } + } + end + + it 'calls RunnersFinder with expected arguments' do + expect(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder) + allow(finder).to receive(:execute).once.and_return([:execute_return_value]) - expect(subject.items.to_a).to eq([:execute_return_value]) + expect(subject.items.to_a).to eq([:execute_return_value]) + end end end end diff --git a/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb b/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb index 9b54d466681..866f4ce7b5a 100644 --- a/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb +++ b/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb @@ -11,7 +11,7 @@ RSpec.describe Resolvers::Clusters::AgentTokensResolver do describe '#resolve' do let(:agent) { create(:cluster_agent) } - let(:user) { create(:user, maintainer_projects: [agent.project]) } + let(:user) { create(:user, developer_projects: [agent.project]) } let(:ctx) { Hash(current_user: user) } let!(:matching_token1) { create(:cluster_agent_token, agent: agent, last_used_at: 5.days.ago) } @@ -33,7 +33,11 @@ RSpec.describe Resolvers::Clusters::AgentTokensResolver do end context 'user does not have permission' do - let(:user) { create(:user, developer_projects: [agent.project]) } + let(:user) { create(:user) } + + before do + agent.project.add_reporter(user) + end it { is_expected.to be_empty } end diff --git a/spec/graphql/resolvers/clusters/agents_resolver_spec.rb b/spec/graphql/resolvers/clusters/agents_resolver_spec.rb index 70f40748e1d..152d7fa22c4 100644 --- a/spec/graphql/resolvers/clusters/agents_resolver_spec.rb +++ b/spec/graphql/resolvers/clusters/agents_resolver_spec.rb @@ -15,10 +15,14 @@ RSpec.describe Resolvers::Clusters::AgentsResolver do describe '#resolve' do let_it_be(:project) { create(:project) } - let_it_be(:maintainer) { create(:user, maintainer_projects: [project]) } - let_it_be(:developer) { create(:user, developer_projects: [project]) } + let_it_be(:maintainer) { create(:user, developer_projects: [project]) } + let_it_be(:reporter) { create(:user) } let_it_be(:agents) { create_list(:cluster_agent, 2, project: project) } + before do + project.add_reporter(reporter) + end + let(:ctx) { { current_user: current_user } } subject { resolve_agents } @@ -32,7 +36,7 @@ RSpec.describe Resolvers::Clusters::AgentsResolver do end context 'the current user does not have access to clusters' do - let(:current_user) { developer } + let(:current_user) { reporter } it 'returns an empty result' do expect(subject).to be_empty diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb index 1d0eac30a23..e4eaeb9bc3c 100644 --- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb +++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb @@ -321,7 +321,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do end describe 'sorting' do - let(:mrs) do + let_it_be(:mrs) do [ merge_request_with_milestone, merge_request_6, merge_request_5, merge_request_4, merge_request_3, merge_request_2, merge_request_1 @@ -363,28 +363,44 @@ RSpec.describe Resolvers::MergeRequestsResolver do def merged_at(mr) nils_last(mr.metrics.merged_at) end + end + + context 'when sorting by closed at' do + before do + merge_request_1.metrics.update!(latest_closed_at: 10.days.ago) + merge_request_3.metrics.update!(latest_closed_at: 5.days.ago) + end + + it 'sorts merge requests ascending' do + expect(resolve_mr(project, sort: :closed_at_asc)) + .to match_array(mrs) + .and be_sorted(->(mr) { [closed_at(mr), -mr.id] }) + end + + it 'sorts merge requests descending' do + expect(resolve_mr(project, sort: :closed_at_desc)) + .to match_array(mrs) + .and be_sorted(->(mr) { [-closed_at(mr), -mr.id] }) + end + + def closed_at(mr) + nils_last(mr.metrics.latest_closed_at) + end + end + + context 'when sorting by title' do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:mr1) { create(:merge_request, :unique_branches, title: 'foo', source_project: project) } + let_it_be(:mr2) { create(:merge_request, :unique_branches, title: 'bar', source_project: project) } + let_it_be(:mr3) { create(:merge_request, :unique_branches, title: 'baz', source_project: project) } + let_it_be(:mr4) { create(:merge_request, :unique_branches, title: 'Baz 2', source_project: project) } + + it 'sorts issues ascending' do + expect(resolve_mr(project, sort: :title_asc).to_a).to eq [mr2, mr3, mr4, mr1] + end - context 'when sorting by closed at' do - before do - merge_request_1.metrics.update!(latest_closed_at: 10.days.ago) - merge_request_3.metrics.update!(latest_closed_at: 5.days.ago) - end - - it 'sorts merge requests ascending' do - expect(resolve_mr(project, sort: :closed_at_asc)) - .to match_array(mrs) - .and be_sorted(->(mr) { [closed_at(mr), -mr.id] }) - end - - it 'sorts merge requests descending' do - expect(resolve_mr(project, sort: :closed_at_desc)) - .to match_array(mrs) - .and be_sorted(->(mr) { [-closed_at(mr), -mr.id] }) - end - - def closed_at(mr) - nils_last(mr.metrics.latest_closed_at) - end + it 'sorts issues descending' do + expect(resolve_mr(project, sort: :title_desc).to_a).to eq [mr1, mr4, mr3, mr2] end end end diff --git a/spec/graphql/resolvers/package_details_resolver_spec.rb b/spec/graphql/resolvers/package_details_resolver_spec.rb index d6acb31d4e3..c8ee489a034 100644 --- a/spec/graphql/resolvers/package_details_resolver_spec.rb +++ b/spec/graphql/resolvers/package_details_resolver_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Resolvers::PackageDetailsResolver do include GraphqlHelpers let_it_be_with_reload(:project) { create(:project) } - let_it_be(:user) { project.owner } + let_it_be(:user) { project.first_owner } let_it_be(:package) { create(:composer_package, project: project) } describe '#resolve' do diff --git a/spec/graphql/resolvers/package_pipelines_resolver_spec.rb b/spec/graphql/resolvers/package_pipelines_resolver_spec.rb index d48d4d8ae01..892dc641201 100644 --- a/spec/graphql/resolvers/package_pipelines_resolver_spec.rb +++ b/spec/graphql/resolvers/package_pipelines_resolver_spec.rb @@ -8,15 +8,16 @@ RSpec.describe Resolvers::PackagePipelinesResolver do let_it_be_with_reload(:package) { create(:package) } let_it_be(:pipelines) { create_list(:ci_pipeline, 3, project: package.project) } - let(:user) { package.project.owner } + let(:user) { package.project.first_owner } let(:args) { {} } describe '#resolve' do subject { resolve(described_class, obj: package, args: args, ctx: { current_user: user }) } before do - package.pipelines = pipelines - package.save! + pipelines.each do |pipeline| + create(:package_build_info, package: package, pipeline: pipeline) + end end it { is_expected.to contain_exactly(*pipelines) } diff --git a/spec/graphql/resolvers/recent_boards_resolver_spec.rb b/spec/graphql/resolvers/recent_boards_resolver_spec.rb new file mode 100644 index 00000000000..1afdcd42b4f --- /dev/null +++ b/spec/graphql/resolvers/recent_boards_resolver_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::RecentBoardsResolver do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + + shared_examples_for 'group and project recent boards resolver' do + let_it_be(:board1) { create(:board, name: 'One', resource_parent: board_parent) } + let_it_be(:board2) { create(:board, name: 'Two', resource_parent: board_parent) } + + before do + [board1, board2].each { |board| visit_board(board, board_parent) } + end + + it 'calls ::Boards::VisitsFinder' do + expect_any_instance_of(::Boards::VisitsFinder) do |finder| + expect(finder).to receive(:latest) + end + + resolve_recent_boards + end + + it 'avoids N+1 queries' do + control = ActiveRecord::QueryRecorder.new { resolve_recent_boards } + + board3 = create(:board, resource_parent: board_parent) + visit_board(board3, board_parent) + + expect { resolve_recent_boards(args: {}) }.not_to exceed_query_limit(control) + end + + it 'returns most recent visited boards' do + expect(resolve_recent_boards).to match_array [board2, board1] + end + + it 'returns a set number of boards' do + stub_const('Board::RECENT_BOARDS_SIZE', 1) + + expect(resolve_recent_boards).to match_array [board2] + end + end + + describe '#resolve' do + context 'when there is no parent' do + let_it_be(:board_parent) { nil } + + it 'returns none if parent is nil' do + expect(resolve_recent_boards).to eq(Board.none) + end + end + + context 'when project boards' do + let_it_be(:board_parent) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) } + + it_behaves_like 'group and project recent boards resolver' + end + + context 'when group boards' do + let_it_be(:board_parent) { create(:group) } + + it_behaves_like 'group and project recent boards resolver' + end + end + + def resolve_recent_boards(args: {}) + resolve(described_class, obj: board_parent, args: args, ctx: { current_user: user }) + end + + def visit_board(board, parent) + if parent.is_a?(Group) + create(:board_group_recent_visit, group: parent, board: board, user: user) + else + create(:board_project_recent_visit, project: parent, board: board, user: user) + end + end +end diff --git a/spec/graphql/types/ci/pipeline_counts_type_spec.rb b/spec/graphql/types/ci/pipeline_counts_type_spec.rb new file mode 100644 index 00000000000..7fdb286d253 --- /dev/null +++ b/spec/graphql/types/ci/pipeline_counts_type_spec.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['PipelineCounts'] do + include GraphqlHelpers + + let(:current_user) { create(:user) } + + let_it_be(:project) { create(:project, :private) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) } + let_it_be(:success_pipeline) { create(:ci_pipeline, :success, project: project) } + let_it_be(:ref_pipeline) { create(:ci_pipeline, project: project, ref: 'awesome-feature') } + let_it_be(:sha_pipeline) { create(:ci_pipeline, :running, project: project, sha: 'deadbeef') } + let_it_be(:on_demand_dast_scan) { create(:ci_pipeline, :success, project: project, source: 'ondemand_dast_scan') } + + before do + project.add_developer(current_user) + end + + specify { expect(described_class.graphql_name).to eq('PipelineCounts') } + + it 'has the expected fields' do + expected_fields = %w[ + all + finished + pending + running + ] + + expect(described_class).to include_graphql_fields(*expected_fields) + end + + shared_examples 'pipeline counts query' do |args: "", expected_counts:| + let_it_be(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + pipelineCounts#{args} { + all + finished + pending + running + } + } + } + ) + end + + subject { GitlabSchema.execute(query, context: { current_user: current_user }).as_json } + + it 'returns pipeline counts' do + actual_counts = subject.dig('data', 'project', 'pipelineCounts') + + expect(actual_counts).to eq(expected_counts) + end + end + + it_behaves_like "pipeline counts query", args: "", expected_counts: { + "all" => 6, + "finished" => 3, + "pending" => 2, + "running" => 1 + } + + it_behaves_like "pipeline counts query", args: '(ref: "awesome-feature")', expected_counts: { + "all" => 1, + "finished" => 0, + "pending" => 1, + "running" => 0 + } + + it_behaves_like "pipeline counts query", args: '(sha: "deadbeef")', expected_counts: { + "all" => 1, + "finished" => 0, + "pending" => 0, + "running" => 1 + } + + it_behaves_like "pipeline counts query", args: '(source: "ondemand_dast_scan")', expected_counts: { + "all" => 1, + "finished" => 1, + "pending" => 0, + "running" => 0 + } +end diff --git a/spec/graphql/types/ci/runner_type_spec.rb b/spec/graphql/types/ci/runner_type_spec.rb index 43d8b585d6b..7697cd0ef79 100644 --- a/spec/graphql/types/ci/runner_type_spec.rb +++ b/spec/graphql/types/ci/runner_type_spec.rb @@ -9,9 +9,10 @@ RSpec.describe GitlabSchema.types['CiRunner'] do it 'contains attributes related to a runner' do expected_fields = %w[ - id description created_at contacted_at maximum_timeout access_level active status + id description created_at contacted_at maximum_timeout access_level active paused status version short_sha revision locked run_untagged ip_address runner_type tag_list project_count job_count admin_url edit_admin_url user_permissions executor_name + groups projects jobs token_expires_at ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/clusters/agent_activity_event_type_spec.rb b/spec/graphql/types/clusters/agent_activity_event_type_spec.rb index 7773bad749d..cae75485846 100644 --- a/spec/graphql/types/clusters/agent_activity_event_type_spec.rb +++ b/spec/graphql/types/clusters/agent_activity_event_type_spec.rb @@ -6,6 +6,6 @@ RSpec.describe GitlabSchema.types['ClusterAgentActivityEvent'] do let(:fields) { %i[recorded_at kind level user agent_token] } it { expect(described_class.graphql_name).to eq('ClusterAgentActivityEvent') } - it { expect(described_class).to require_graphql_authorizations(:admin_cluster) } + it { expect(described_class).to require_graphql_authorizations(:read_cluster) } it { expect(described_class).to have_graphql_fields(fields) } end diff --git a/spec/graphql/types/clusters/agent_token_type_spec.rb b/spec/graphql/types/clusters/agent_token_type_spec.rb index 3f0720cb4b5..1ca6d690c80 100644 --- a/spec/graphql/types/clusters/agent_token_type_spec.rb +++ b/spec/graphql/types/clusters/agent_token_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['ClusterAgentToken'] do it { expect(described_class.graphql_name).to eq('ClusterAgentToken') } - it { expect(described_class).to require_graphql_authorizations(:admin_cluster) } + it { expect(described_class).to require_graphql_authorizations(:read_cluster) } it { expect(described_class).to have_graphql_fields(fields) } end diff --git a/spec/graphql/types/clusters/agent_type_spec.rb b/spec/graphql/types/clusters/agent_type_spec.rb index a1e5952bf73..3f4faccf15d 100644 --- a/spec/graphql/types/clusters/agent_type_spec.rb +++ b/spec/graphql/types/clusters/agent_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['ClusterAgent'] do it { expect(described_class.graphql_name).to eq('ClusterAgent') } - it { expect(described_class).to require_graphql_authorizations(:admin_cluster) } + it { expect(described_class).to require_graphql_authorizations(:read_cluster) } it { expect(described_class).to have_graphql_fields(fields) } end diff --git a/spec/graphql/types/global_id_type_spec.rb b/spec/graphql/types/global_id_type_spec.rb index 4efa3018dad..e7e69cfad9e 100644 --- a/spec/graphql/types/global_id_type_spec.rb +++ b/spec/graphql/types/global_id_type_spec.rb @@ -191,7 +191,7 @@ RSpec.describe Types::GlobalIDType do describe 'executing against the schema' do let(:query_result) do - context = { current_user: issue.project.owner } + context = { current_user: issue.project.first_owner } variables = { 'id' => gid } run_with_clean_state(query, context: context, variables: variables).to_h diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb index 0ba322a100a..82703948cea 100644 --- a/spec/graphql/types/group_type_spec.rb +++ b/spec/graphql/types/group_type_spec.rb @@ -23,6 +23,7 @@ RSpec.describe GitlabSchema.types['Group'] do dependency_proxy_blob_count dependency_proxy_total_size dependency_proxy_image_prefix dependency_proxy_image_ttl_policy shared_runners_setting timelogs organizations contacts work_item_types + recent_issue_boards ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/issuable_type_spec.rb b/spec/graphql/types/issuable_type_spec.rb index 992a58f524b..cb18bbe2eab 100644 --- a/spec/graphql/types/issuable_type_spec.rb +++ b/spec/graphql/types/issuable_type_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['Issuable'] do it 'returns possible types' do - expect(described_class.possible_types).to include(Types::IssueType, Types::MergeRequestType) + expect(described_class.possible_types).to include(Types::IssueType, Types::MergeRequestType, Types::WorkItemType) end describe '.resolve_type' do @@ -16,6 +16,10 @@ RSpec.describe GitlabSchema.types['Issuable'] do expect(described_class.resolve_type(build(:merge_request), {})).to eq(Types::MergeRequestType) end + it 'resolves work items' do + expect(described_class.resolve_type(build(:work_item), {})).to eq(Types::WorkItemType) + end + it 'raises an error for invalid types' do expect { described_class.resolve_type(build(:user), {}) }.to raise_error 'Unsupported issuable type' end diff --git a/spec/graphql/types/member_interface_spec.rb b/spec/graphql/types/member_interface_spec.rb index 11fd09eb335..8ecaaa46bed 100644 --- a/spec/graphql/types/member_interface_spec.rb +++ b/spec/graphql/types/member_interface_spec.rb @@ -12,6 +12,7 @@ RSpec.describe Types::MemberInterface do updated_at expires_at user + merge_request_interaction ] expect(described_class).to have_graphql_fields(*expected_fields) @@ -40,4 +41,16 @@ RSpec.describe Types::MemberInterface do end end end + + describe '#merge_request_interaction' do + subject { described_class.fields['mergeRequestInteraction'] } + + it 'returns the correct type' do + is_expected.to have_graphql_type(Types::UserMergeRequestInteractionType) + end + + it 'has the correct arguments' do + expect(subject.arguments).to have_key('id') + end + end end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 961e12288d4..7433d465b38 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -25,7 +25,7 @@ RSpec.describe GitlabSchema.types['Project'] do only_allow_merge_if_pipeline_succeeds request_access_enabled only_allow_merge_if_all_discussions_are_resolved printing_merge_request_link_enabled namespace group statistics repository merge_requests merge_request issues - issue milestones pipelines removeSourceBranchAfterMerge sentryDetailedError snippets + issue milestones pipelines removeSourceBranchAfterMerge pipeline_counts sentryDetailedError snippets grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments environment boards jira_import_status jira_imports services releases release alert_management_alerts alert_management_alert alert_management_alert_status_counts @@ -35,6 +35,7 @@ RSpec.describe GitlabSchema.types['Project'] do pipeline_analytics squash_read_only sast_ci_configuration 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 ] expect(described_class).to include_graphql_fields(*expected_fields) @@ -299,6 +300,8 @@ RSpec.describe GitlabSchema.types['Project'] do :merged_before, :created_after, :created_before, + :updated_after, + :updated_before, :author_username, :assignee_username, :reviewer_username, @@ -309,6 +312,13 @@ RSpec.describe GitlabSchema.types['Project'] do end end + describe 'pipelineCounts field' do + subject { described_class.fields['pipelineCounts'] } + + it { is_expected.to have_graphql_type(Types::Ci::PipelineCountsType) } + it { is_expected.to have_graphql_resolver(Resolvers::Ci::ProjectPipelineCountsResolver) } + end + describe 'snippets field' do subject { described_class.fields['snippets'] } diff --git a/spec/graphql/types/repository/blob_type_spec.rb b/spec/graphql/types/repository/blob_type_spec.rb index 8d845e5d814..565341d15b9 100644 --- a/spec/graphql/types/repository/blob_type_spec.rb +++ b/spec/graphql/types/repository/blob_type_spec.rb @@ -29,6 +29,8 @@ RSpec.describe Types::Repository::BlobType do :blame_path, :history_path, :permalink_path, + :environment_formatted_external_url, + :environment_external_url_for_route_map, :code_owners, :simple_viewer, :rich_viewer, @@ -39,7 +41,8 @@ RSpec.describe Types::Repository::BlobType do :ide_edit_path, :external_storage_url, :fork_and_edit_path, - :ide_fork_and_edit_path + :ide_fork_and_edit_path, + :language ) end end diff --git a/spec/graphql/types/root_storage_statistics_type_spec.rb b/spec/graphql/types/root_storage_statistics_type_spec.rb index 4fef8f6eafd..7818be6ee02 100644 --- a/spec/graphql/types/root_storage_statistics_type_spec.rb +++ b/spec/graphql/types/root_storage_statistics_type_spec.rb @@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['RootStorageStatistics'] do it 'has all the required fields' do expect(described_class).to have_graphql_fields(:storage_size, :repository_size, :lfs_objects_size, :build_artifacts_size, :packages_size, :wiki_size, :snippets_size, - :pipeline_artifacts_size, :uploads_size) + :pipeline_artifacts_size, :uploads_size, :dependency_proxy_size) end specify { expect(described_class).to require_graphql_authorizations(:read_statistics) } diff --git a/spec/graphql/types/subscription_type_spec.rb b/spec/graphql/types/subscription_type_spec.rb index bf933945a31..593795de004 100644 --- a/spec/graphql/types/subscription_type_spec.rb +++ b/spec/graphql/types/subscription_type_spec.rb @@ -7,6 +7,7 @@ RSpec.describe GitlabSchema.types['Subscription'] do expected_fields = %i[ issuable_assignees_updated issue_crm_contacts_updated + issuable_title_updated ] expect(described_class).to have_graphql_fields(*expected_fields).only diff --git a/spec/graphql/types/user_preferences_type_spec.rb b/spec/graphql/types/user_preferences_type_spec.rb new file mode 100644 index 00000000000..fac45443290 --- /dev/null +++ b/spec/graphql/types/user_preferences_type_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::UserPreferencesType do + specify { expect(described_class.graphql_name).to eq('UserPreferences') } + + it 'exposes the expected fields' do + expected_fields = %i[ + issues_sort + ] + + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb index 4e3f442dc71..a2fc8f4c954 100644 --- a/spec/graphql/types/user_type_spec.rb +++ b/spec/graphql/types/user_type_spec.rb @@ -67,14 +67,14 @@ RSpec.describe GitlabSchema.types['User'] do ) end - subject { GitlabSchema.execute(query, context: { current_user: current_user }).as_json.dig('data', 'user', 'name') } + subject(:user_name) { GitlabSchema.execute(query, context: { current_user: current_user }).as_json.dig('data', 'user', 'name') } context 'user requests' do let(:current_user) { user } context 'a user' do it 'returns name' do - expect(subject).to eq('John Smith') + expect(user_name).to eq('John Smith') end end @@ -85,21 +85,39 @@ RSpec.describe GitlabSchema.types['User'] do let(:current_user) { nil } it 'returns `****`' do - expect(subject).to eq('****') + expect(user_name).to eq('****') end end - it 'returns `****` for a regular user' do - expect(subject).to eq('****') + context 'when the requester is not a project member' do + it 'returns `Project bot` for a non project member in a public project' do + expect(user_name).to eq('Project bot') + end + + context 'in a private project' do + let(:project) { create(:project, :private) } + + it 'returns `****` for a non project member in a private project' do + expect(user_name).to eq('****') + end + end end - context 'when requester is a project maintainer' do + context 'with a project member' do before do - project.add_maintainer(user) + project.add_guest(user) + end + + it 'returns `Project bot` for a project member' do + expect(user_name).to eq('Project bot') end - it 'returns name' do - expect(subject).to eq('Project bot') + context 'in a private project' do + let(:project) { create(:project, :private) } + + it 'returns `Project bot` for a project member in a private project' do + expect(user_name).to eq('Project bot') + end end end end |