diff options
Diffstat (limited to 'spec/graphql')
39 files changed, 542 insertions, 144 deletions
diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index e52f60a2c02..4db12643069 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -237,7 +237,7 @@ RSpec.describe GitlabSchema do it 'raises an error' do expect { described_class.parse_gid(global_id) } - .to raise_error(Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid GitLab id.") + .to raise_error(Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid GitLab ID.") end end @@ -256,7 +256,7 @@ RSpec.describe GitlabSchema do 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.") + .to raise_error(Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid ID for TestTwo.") end end end diff --git a/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb b/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb index f8b61c5064a..31abbabe385 100644 --- a/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb +++ b/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb @@ -55,6 +55,7 @@ RSpec.describe Mutations::AlertManagement::Alerts::SetAssignees do context 'when operation mode is not specified' do it_behaves_like 'successful resolution' + it_behaves_like 'an incident management tracked event', :incident_management_alert_assigned end context 'when user does not have permission to update alerts' do 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 11ee40a4c7e..a10c3725ba2 100644 --- a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb +++ b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb @@ -16,6 +16,8 @@ RSpec.describe Mutations::AlertManagement::Alerts::Todo::Create do describe '#resolve' do subject(:resolve) { mutation.resolve(args) } + it_behaves_like 'an incident management tracked event', :incident_management_alert_todo + context 'when user does not have permissions' do let(:current_user) { nil } diff --git a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb index fa5a84b4fcc..e6a7434d579 100644 --- a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb +++ b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb @@ -26,6 +26,8 @@ RSpec.describe Mutations::AlertManagement::CreateAlertIssue do errors: [] ) end + + it_behaves_like 'an incident management tracked event', :incident_management_incident_created end context 'when CreateAlertIssue responds with an error' do diff --git a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb index a224b564de9..ab98088ebcd 100644 --- a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb +++ b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb @@ -30,6 +30,10 @@ RSpec.describe Mutations::AlertManagement::UpdateAlertStatus do ) end + it_behaves_like 'an incident management tracked event', :incident_management_alert_status_changed do + let(:user) { current_user } + end + context 'error occurs when updating' do it 'returns the alert with errors' do # Stub an error on the alert diff --git a/spec/graphql/mutations/boards/lists/create_spec.rb b/spec/graphql/mutations/boards/lists/create_spec.rb index 1a881ac81e8..b1fe9911c7b 100644 --- a/spec/graphql/mutations/boards/lists/create_spec.rb +++ b/spec/graphql/mutations/boards/lists/create_spec.rb @@ -24,14 +24,12 @@ RSpec.describe Mutations::Boards::Lists::Create do describe '#ready?' do it 'raises an error if required arguments are missing' do expect { mutation.ready?({ board_id: 'some id' }) } - .to raise_error(Gitlab::Graphql::Errors::ArgumentError, - 'one and only one of backlog or labelId is required') + .to raise_error(Gitlab::Graphql::Errors::ArgumentError, /one and only one of/) end it 'raises an error if too many required arguments are specified' do expect { mutation.ready?({ board_id: 'some id', backlog: true, label_id: 'some label' }) } - .to raise_error(Gitlab::Graphql::Errors::ArgumentError, - 'one and only one of backlog or labelId is required') + .to raise_error(Gitlab::Graphql::Errors::ArgumentError, /one and only one of/) end end @@ -66,6 +64,15 @@ RSpec.describe Mutations::Boards::Lists::Create do expect(new_list.title).to eq dev_label.title expect(new_list.position).to eq 0 end + + context 'when label not found' do + let(:list_create_params) { { label_id: "gid://gitlab/Label/#{non_existing_record_id}" } } + + it 'raises an error' do + expect { subject } + .to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'Label not found!') + end + end end end diff --git a/spec/graphql/mutations/discussions/toggle_resolve_spec.rb b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb index 9ac4d6ab165..d779a2227c1 100644 --- a/spec/graphql/mutations/discussions/toggle_resolve_spec.rb +++ b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb @@ -51,7 +51,7 @@ RSpec.describe Mutations::Discussions::ToggleResolve do it 'raises an error' do expect { subject }.to raise_error( Gitlab::Graphql::Errors::ArgumentError, - "#{discussion.to_global_id} is not a valid id for Discussion." + "#{discussion.to_global_id} is not a valid ID for Discussion." ) end end diff --git a/spec/graphql/mutations/issues/set_severity_spec.rb b/spec/graphql/mutations/issues/set_severity_spec.rb new file mode 100644 index 00000000000..ed73d3b777e --- /dev/null +++ b/spec/graphql/mutations/issues/set_severity_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::Issues::SetSeverity do + let_it_be(:user) { create(:user) } + let_it_be(:issue) { create(:incident) } + let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + + specify { expect(described_class).to require_graphql_authorizations(:update_issue) } + + describe '#resolve' do + let(:severity) { 'CRITICAL' } + let(:mutated_incident) { subject[:issue] } + + subject(:resolve) { mutation.resolve(project_path: issue.project.full_path, iid: issue.iid, severity: severity) } + + context 'when the user cannot update the issue' do + it 'raises an error' do + expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + context 'when the user can update the issue' do + before do + issue.project.add_developer(user) + end + + context 'when issue type is incident' do + context 'when severity has a correct value' do + it 'updates severity' do + expect(resolve[:issue].severity).to eq('critical') + end + + it 'returns no errors' do + expect(resolve[:errors]).to be_empty + end + end + + context 'when severity has an unsuported value' do + let(:severity) { 'unsupported-severity' } + + it 'sets severity to default' do + expect(resolve[:issue].severity).to eq(IssuableSeverity::DEFAULT) + end + + it 'returns no errorsr' do + expect(resolve[:errors]).to be_empty + end + end + end + + context 'when issue type is not incident' do + let!(:issue) { create(:issue) } + + it 'does not updates the issue' do + expect { resolve }.not_to change { issue.updated_at } + end + end + end + end +end diff --git a/spec/graphql/resolvers/admin/analytics/instance_statistics/measurements_resolver_spec.rb b/spec/graphql/resolvers/admin/analytics/instance_statistics/measurements_resolver_spec.rb new file mode 100644 index 00000000000..76854be2daa --- /dev/null +++ b/spec/graphql/resolvers/admin/analytics/instance_statistics/measurements_resolver_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Admin::Analytics::InstanceStatistics::MeasurementsResolver do + include GraphqlHelpers + + describe '#resolve' do + let_it_be(:user) { create(:user) } + let_it_be(:admin_user) { create(:user, :admin) } + + let_it_be(:project_measurement_new) { create(:instance_statistics_measurement, :project_count, recorded_at: 2.days.ago) } + let_it_be(:project_measurement_old) { create(:instance_statistics_measurement, :project_count, recorded_at: 10.days.ago) } + + subject { resolve_measurements({ identifier: 'projects' }, { current_user: current_user }) } + + context 'when requesting project count measurements' do + context 'as an admin user' do + let(:current_user) { admin_user } + + it 'returns the records, latest first' do + expect(subject).to eq([project_measurement_new, project_measurement_old]) + end + end + + context 'as a non-admin user' do + let(:current_user) { user } + + it 'raises ResourceNotAvailable error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + context 'as an unauthenticated user' do + let(:current_user) { nil } + + it 'raises ResourceNotAvailable error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + end + end + + def resolve_measurements(args = {}, context = {}) + resolve(described_class, args: args, ctx: context) + end +end diff --git a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb index e23a37b3d69..4ccf194522f 100644 --- a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb +++ b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb @@ -11,41 +11,59 @@ RSpec.describe Resolvers::BoardListIssuesResolver do let_it_be(:group) { create(:group, :private) } shared_examples_for 'group and project board list issues resolver' do - let!(:board) { create(:board, resource_parent: board_parent) } - before do board_parent.add_developer(user) end # auth is handled by the parent object context 'when authorized' do - let!(:list) { create(:list, board: board, label: label) } + let!(:issue1) { create(:issue, project: project, labels: [label], relative_position: 10) } + let!(:issue2) { create(:issue, project: project, labels: [label, label2], relative_position: 12) } + let!(:issue3) { create(:issue, project: project, labels: [label, label3], relative_position: 10) } it 'returns the issues in the correct order' do - issue1 = create(:issue, project: project, labels: [label], relative_position: 10) - issue2 = create(:issue, project: project, labels: [label], relative_position: 12) - issue3 = create(:issue, project: project, labels: [label], relative_position: 10) - # by relative_position and then ID issues = resolve_board_list_issues.items expect(issues.map(&:id)).to eq [issue3.id, issue1.id, issue2.id] end + + it 'finds only issues matching filters' do + result = resolve_board_list_issues(args: { filters: { label_name: label.title, not: { label_name: label2.title } } }).items + + expect(result).to match_array([issue1, issue3]) + end + + it 'finds only issues matching search param' do + result = resolve_board_list_issues(args: { filters: { search: issue1.title } }).items + + expect(result).to match_array([issue1]) + end end end describe '#resolve' do context 'when project boards' do + let_it_be(:label) { create(:label, project: user_project) } + let_it_be(:label2) { create(:label, project: user_project) } + let_it_be(:label3) { create(:label, project: user_project) } + let_it_be(:board) { create(:board, resource_parent: user_project) } + let_it_be(:list) { create(:list, board: board, label: label) } + let(:board_parent) { user_project } - let!(:label) { create(:label, project: project, name: 'project label') } let(:project) { user_project } it_behaves_like 'group and project board list issues resolver' end context 'when group boards' do + let_it_be(:label) { create(:group_label, group: group) } + let_it_be(:label2) { create(:group_label, group: group) } + let_it_be(:label3) { create(:group_label, group: group) } + let_it_be(:board) { create(:board, resource_parent: group) } + let_it_be(:list) { create(:list, board: board, label: label) } + let(:board_parent) { group } - let!(:label) { create(:group_label, group: group, name: 'group label') } let!(:project) { create(:project, :private, group: group) } it_behaves_like 'group and project board list issues resolver' diff --git a/spec/graphql/resolvers/group_members_resolver_spec.rb b/spec/graphql/resolvers/group_members_resolver_spec.rb new file mode 100644 index 00000000000..bbfea575492 --- /dev/null +++ b/spec/graphql/resolvers/group_members_resolver_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::GroupMembersResolver do + include GraphqlHelpers + + it_behaves_like 'querying members with a group' do + let_it_be(:resource_member) { create(:group_member, user: user_1, group: group_1) } + let_it_be(:resource) { group_1 } + end +end diff --git a/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb b/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb index 69e940ee6ca..16eb190efc6 100644 --- a/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb +++ b/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb @@ -41,6 +41,12 @@ RSpec.describe Resolvers::IssueStatusCountsResolver do it_behaves_like 'returns expected results' + context 'project used as parent' do + let(:parent) { project } + + it_behaves_like 'returns expected results' + end + context 'group used as parent' do let(:parent) { project.group } diff --git a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb index 2fe3e86ec14..ae3097c1d9e 100644 --- a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb +++ b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb @@ -29,4 +29,11 @@ RSpec.describe Resolvers::MergeRequestPipelinesResolver do it 'resolves only MRs for the passed merge request' do expect(resolve_pipelines).to contain_exactly(pipeline) end + + describe 'with archived project' do + let(:archived_project) { create(:project, :archived) } + let(:merge_request) { create(:merge_request, source_project: archived_project) } + + it { expect(resolve_pipelines).not_to contain_exactly(pipeline) } + end end diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb index e939edae779..aecffc487aa 100644 --- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb +++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb @@ -6,16 +6,20 @@ RSpec.describe Resolvers::MergeRequestsResolver do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } + let_it_be(:milestone) { create(:milestone, project: project) } let_it_be(:current_user) { create(:user) } + let_it_be(:other_user) { create(:user) } let_it_be(:common_attrs) { { author: current_user, source_project: project, target_project: project } } let_it_be(:merge_request_1) { create(:merge_request, :simple, **common_attrs) } let_it_be(:merge_request_2) { create(:merge_request, :rebased, **common_attrs) } let_it_be(:merge_request_3) { create(:merge_request, :unique_branches, **common_attrs) } let_it_be(:merge_request_4) { create(:merge_request, :unique_branches, :locked, **common_attrs) } let_it_be(:merge_request_5) { create(:merge_request, :simple, :locked, **common_attrs) } - let_it_be(:merge_request_6) { create(:labeled_merge_request, :unique_branches, labels: create_list(:label, 2), **common_attrs) } + let_it_be(:merge_request_6) { create(:labeled_merge_request, :unique_branches, labels: create_list(:label, 2, project: project), **common_attrs) } + let_it_be(:merge_request_with_milestone) { create(:merge_request, :unique_branches, **common_attrs, milestone: milestone) } let_it_be(:other_project) { create(:project, :repository) } let_it_be(:other_merge_request) { create(:merge_request, source_project: other_project, target_project: other_project) } + let(:iid_1) { merge_request_1.iid } let(:iid_2) { merge_request_2.iid } let(:other_iid) { other_merge_request.iid } @@ -32,7 +36,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do it 'returns all merge requests' do result = resolve_mr(project, {}) - expect(result).to contain_exactly(merge_request_1, merge_request_2, merge_request_3, merge_request_4, merge_request_5, merge_request_6) + expect(result).to contain_exactly(merge_request_1, merge_request_2, merge_request_3, merge_request_4, merge_request_5, merge_request_6, merge_request_with_milestone) end it 'returns only merge requests that the current user can see' do @@ -179,6 +183,20 @@ RSpec.describe Resolvers::MergeRequestsResolver do end end + context 'by milestone' do + it 'filters merge requests by milestone title' do + result = resolve_mr(project, milestone_title: milestone.title) + + expect(result).to eq([merge_request_with_milestone]) + end + + it 'does not find anything' do + result = resolve_mr(project, milestone_title: 'unknown-milestone') + + expect(result).to be_empty + end + end + describe 'combinations' do it 'requires all filters' do create(:merge_request, :closed, source_project: project, target_project: project, source_branch: merge_request_4.source_branch) @@ -188,6 +206,33 @@ RSpec.describe Resolvers::MergeRequestsResolver do expect(result.compact).to contain_exactly(merge_request_4) end end + + describe 'sorting' do + context 'when sorting by created' do + it 'sorts merge requests ascending' do + expect(resolve_mr(project, sort: 'created_asc')).to eq [merge_request_1, merge_request_2, merge_request_3, merge_request_4, merge_request_5, merge_request_6, merge_request_with_milestone] + end + + it 'sorts merge requests descending' do + expect(resolve_mr(project, sort: 'created_desc')).to eq [merge_request_with_milestone, merge_request_6, merge_request_5, merge_request_4, merge_request_3, merge_request_2, merge_request_1] + end + end + + context 'when sorting by merged at' do + before do + merge_request_1.metrics.update!(merged_at: 10.days.ago) + merge_request_3.metrics.update!(merged_at: 5.days.ago) + end + + it 'sorts merge requests ascending' do + expect(resolve_mr(project, sort: :merged_at_asc)).to eq [merge_request_1, merge_request_3, merge_request_with_milestone, merge_request_6, merge_request_5, merge_request_4, merge_request_2] + end + + it 'sorts merge requests descending' do + expect(resolve_mr(project, sort: :merged_at_desc)).to eq [merge_request_3, merge_request_1, merge_request_with_milestone, merge_request_6, merge_request_5, merge_request_4, merge_request_2] + end + end + end end def resolve_mr_single(project, iid) diff --git a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb index 699269b47e0..4ad8f99219f 100644 --- a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb +++ b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb @@ -27,7 +27,7 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do end it 'finds all projects including the subgroups' do - expect(resolve_projects(include_subgroups: true)).to contain_exactly(project1, project2, nested_project) + expect(resolve_projects(include_subgroups: true, sort: nil, search: nil)).to contain_exactly(project1, project2, nested_project) end context 'with an user namespace' do @@ -38,7 +38,52 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do end it 'finds all projects including the subgroups' do - expect(resolve_projects(include_subgroups: true)).to contain_exactly(project1, project2) + expect(resolve_projects(include_subgroups: true, sort: nil, search: nil)).to contain_exactly(project1, project2) + end + end + end + + context 'search and similarity sorting' do + let(:project_1) { create(:project, name: 'Project', path: 'project', namespace: namespace) } + let(:project_2) { create(:project, name: 'Test Project', path: 'test-project', namespace: namespace) } + let(:project_3) { create(:project, name: 'Test', path: 'test', namespace: namespace) } + + before do + project_1.add_developer(current_user) + project_2.add_developer(current_user) + project_3.add_developer(current_user) + end + + it 'returns projects ordered by similarity to the search input' do + projects = resolve_projects(include_subgroups: true, sort: :similarity, search: 'test') + + project_names = projects.map { |proj| proj['name'] } + expect(project_names.first).to eq('Test') + expect(project_names.second).to eq('Test Project') + end + + it 'filters out result that do not match the search input' do + projects = resolve_projects(include_subgroups: true, sort: :similarity, search: 'test') + + project_names = projects.map { |proj| proj['name'] } + expect(project_names).not_to include('Project') + end + + context 'when `search` parameter is not given' do + it 'returns projects not ordered by similarity' do + projects = resolve_projects(include_subgroups: true, sort: :similarity, search: nil) + + project_names = projects.map { |proj| proj['name'] } + expect(project_names.first).not_to eq('Test') + end + end + + context 'when only search term is given' do + it 'filters out result that do not match the search input, but does not sort them' do + projects = resolve_projects(include_subgroups: true, sort: :nil, search: 'test') + + project_names = projects.map { |proj| proj['name'] } + expect(project_names).to contain_exactly('Test', 'Test Project') end end end @@ -63,7 +108,7 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do expect(field.to_graphql.complexity.call({}, { include_subgroups: true }, 1)).to eq 24 end - def resolve_projects(args = { include_subgroups: false }, context = { current_user: current_user }) + def resolve_projects(args = { include_subgroups: false, sort: nil, search: nil }, context = { current_user: current_user }) resolve(described_class, obj: namespace, args: args, ctx: context) end end diff --git a/spec/graphql/resolvers/project_members_resolver_spec.rb b/spec/graphql/resolvers/project_members_resolver_spec.rb index 602225cf632..2f4145b3215 100644 --- a/spec/graphql/resolvers/project_members_resolver_spec.rb +++ b/spec/graphql/resolvers/project_members_resolver_spec.rb @@ -5,58 +5,9 @@ require 'spec_helper' RSpec.describe Resolvers::ProjectMembersResolver do include GraphqlHelpers - context "with a group" do - let_it_be(:root_group) { create(:group) } - let_it_be(:group_1) { create(:group, parent: root_group) } - let_it_be(:group_2) { create(:group, parent: root_group) } - let_it_be(:project) { create(:project, :public, group: group_1) } - - let_it_be(:user_1) { create(:user, name: 'test user') } - let_it_be(:user_2) { create(:user, name: 'test user 2') } - let_it_be(:user_3) { create(:user, name: 'another user 1') } - let_it_be(:user_4) { create(:user, name: 'another user 2') } - - let_it_be(:project_member) { create(:project_member, user: user_1, project: project) } - let_it_be(:group_1_member) { create(:group_member, user: user_2, group: group_1) } - let_it_be(:group_2_member) { create(:group_member, user: user_3, group: group_2) } - let_it_be(:root_group_member) { create(:group_member, user: user_4, group: root_group) } - - let(:args) { {} } - - subject do - resolve(described_class, obj: project, args: args, ctx: { context: user_4 }) - end - - describe '#resolve' do - it 'finds all project members' do - expect(subject).to contain_exactly(project_member, group_1_member, root_group_member) - end - - context 'with search' do - context 'when the search term matches a user' do - let(:args) { { search: 'test' } } - - it 'searches users by user name' do - expect(subject).to contain_exactly(project_member, group_1_member) - end - end - - context 'when the search term does not match any user' do - let(:args) { { search: 'nothing' } } - - it 'is empty' do - expect(subject).to be_empty - end - end - end - - context 'when project is nil' do - let(:project) { nil } - - it 'returns nil' do - expect(subject).to be_empty - end - end - end + it_behaves_like 'querying members with a group' do + let_it_be(:project) { create(:project, group: group_1) } + let_it_be(:resource_member) { create(:project_member, user: user_1, project: project) } + let_it_be(:resource) { project } end end diff --git a/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb b/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb new file mode 100644 index 00000000000..bfb3ce91d58 --- /dev/null +++ b/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::ProjectMergeRequestsResolver do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :repository) } + let_it_be(:current_user) { create(:user) } + let_it_be(:other_user) { create(:user) } + + let_it_be(:merge_request_with_author_and_assignee) do + create(:merge_request, + :unique_branches, + source_project: project, + target_project: project, + author: other_user, + assignee: other_user) + end + + before do + project.add_developer(current_user) + end + + context 'by assignee' do + it 'filters merge requests by assignee username' do + result = resolve_mr(project, assignee_username: other_user.username) + + expect(result).to eq([merge_request_with_author_and_assignee]) + end + + it 'does not find anything' do + result = resolve_mr(project, assignee_username: 'unknown-user') + + expect(result).to be_empty + end + end + + context 'by author' do + it 'filters merge requests by author username' do + result = resolve_mr(project, author_username: other_user.username) + + expect(result).to eq([merge_request_with_author_and_assignee]) + end + + it 'does not find anything' do + result = resolve_mr(project, author_username: 'unknown-user') + + expect(result).to be_empty + end + end + + def resolve_mr(project, args, resolver: described_class, user: current_user) + resolve(resolver, obj: project, args: args, ctx: { current_user: user }) + end +end diff --git a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb index fada2f9193c..a6a86c49373 100644 --- a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb +++ b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb @@ -34,12 +34,10 @@ RSpec.describe Resolvers::ProjectPipelineResolver do expect { resolve_pipeline(project, {}) }.to raise_error(ArgumentError) end - context 'when the pipeline is not a ci_config_source' do + context 'when the pipeline is a dangling pipeline' do let(:pipeline) do - config_source_value = Ci::PipelineEnums.non_ci_config_source_values.first - config_source = Ci::PipelineEnums.config_sources.key(config_source_value) - - create(:ci_pipeline, config_source: config_source, project: project) + dangling_source = ::Enums::Ci::Pipeline.dangling_sources.each_value.first + create(:ci_pipeline, source: dangling_source, project: project) end it 'resolves pipeline for the passed iid' do diff --git a/spec/graphql/resolvers/projects_resolver_spec.rb b/spec/graphql/resolvers/projects_resolver_spec.rb index db7c9225c84..d22ffeed740 100644 --- a/spec/graphql/resolvers/projects_resolver_spec.rb +++ b/spec/graphql/resolvers/projects_resolver_spec.rb @@ -71,6 +71,14 @@ RSpec.describe Resolvers::ProjectsResolver do is_expected.to contain_exactly(project, private_project) end end + + context 'when ids filter is provided' do + let(:filters) { { ids: [project.to_global_id.to_s] } } + + it 'returns matching project' do + is_expected.to contain_exactly(project) + end + end end end end diff --git a/spec/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum_spec.rb b/spec/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum_spec.rb new file mode 100644 index 00000000000..625fb17bbf8 --- /dev/null +++ b/spec/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['MeasurementIdentifier'] do + specify { expect(described_class.graphql_name).to eq('MeasurementIdentifier') } + + it 'exposes all the existing identifier values' do + identifiers = Analytics::InstanceStatistics::Measurement.identifiers.keys.map(&:upcase) + + expect(described_class.values.keys).to match_array(identifiers) + end +end diff --git a/spec/graphql/types/admin/analytics/instance_statistics/measurement_type_spec.rb b/spec/graphql/types/admin/analytics/instance_statistics/measurement_type_spec.rb new file mode 100644 index 00000000000..de8143a5466 --- /dev/null +++ b/spec/graphql/types/admin/analytics/instance_statistics/measurement_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['InstanceStatisticsMeasurement'] do + subject { described_class } + + it { is_expected.to have_graphql_field(:recorded_at) } + it { is_expected.to have_graphql_field(:identifier) } + it { is_expected.to have_graphql_field(:count) } +end diff --git a/spec/graphql/types/base_enum_spec.rb b/spec/graphql/types/base_enum_spec.rb index 0d0f6346f2d..b7adcf217f6 100644 --- a/spec/graphql/types/base_enum_spec.rb +++ b/spec/graphql/types/base_enum_spec.rb @@ -21,4 +21,16 @@ RSpec.describe Types::BaseEnum do expect(enum.enum).to be_a(HashWithIndifferentAccess) end end + + include_examples 'Gitlab-style deprecations' do + def subject(args = {}) + enum = Class.new(described_class) do + graphql_name 'TestEnum' + + value 'TEST_VALUE', **args + end + + enum.to_graphql.values['TEST_VALUE'] + end + end end diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb index 73bb54e7ad0..bcfbd7f2480 100644 --- a/spec/graphql/types/base_field_spec.rb +++ b/spec/graphql/types/base_field_spec.rb @@ -167,70 +167,23 @@ RSpec.describe Types::BaseField do end end - describe '`deprecated` property' do - def test_field(args = {}) + include_examples 'Gitlab-style deprecations' do + def subject(args = {}) base_args = { name: 'test', type: GraphQL::STRING_TYPE, null: true } described_class.new(**base_args.merge(args)) end - describe 'validations' do - it 'raises an informative error if `deprecation_reason` is used' do - expect { test_field(deprecation_reason: 'foo') }.to raise_error( - ArgumentError, - 'Use `deprecated` property instead of `deprecation_reason`. ' \ - 'See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-fields' - ) - end - - it 'raises an error if a required property is missing', :aggregate_failures do - expect { test_field(deprecated: { milestone: '1.10' }) }.to raise_error( - ArgumentError, - 'Please provide a `reason` within `deprecated`' - ) - expect { test_field(deprecated: { reason: 'Deprecation reason' }) }.to raise_error( - ArgumentError, - 'Please provide a `milestone` within `deprecated`' - ) - end - - it 'raises an error if milestone is not a String', :aggregate_failures do - expect { test_field(deprecated: { milestone: 1.10, reason: 'Deprecation reason' }) }.to raise_error( - ArgumentError, - '`milestone` must be a `String`' - ) - end - end - - it 'adds a formatted `deprecated_reason` to the field' do - field = test_field(deprecated: { milestone: '1.10', reason: 'Deprecation reason' }) - - expect(field.deprecation_reason).to eq('Deprecation reason. Deprecated in 1.10') - end - - it 'appends to the description if given' do - field = test_field( - deprecated: { milestone: '1.10', reason: 'Deprecation reason' }, - description: 'Field description' - ) - - expect(field.description).to eq('Field description. Deprecated in 1.10: Deprecation reason') - end - - it 'does not append to the description if it is absent' do - field = test_field(deprecated: { milestone: '1.10', reason: 'Deprecation reason' }) - - expect(field.description).to be_nil - end - it 'interacts well with the `feature_flag` property' do - field = test_field( + field = subject( deprecated: { milestone: '1.10', reason: 'Deprecation reason' }, description: 'Field description', feature_flag: 'foo_flag' ) - expect(field.description).to eq('Field description. Available only when feature flag `foo_flag` is enabled. Deprecated in 1.10: Deprecation reason') + expectation = 'Field description. Available only when feature flag `foo_flag` is enabled. Deprecated in 1.10: Deprecation reason' + + expect(field.description).to eq(expectation) end end end diff --git a/spec/graphql/types/boards/board_issue_input_type_spec.rb b/spec/graphql/types/boards/board_issue_input_type_spec.rb new file mode 100644 index 00000000000..6319ff9a88e --- /dev/null +++ b/spec/graphql/types/boards/board_issue_input_type_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['BoardIssueInput'] do + it { expect(described_class.graphql_name).to eq('BoardIssueInput') } + + it 'exposes negated issue arguments' do + allowed_args = %w(labelName milestoneTitle assigneeUsername authorUsername + releaseTag myReactionEmoji not search) + + expect(described_class.arguments.keys).to include(*allowed_args) + expect(described_class.arguments['not'].type).to eq(Types::Boards::NegatedBoardIssueInputType) + end +end diff --git a/spec/graphql/types/current_user_todos_type_spec.rb b/spec/graphql/types/current_user_todos_type_spec.rb new file mode 100644 index 00000000000..a0015e96788 --- /dev/null +++ b/spec/graphql/types/current_user_todos_type_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['CurrentUserTodos'] do + specify { expect(described_class.graphql_name).to eq('CurrentUserTodos') } + + specify { expect(described_class).to have_graphql_fields(:current_user_todos).only } +end diff --git a/spec/graphql/types/design_management/design_type_spec.rb b/spec/graphql/types/design_management/design_type_spec.rb index 7a38b397965..cae98a013e1 100644 --- a/spec/graphql/types/design_management/design_type_spec.rb +++ b/spec/graphql/types/design_management/design_type_spec.rb @@ -3,8 +3,10 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['Design'] do + specify { expect(described_class.interfaces).to include(Types::CurrentUserTodos) } + it_behaves_like 'a GraphQL type with design fields' do - let(:extra_design_fields) { %i[notes discussions versions] } + let(:extra_design_fields) { %i[notes current_user_todos discussions versions] } let_it_be(:design) { create(:design, :with_versions) } let(:object_id) { GitlabSchema.id_from_object(design) } let_it_be(:object_id_b) { GitlabSchema.id_from_object(create(:design, :with_versions)) } diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb index 0b87805c2ef..bf7ddebaf5b 100644 --- a/spec/graphql/types/group_type_spec.rb +++ b/spec/graphql/types/group_type_spec.rb @@ -16,7 +16,7 @@ RSpec.describe GitlabSchema.types['Group'] do web_url avatar_url share_with_group_lock project_creation_level subgroup_creation_level require_two_factor_authentication two_factor_grace_period auto_devops_enabled emails_disabled - mentions_disabled parent boards milestones + mentions_disabled parent boards milestones group_members ] expect(described_class).to include_graphql_fields(*expected_fields) @@ -30,5 +30,12 @@ RSpec.describe GitlabSchema.types['Group'] do end end + describe 'members field' do + subject { described_class.fields['groupMembers'] } + + it { is_expected.to have_graphql_type(Types::GroupMemberType.connection_type) } + it { is_expected.to have_graphql_resolver(Resolvers::GroupMembersResolver) } + end + it_behaves_like 'a GraphQL type with labels' end diff --git a/spec/graphql/types/issuable_severity_enum_spec.rb b/spec/graphql/types/issuable_severity_enum_spec.rb new file mode 100644 index 00000000000..3f8bd76fa62 --- /dev/null +++ b/spec/graphql/types/issuable_severity_enum_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::IssuableSeverityEnum do + specify { expect(described_class.graphql_name).to eq('IssuableSeverity') } + + it 'exposes all the existing issuable severity values' do + expect(described_class.values.keys).to contain_exactly( + *%w[UNKNOWN LOW MEDIUM HIGH CRITICAL] + ) + end +end diff --git a/spec/graphql/types/issue_type_spec.rb b/spec/graphql/types/issue_type_spec.rb index 24353f8fe3a..c55e624dd11 100644 --- a/spec/graphql/types/issue_type_spec.rb +++ b/spec/graphql/types/issue_type_spec.rb @@ -11,11 +11,13 @@ RSpec.describe GitlabSchema.types['Issue'] do specify { expect(described_class.interfaces).to include(Types::Notes::NoteableType) } + specify { expect(described_class.interfaces).to include(Types::CurrentUserTodos) } + it 'has specific fields' do fields = %i[id iid title description state reference author assignees participants labels milestone due_date confidential discussion_locked upvotes downvotes user_notes_count web_path web_url relative_position subscribed time_estimate total_time_spent closed_at created_at updated_at task_completion_status - designs design_collection] + designs design_collection alert_management_alert severity current_user_todos] fields.each do |field_name| expect(described_class).to have_graphql_field(field_name) diff --git a/spec/graphql/types/member_interface_spec.rb b/spec/graphql/types/member_interface_spec.rb new file mode 100644 index 00000000000..11fd09eb335 --- /dev/null +++ b/spec/graphql/types/member_interface_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::MemberInterface do + it 'exposes the expected fields' do + expected_fields = %i[ + id + access_level + created_by + created_at + updated_at + expires_at + user + ] + + expect(described_class).to have_graphql_fields(*expected_fields) + end + + describe '.resolve_type' do + subject { described_class.resolve_type(object, {}) } + + context 'for project member' do + let(:object) { build(:project_member) } + + it { is_expected.to be Types::ProjectMemberType } + end + + context 'for group member' do + let(:object) { build(:group_member) } + + it { is_expected.to be Types::GroupMemberType } + end + + context 'for an unkown type' do + let(:object) { build(:user) } + + it 'raises an error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::BaseError) + end + end + end +end diff --git a/spec/graphql/types/merge_request_sort_enum_spec.rb b/spec/graphql/types/merge_request_sort_enum_spec.rb new file mode 100644 index 00000000000..472eba1a50d --- /dev/null +++ b/spec/graphql/types/merge_request_sort_enum_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['MergeRequestSort'] do + specify { expect(described_class.graphql_name).to eq('MergeRequestSort') } + + it_behaves_like 'common sort values' + + it 'exposes all the existing issue sort values' do + expect(described_class.values.keys).to include( + *%w[MERGED_AT_ASC MERGED_AT_DESC] + ) + end +end diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb index b11951190e0..46aebbdabeb 100644 --- a/spec/graphql/types/merge_request_type_spec.rb +++ b/spec/graphql/types/merge_request_type_spec.rb @@ -9,6 +9,8 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do specify { expect(described_class.interfaces).to include(Types::Notes::NoteableType) } + specify { expect(described_class.interfaces).to include(Types::CurrentUserTodos) } + it 'has the expected fields' do expected_fields = %w[ notes discussions user_permissions id iid title title_html description @@ -24,10 +26,16 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do source_branch_exists target_branch_exists upvotes downvotes head_pipeline pipelines task_completion_status milestone assignees participants subscribed labels discussion_locked time_estimate - total_time_spent reference author merged_at commit_count + total_time_spent reference author merged_at commit_count current_user_todos + conflicts auto_merge_enabled ] - expected_fields << 'approved_by' if Gitlab.ee? + if Gitlab.ee? + expected_fields << 'approved' + expected_fields << 'approvals_left' + expected_fields << 'approvals_required' + expected_fields << 'approved_by' + end expect(described_class).to have_graphql_fields(*expected_fields) end diff --git a/spec/graphql/types/milestone_type_spec.rb b/spec/graphql/types/milestone_type_spec.rb index 2315c10433b..806495250ac 100644 --- a/spec/graphql/types/milestone_type_spec.rb +++ b/spec/graphql/types/milestone_type_spec.rb @@ -15,7 +15,7 @@ RSpec.describe GitlabSchema.types['Milestone'] do stats ] - expect(described_class).to have_graphql_fields(*expected_fields) + expect(described_class).to have_graphql_fields(*expected_fields).at_least end describe 'stats field' do diff --git a/spec/graphql/types/package_type_enum_spec.rb b/spec/graphql/types/package_type_enum_spec.rb index fadec9744ed..80a20a68bc2 100644 --- a/spec/graphql/types/package_type_enum_spec.rb +++ b/spec/graphql/types/package_type_enum_spec.rb @@ -4,6 +4,6 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['PackageTypeEnum'] do it 'exposes all package types' do - expect(described_class.values.keys).to contain_exactly(*%w[MAVEN NPM CONAN NUGET PYPI COMPOSER]) + expect(described_class.values.keys).to contain_exactly(*%w[MAVEN NPM CONAN NUGET PYPI COMPOSER GENERIC]) end end diff --git a/spec/graphql/types/permission_types/merge_request_spec.rb b/spec/graphql/types/permission_types/merge_request_spec.rb index 73a178540a6..2849dead9a8 100644 --- a/spec/graphql/types/permission_types/merge_request_spec.rb +++ b/spec/graphql/types/permission_types/merge_request_spec.rb @@ -7,7 +7,8 @@ RSpec.describe Types::PermissionTypes::MergeRequest do expected_permissions = [ :read_merge_request, :admin_merge_request, :update_merge_request, :create_note, :push_to_source_branch, :remove_source_branch, - :cherry_pick_on_current_merge_request, :revert_on_current_merge_request + :cherry_pick_on_current_merge_request, :revert_on_current_merge_request, + :can_merge ] expect(described_class).to have_graphql_fields(expected_permissions) diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 8a5d0cdf12d..44a89bfa35e 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -59,7 +59,7 @@ RSpec.describe GitlabSchema.types['Project'] do subject { described_class.fields['mergeRequests'] } it { is_expected.to have_graphql_type(Types::MergeRequestType.connection_type) } - it { is_expected.to have_graphql_resolver(Resolvers::MergeRequestsResolver) } + it { is_expected.to have_graphql_resolver(Resolvers::ProjectMergeRequestsResolver) } it do is_expected.to have_graphql_arguments(:iids, @@ -72,7 +72,11 @@ RSpec.describe GitlabSchema.types['Project'] do :first, :last, :merged_after, - :merged_before + :merged_before, + :author_username, + :assignee_username, + :milestone_title, + :sort ) end end @@ -108,7 +112,7 @@ RSpec.describe GitlabSchema.types['Project'] do describe 'members field' do subject { described_class.fields['projectMembers'] } - it { is_expected.to have_graphql_type(Types::ProjectMemberType.connection_type) } + it { is_expected.to have_graphql_type(Types::MemberInterface.connection_type) } it { is_expected.to have_graphql_resolver(Resolvers::ProjectMembersResolver) } end diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb index ab13162b406..11f780a4f3f 100644 --- a/spec/graphql/types/query_type_spec.rb +++ b/spec/graphql/types/query_type_spec.rb @@ -20,6 +20,8 @@ RSpec.describe GitlabSchema.types['Query'] do milestone user users + issue + instance_statistics_measurements ] expect(described_class).to have_graphql_fields(*expected_fields).at_least @@ -53,4 +55,20 @@ RSpec.describe GitlabSchema.types['Query'] do is_expected.to have_graphql_resolver(Resolvers::MetadataResolver) end end + + describe 'issue field' do + subject { described_class.fields['issue'] } + + it 'returns issue' do + is_expected.to have_graphql_type(Types::IssueType) + end + end + + describe 'instance_statistics_measurements field' do + subject { described_class.fields['instanceStatisticsMeasurements'] } + + it 'returns issue' do + is_expected.to have_graphql_type(Types::Admin::Analytics::InstanceStatistics::MeasurementType.connection_type) + end + end end diff --git a/spec/graphql/types/release_asset_link_type_spec.rb b/spec/graphql/types/release_asset_link_type_spec.rb index 679431012cf..6800d5459c4 100644 --- a/spec/graphql/types/release_asset_link_type_spec.rb +++ b/spec/graphql/types/release_asset_link_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['ReleaseAssetLink'] do it 'has the expected fields' do expected_fields = %w[ - id name url external link_type + id name url external link_type direct_asset_url ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb index 7710b8efefe..1d5af24b3d9 100644 --- a/spec/graphql/types/user_type_spec.rb +++ b/spec/graphql/types/user_type_spec.rb @@ -25,6 +25,7 @@ RSpec.describe GitlabSchema.types['User'] do assignedMergeRequests groupMemberships projectMemberships + starredProjects ] expect(described_class).to have_graphql_fields(*expected_fields) |