summaryrefslogtreecommitdiff
path: root/spec/graphql
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 13:18:24 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 13:18:24 +0000
commit0653e08efd039a5905f3fa4f6e9cef9f5d2f799c (patch)
tree4dcc884cf6d81db44adae4aa99f8ec1233a41f55 /spec/graphql
parent744144d28e3e7fddc117924fef88de5d9674fe4c (diff)
downloadgitlab-ce-0653e08efd039a5905f3fa4f6e9cef9f5d2f799c.tar.gz
Add latest changes from gitlab-org/gitlab@14-3-stable-eev14.3.0-rc42
Diffstat (limited to 'spec/graphql')
-rw-r--r--spec/graphql/mutations/custom_emoji/destroy_spec.rb79
-rw-r--r--spec/graphql/mutations/customer_relations/organizations/create_spec.rb74
-rw-r--r--spec/graphql/mutations/customer_relations/organizations/update_spec.rb74
-rw-r--r--spec/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb110
-rw-r--r--spec/graphql/resolvers/board_list_issues_resolver_spec.rb41
-rw-r--r--spec/graphql/resolvers/ci/group_runners_resolver_spec.rb94
-rw-r--r--spec/graphql/resolvers/ci/runners_resolver_spec.rb209
-rw-r--r--spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb26
-rw-r--r--spec/graphql/resolvers/group_resolver_spec.rb7
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb48
-rw-r--r--spec/graphql/resolvers/merge_requests_resolver_spec.rb10
-rw-r--r--spec/graphql/resolvers/project_resolver_spec.rb5
-rw-r--r--spec/graphql/resolvers/users/groups_resolver_spec.rb106
-rw-r--r--spec/graphql/types/ci/job_type_spec.rb1
-rw-r--r--spec/graphql/types/customer_relations/contact_type_spec.rb11
-rw-r--r--spec/graphql/types/customer_relations/organization_type_spec.rb11
-rw-r--r--spec/graphql/types/dependency_proxy/blob_type_spec.rb13
-rw-r--r--spec/graphql/types/dependency_proxy/group_setting_type_spec.rb13
-rw-r--r--spec/graphql/types/dependency_proxy/image_ttl_group_policy_type_spec.rb17
-rw-r--r--spec/graphql/types/dependency_proxy/manifest_type_spec.rb13
-rw-r--r--spec/graphql/types/group_type_spec.rb6
-rw-r--r--spec/graphql/types/issue_type_spec.rb52
-rw-r--r--spec/graphql/types/merge_requests/reviewer_type_spec.rb1
-rw-r--r--spec/graphql/types/project_statistics_type_spec.rb2
-rw-r--r--spec/graphql/types/terraform/state_version_type_spec.rb58
-rw-r--r--spec/graphql/types/user_type_spec.rb1
26 files changed, 895 insertions, 187 deletions
diff --git a/spec/graphql/mutations/custom_emoji/destroy_spec.rb b/spec/graphql/mutations/custom_emoji/destroy_spec.rb
new file mode 100644
index 00000000000..4667812cc80
--- /dev/null
+++ b/spec/graphql/mutations/custom_emoji/destroy_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::CustomEmoji::Destroy do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:custom_emoji) { create(:custom_emoji, group: group) }
+
+ let(:args) { { id: custom_emoji.to_global_id } }
+ let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
+
+ context 'field tests' do
+ subject { described_class }
+
+ it { is_expected.to have_graphql_arguments(:id) }
+ it { is_expected.to have_graphql_field(:custom_emoji) }
+ end
+
+ shared_examples 'does not delete custom emoji' do
+ it 'raises exception' do
+ expect { subject }
+ .to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ shared_examples 'deletes custom emoji' do
+ it 'returns deleted custom emoji' do
+ result = subject
+
+ expect(result[:custom_emoji][:name]).to eq(custom_emoji.name)
+ end
+ end
+
+ describe '#resolve' do
+ subject { mutation.resolve(**args) }
+
+ context 'when the user' do
+ context 'has no permissions' do
+ it_behaves_like 'does not delete custom emoji'
+ end
+
+ context 'when the user is developer and not the owner of custom emoji' do
+ before do
+ group.add_developer(user)
+ end
+
+ it_behaves_like 'does not delete custom emoji'
+ end
+ end
+
+ context 'when user' do
+ context 'is maintainer' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it_behaves_like 'deletes custom emoji'
+ end
+
+ context 'is owner' do
+ before do
+ group.add_owner(user)
+ end
+
+ it_behaves_like 'deletes custom emoji'
+ end
+
+ context 'is developer and creator of the emoji' do
+ before do
+ group.add_developer(user)
+ custom_emoji.update_attribute(:creator, user)
+ end
+
+ it_behaves_like 'deletes custom emoji'
+ end
+ end
+ end
+end
diff --git a/spec/graphql/mutations/customer_relations/organizations/create_spec.rb b/spec/graphql/mutations/customer_relations/organizations/create_spec.rb
new file mode 100644
index 00000000000..ab430b9240b
--- /dev/null
+++ b/spec/graphql/mutations/customer_relations/organizations/create_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::CustomerRelations::Organizations::Create do
+ let_it_be(:user) { create(:user) }
+
+ let(:valid_params) do
+ attributes_for(:organization,
+ group: group,
+ description: 'This company is super important!',
+ default_rate: 1_000
+ )
+ end
+
+ describe 'create organizations mutation' do
+ describe '#resolve' do
+ subject(:resolve_mutation) do
+ described_class.new(object: nil, context: { current_user: user }, field: nil).resolve(
+ **valid_params,
+ group_id: group.to_global_id
+ )
+ end
+
+ context 'when the user does not have permission' do
+ let_it_be(:group) { create(:group) }
+
+ before do
+ group.add_guest(user)
+ end
+
+ it 'raises an error' do
+ expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when the user has permission' do
+ let_it_be(:group) { create(:group) }
+
+ before_all do
+ group.add_reporter(user)
+ end
+
+ context 'when the feature is disabled' do
+ before do
+ stub_feature_flags(customer_relations: false)
+ end
+
+ it 'raises an error' do
+ expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when the params are invalid' do
+ before do
+ valid_params[:name] = nil
+ end
+
+ it 'returns the validation error' do
+ expect(resolve_mutation[:errors]).to eq(["Name can't be blank"])
+ end
+ end
+
+ context 'when the user has permission to create an organization' do
+ it 'creates organization with correct values' do
+ expect(resolve_mutation[:organization]).to have_attributes(valid_params)
+ end
+ end
+ end
+ end
+ end
+
+ specify { expect(described_class).to require_graphql_authorizations(:admin_organization) }
+end
diff --git a/spec/graphql/mutations/customer_relations/organizations/update_spec.rb b/spec/graphql/mutations/customer_relations/organizations/update_spec.rb
new file mode 100644
index 00000000000..f5aa6c00301
--- /dev/null
+++ b/spec/graphql/mutations/customer_relations/organizations/update_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::CustomerRelations::Organizations::Update do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:name) { 'GitLab' }
+ let_it_be(:default_rate) { 1000.to_f }
+ let_it_be(:description) { 'VIP' }
+
+ let(:organization) { create(:organization, group: group) }
+ let(:attributes) do
+ {
+ id: organization.to_global_id,
+ name: name,
+ default_rate: default_rate,
+ description: description
+ }
+ end
+
+ describe '#resolve' do
+ subject(:resolve_mutation) do
+ described_class.new(object: nil, context: { current_user: user }, field: nil).resolve(
+ attributes
+ )
+ end
+
+ context 'when the user does not have permission to update an organization' do
+ let_it_be(:group) { create(:group) }
+
+ before do
+ group.add_guest(user)
+ end
+
+ it 'raises an error' do
+ expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when the organization does not exist' do
+ let_it_be(:group) { create(:group) }
+
+ it 'raises an error' do
+ attributes[:id] = 'gid://gitlab/CustomerRelations::Organization/999'
+
+ expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when the user has permission to update an organization' do
+ let_it_be(:group) { create(:group) }
+
+ before_all do
+ group.add_reporter(user)
+ end
+
+ it 'updates the organization with correct values' do
+ expect(resolve_mutation[:organization]).to have_attributes(attributes)
+ end
+
+ context 'when the feature is disabled' do
+ before do
+ stub_feature_flags(customer_relations: false)
+ end
+
+ it 'raises an error' do
+ expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+ end
+ end
+
+ specify { expect(described_class).to require_graphql_authorizations(:admin_organization) }
+end
diff --git a/spec/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb b/spec/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb
new file mode 100644
index 00000000000..792e87f0d25
--- /dev/null
+++ b/spec/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::DependencyProxy::ImageTtlGroupPolicy::Update do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be_with_reload(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+
+ let(:params) { { group_path: group.full_path } }
+
+ specify { expect(described_class).to require_graphql_authorizations(:admin_dependency_proxy) }
+
+ describe '#resolve' do
+ subject { described_class.new(object: group, context: { current_user: user }, field: nil).resolve(**params) }
+
+ shared_examples 'returning a success' do
+ it 'returns the dependency proxy image ttl group policy with no errors' do
+ expect(subject).to eq(
+ dependency_proxy_image_ttl_policy: ttl_policy,
+ errors: []
+ )
+ end
+ end
+
+ shared_examples 'updating the dependency proxy image ttl policy' do
+ it_behaves_like 'updating the dependency proxy image ttl policy attributes',
+ from: { enabled: true, ttl: 90 },
+ to: { enabled: false, ttl: 2 }
+
+ it_behaves_like 'returning a success'
+
+ context 'with invalid params' do
+ let_it_be(:params) { { group_path: group.full_path, enabled: nil } }
+
+ it "doesn't create the dependency proxy image ttl policy" do
+ expect { subject }.not_to change { DependencyProxy::ImageTtlGroupPolicy.count }
+ end
+
+ it 'does not update' do
+ expect { subject }
+ .not_to change { ttl_policy.reload.enabled }
+ end
+
+ it 'returns an error' do
+ expect(subject).to eq(
+ dependency_proxy_image_ttl_policy: nil,
+ errors: ['Enabled is not included in the list']
+ )
+ end
+ end
+ end
+
+ shared_examples 'denying access to dependency proxy image ttl policy' do
+ it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ before do
+ stub_config(dependency_proxy: { enabled: true })
+ end
+
+ context 'with existing dependency proxy image ttl policy' do
+ let_it_be(:ttl_policy) { create(:image_ttl_group_policy, group: group) }
+ let_it_be(:params) do
+ { group_path: group.full_path,
+ enabled: false,
+ ttl: 2 }
+ end
+
+ where(:user_role, :shared_examples_name) do
+ :maintainer | 'updating the dependency proxy image ttl policy'
+ :developer | 'updating the dependency proxy image ttl policy'
+ :reporter | 'denying access to dependency proxy image ttl policy'
+ :guest | 'denying access to dependency proxy image ttl policy'
+ :anonymous | 'denying access to dependency proxy image ttl policy'
+ end
+
+ with_them do
+ before do
+ group.send("add_#{user_role}", user) unless user_role == :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name]
+ end
+ end
+
+ context 'without existing dependency proxy image ttl policy' do
+ let_it_be(:ttl_policy) { group.dependency_proxy_image_ttl_policy }
+
+ where(:user_role, :shared_examples_name) do
+ :maintainer | 'creating the dependency proxy image ttl policy'
+ :developer | 'creating the dependency proxy image ttl policy'
+ :reporter | 'denying access to dependency proxy image ttl policy'
+ :guest | 'denying access to dependency proxy image ttl policy'
+ :anonymous | 'denying access to dependency proxy image ttl policy'
+ end
+
+ with_them do
+ before do
+ group.send("add_#{user_role}", user) unless user_role == :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name]
+ end
+ end
+ 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 6ffc8b045e9..26040f4ec1a 100644
--- a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb
@@ -17,21 +17,38 @@ RSpec.describe Resolvers::BoardListIssuesResolver do
# auth is handled by the parent object
context 'when authorized' do
- 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) }
+ let!(:issue1) { create(:issue, project: project, labels: [label], relative_position: 10, milestone: started_milestone) }
+ let!(:issue2) { create(:issue, project: project, labels: [label, label2], relative_position: 12, milestone: started_milestone) }
+ let!(:issue3) { create(:issue, project: project, labels: [label, label3], relative_position: 10, milestone: future_milestone) }
+ let!(:issue4) { create(:issue, project: project, labels: [label], relative_position: nil) }
- it 'returns the issues in the correct order' do
+ let(:wildcard_started) { 'STARTED' }
+ let(:filters) { { milestone_title: ["started"], milestone_wildcard_id: wildcard_started } }
+
+ it 'raises a mutually exclusive filter error when milstone wildcard and title are provided' do
+ expect do
+ resolve_board_list_issues(args: { filters: filters })
+ end.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+
+ it 'returns issues in the correct order with non-nil relative positions', :aggregate_failures do
# by relative_position and then ID
- issues = resolve_board_list_issues
+ result = resolve_board_list_issues
- expect(issues.map(&:id)).to eq [issue3.id, issue1.id, issue2.id]
+ expect(result.map(&:id)).to eq [issue3.id, issue1.id, issue2.id, issue4.id]
+ expect(result.map(&:relative_position)).not_to include(nil)
end
it 'finds only issues matching filters' do
result = resolve_board_list_issues(args: { filters: { label_name: [label.title], not: { label_name: [label2.title] } } })
- expect(result).to match_array([issue1, issue3])
+ expect(result).to match_array([issue1, issue3, issue4])
+ end
+
+ it 'finds only issues filtered by milestone wildcard' do
+ result = resolve_board_list_issues(args: { filters: { milestone_wildcard_id: wildcard_started } })
+
+ expect(result).to match_array([issue1, issue2])
end
it 'finds only issues matching search param' do
@@ -49,7 +66,7 @@ RSpec.describe Resolvers::BoardListIssuesResolver do
it 'accepts assignee wildcard id NONE' do
result = resolve_board_list_issues(args: { filters: { assignee_wildcard_id: 'NONE' } })
- expect(result).to match_array([issue1, issue2, issue3])
+ expect(result).to match_array([issue1, issue2, issue3, issue4])
end
it 'accepts assignee wildcard id ANY' do
@@ -71,6 +88,9 @@ RSpec.describe Resolvers::BoardListIssuesResolver do
let(:board_parent) { user_project }
let(:project) { user_project }
+ let_it_be(:started_milestone) { create(:milestone, project: user_project, title: 'started milestone', start_date: 1.day.ago, due_date: 1.day.from_now) }
+ let_it_be(:future_milestone) { create(:milestone, project: user_project, title: 'future milestone', start_date: 1.day.from_now) }
+
it_behaves_like 'group and project board list issues resolver'
end
@@ -84,11 +104,14 @@ RSpec.describe Resolvers::BoardListIssuesResolver do
let(:board_parent) { group }
let!(:project) { create(:project, :private, group: group) }
+ let_it_be(:started_milestone) { create(:milestone, group: group, title: 'started milestone', start_date: 1.day.ago, due_date: 1.day.from_now) }
+ let_it_be(:future_milestone) { create(:milestone, group: group, title: 'future milestone', start_date: 1.day.from_now) }
+
it_behaves_like 'group and project board list issues resolver'
end
end
def resolve_board_list_issues(args: {}, current_user: user)
- resolve(described_class, obj: list, args: args, ctx: { current_user: current_user })
+ resolve(described_class, obj: list, args: args, ctx: { current_user: current_user }).items
end
end
diff --git a/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb b/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb
new file mode 100644
index 00000000000..89a2437a189
--- /dev/null
+++ b/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Ci::GroupRunnersResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ subject { resolve(described_class, obj: obj, ctx: { current_user: user }, args: args) }
+
+ include_context 'runners resolver setup'
+
+ let(:obj) { group }
+ let(:args) { {} }
+
+ # First, we can do a couple of basic real tests to verify common cases. That ensures that the code works.
+ context 'when user cannot see runners' do
+ it 'returns no runners' do
+ expect(subject.items.to_a).to eq([])
+ end
+ end
+
+ context 'with user as group owner' do
+ before do
+ group.add_owner(user)
+ end
+
+ it 'returns all the runners' do
+ expect(subject.items.to_a).to contain_exactly(inactive_project_runner, offline_project_runner, group_runner, subgroup_runner)
+ end
+
+ context 'with membership direct' do
+ let(:args) { { membership: :direct } }
+
+ it 'returns only direct runners' do
+ expect(subject.items.to_a).to contain_exactly(group_runner)
+ end
+ end
+ end
+
+ # Then, we can check specific edge cases for this resolver
+ context 'with obj set to nil' do
+ let(:obj) { nil }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error('Expected group missing')
+ end
+ end
+
+ context 'with obj not set to group' do
+ let(:obj) { build(:project) }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error('Expected group missing')
+ end
+ end
+
+ # Here we have a mocked part. We assume that all possible edge cases are covered in RunnersFinder spec. So we don't need to test them twice.
+ # 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
+ {
+ status: 'active',
+ type: :group_type,
+ tag_list: ['active_runner'],
+ search: 'abc',
+ sort: :contacted_asc,
+ membership: :descendants
+ }
+ end
+
+ let(:expected_params) do
+ {
+ status_status: 'active',
+ type_type: :group_type,
+ tag_name: ['active_runner'],
+ preload: { tag_name: nil },
+ search: 'abc',
+ sort: 'contacted_asc',
+ membership: :descendants,
+ group: group
+ }
+ 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])
+
+ expect(subject.items.to_a).to eq([:execute_return_value])
+ end
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/ci/runners_resolver_spec.rb b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
index 5ac15d5729f..bb8dadeca40 100644
--- a/spec/graphql/resolvers/ci/runners_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
@@ -5,185 +5,70 @@ require 'spec_helper'
RSpec.describe Resolvers::Ci::RunnersResolver do
include GraphqlHelpers
- let_it_be(:user) { create_default(:user, :admin) }
- let_it_be(:group) { create(:group, :public) }
- let_it_be(:project) { create(:project, :repository, :public) }
-
- let_it_be(:inactive_project_runner) do
- create(:ci_runner, :project, projects: [project], description: 'inactive project runner', token: 'abcdef', active: false, contacted_at: 1.minute.ago, tag_list: %w(project_runner))
- end
-
- let_it_be(:offline_project_runner) do
- create(:ci_runner, :project, projects: [project], description: 'offline project runner', token: 'defghi', contacted_at: 1.day.ago, tag_list: %w(project_runner active_runner))
- end
-
- let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group], token: 'mnopqr', description: 'group runner', contacted_at: 1.second.ago) }
- let_it_be(:instance_runner) { create(:ci_runner, :instance, description: 'shared runner', token: 'stuvxz', contacted_at: 2.minutes.ago, tag_list: %w(instance_runner active_runner)) }
-
describe '#resolve' do
- subject { resolve(described_class, ctx: { current_user: user }, args: args).items.to_a }
-
- let(:args) do
- {}
- end
-
- context 'when the user cannot see runners' do
- let(:user) { create(:user) }
-
- it 'returns no runners' do
- is_expected.to be_empty
- end
- end
-
- context 'without sort' do
- it 'returns all the runners' do
- is_expected.to contain_exactly(inactive_project_runner, offline_project_runner, group_runner, instance_runner)
- end
- end
-
- context 'with a sort argument' do
- context "set to :contacted_asc" do
- let(:args) do
- { sort: :contacted_asc }
- end
-
- it { is_expected.to eq([offline_project_runner, instance_runner, inactive_project_runner, group_runner]) }
- end
-
- context "set to :contacted_desc" do
- let(:args) do
- { sort: :contacted_desc }
- end
-
- it { is_expected.to eq([offline_project_runner, instance_runner, inactive_project_runner, group_runner].reverse) }
- end
-
- context "set to :created_at_desc" do
- let(:args) do
- { sort: :created_at_desc }
- end
+ let(:obj) { nil }
+ let(:args) { {} }
- it { is_expected.to eq([instance_runner, group_runner, offline_project_runner, inactive_project_runner]) }
- end
-
- context "set to :created_at_asc" do
- let(:args) do
- { sort: :created_at_asc }
- end
-
- it { is_expected.to eq([instance_runner, group_runner, offline_project_runner, inactive_project_runner].reverse) }
- end
- end
+ subject { resolve(described_class, obj: obj, ctx: { current_user: user }, args: args) }
- context 'when type is filtered' do
- let(:args) do
- { type: runner_type.to_s }
- end
+ include_context 'runners resolver setup'
- context 'to instance runners' do
- let(:runner_type) { :instance_type }
+ # First, we can do a couple of basic real tests to verify common cases. That ensures that the code works.
+ context 'when user cannot see runners' do
+ let(:user) { build(:user) }
- it 'returns the instance runner' do
- is_expected.to contain_exactly(instance_runner)
- end
- end
-
- context 'to group runners' do
- let(:runner_type) { :group_type }
-
- it 'returns the group runner' do
- is_expected.to contain_exactly(group_runner)
- end
- end
-
- context 'to project runners' do
- let(:runner_type) { :project_type }
-
- it 'returns the project runner' do
- is_expected.to contain_exactly(inactive_project_runner, offline_project_runner)
- end
+ it 'returns no runners' do
+ expect(subject.items.to_a).to eq([])
end
end
- context 'when status is filtered' do
- let(:args) do
- { status: runner_status.to_s }
- end
-
- context 'to active runners' do
- let(:runner_status) { :active }
-
- it 'returns the instance and group runners' do
- is_expected.to contain_exactly(offline_project_runner, group_runner, instance_runner)
- end
- end
-
- context 'to offline runners' do
- let(:runner_status) { :offline }
+ context 'when user can see runners' do
+ let(:obj) { nil }
- it 'returns the offline project runner' do
- is_expected.to contain_exactly(offline_project_runner)
- end
+ it 'returns all the runners' do
+ expect(subject.items.to_a).to contain_exactly(inactive_project_runner, offline_project_runner, group_runner, subgroup_runner, instance_runner)
end
end
- context 'when tag list is filtered' do
- let(:args) do
- { tag_list: tag_list }
- end
-
- context 'with "project_runner" tag' do
- let(:tag_list) { ['project_runner'] }
+ # Then, we can check specific edge cases for this resolver
+ context 'with obj not set to nil' do
+ let(:obj) { build(:project) }
- it 'returns the project_runner runners' do
- is_expected.to contain_exactly(offline_project_runner, inactive_project_runner)
- end
- end
-
- context 'with "project_runner" and "active_runner" tags as comma-separated string' do
- let(:tag_list) { ['project_runner,active_runner'] }
-
- it 'returns the offline_project_runner runner' do
- is_expected.to contain_exactly(offline_project_runner)
- end
- end
-
- context 'with "active_runner" and "instance_runner" tags as array' do
- let(:tag_list) { %w[instance_runner active_runner] }
-
- it 'returns the offline_project_runner runner' do
- is_expected.to contain_exactly(instance_runner)
- end
+ it 'raises an error' do
+ expect { subject }.to raise_error(a_string_including('Unexpected parent type'))
end
end
- context 'when text is filtered' do
+ # Here we have a mocked part. We assume that all possible edge cases are covered in RunnersFinder spec. So we don't need to test them twice.
+ # 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
- { search: search_term }
- end
-
- context 'to "project"' do
- let(:search_term) { 'project' }
-
- it 'returns both project runners' do
- is_expected.to contain_exactly(inactive_project_runner, offline_project_runner)
- end
- end
-
- context 'to "group"' do
- let(:search_term) { 'group' }
-
- it 'returns group runner' do
- is_expected.to contain_exactly(group_runner)
- end
- end
-
- context 'to "defghi"' do
- let(:search_term) { 'defghi' }
-
- it 'returns runners containing term in token' do
- is_expected.to contain_exactly(offline_project_runner)
- end
+ {
+ status: 'active',
+ type: :instance_type,
+ tag_list: ['active_runner'],
+ search: 'abc',
+ sort: :contacted_asc
+ }
+ end
+
+ let(:expected_params) do
+ {
+ 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
+ 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])
+
+ expect(subject.items.to_a).to eq([:execute_return_value])
end
end
end
diff --git a/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
index 6f6855c8f84..865e892b12d 100644
--- a/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
+++ b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe ResolvesPipelines do
project.add_developer(current_user)
end
- it { is_expected.to have_graphql_arguments(:status, :ref, :sha) }
+ it { is_expected.to have_graphql_arguments(:status, :ref, :sha, :source) }
it 'finds all pipelines' do
expect(resolve_pipelines).to contain_exactly(pipeline, failed_pipeline, ref_pipeline, sha_pipeline)
@@ -45,6 +45,30 @@ RSpec.describe ResolvesPipelines do
expect(resolve_pipelines(sha: 'deadbeef')).to contain_exactly(sha_pipeline)
end
+ context 'filtering by source' do
+ let_it_be(:source_pipeline) { create(:ci_pipeline, project: project, source: 'web') }
+
+ context 'when `dast_view_scans` feature flag is disabled' do
+ before do
+ stub_feature_flags(dast_view_scans: false)
+ end
+
+ it 'does not filter by source' do
+ expect(resolve_pipelines(source: 'web')).to contain_exactly(pipeline, failed_pipeline, ref_pipeline, sha_pipeline, source_pipeline)
+ end
+ end
+
+ context 'when `dast_view_scans` feature flag is enabled' do
+ it 'does filter by source' do
+ expect(resolve_pipelines(source: 'web')).to contain_exactly(source_pipeline)
+ end
+
+ it 'returns all the pipelines' do
+ expect(resolve_pipelines).to contain_exactly(pipeline, failed_pipeline, ref_pipeline, sha_pipeline, source_pipeline)
+ end
+ end
+ end
+
it 'does not return any pipelines if the user does not have access' do
expect(resolve_pipelines({}, {})).to be_empty
end
diff --git a/spec/graphql/resolvers/group_resolver_spec.rb b/spec/graphql/resolvers/group_resolver_spec.rb
index a03e7854177..ed406d14772 100644
--- a/spec/graphql/resolvers/group_resolver_spec.rb
+++ b/spec/graphql/resolvers/group_resolver_spec.rb
@@ -20,10 +20,15 @@ RSpec.describe Resolvers::GroupResolver do
end
it 'resolves an unknown full_path to nil' do
- result = batch_sync { resolve_group('unknown/project') }
+ result = batch_sync { resolve_group('unknown/group') }
expect(result).to be_nil
end
+
+ it 'treats group full path as case insensitive' do
+ result = batch_sync { resolve_group(group1.full_path.upcase) }
+ expect(result).to eq group1
+ end
end
def resolve_group(full_path)
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index 6e187e57729..e992b2b04ae 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Resolvers::IssuesResolver do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
@@ -19,6 +20,7 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be(:issue4) { create(:issue) }
let_it_be(:label1) { create(:label, project: project) }
let_it_be(:label2) { create(:label, project: project) }
+ let_it_be(:upvote_award) { create(:award_emoji, :upvote, user: current_user, awardable: issue1) }
specify do
expect(described_class).to have_nullable_graphql_type(Types::IssueType.connection_type)
@@ -27,6 +29,7 @@ RSpec.describe Resolvers::IssuesResolver do
context "with a project" do
before_all do
project.add_developer(current_user)
+ project.add_reporter(reporter)
create(:label_link, label: label1, target: issue1)
create(:label_link, label: label1, target: issue2)
create(:label_link, label: label2, target: issue2)
@@ -198,6 +201,27 @@ RSpec.describe Resolvers::IssuesResolver do
end
end
+ context 'filtering by reaction emoji' do
+ let_it_be(:downvoted_issue) { create(:issue, project: project) }
+ let_it_be(:downvote_award) { create(:award_emoji, :downvote, user: current_user, awardable: downvoted_issue) }
+
+ it 'filters by reaction emoji' do
+ expect(resolve_issues(my_reaction_emoji: upvote_award.name)).to contain_exactly(issue1)
+ end
+
+ it 'filters by reaction emoji wildcard "none"' do
+ expect(resolve_issues(my_reaction_emoji: 'none')).to contain_exactly(issue2)
+ end
+
+ it 'filters by reaction emoji wildcard "any"' do
+ expect(resolve_issues(my_reaction_emoji: 'any')).to contain_exactly(issue1, downvoted_issue)
+ end
+
+ it 'filters by negated reaction emoji' do
+ expect(resolve_issues(not: { my_reaction_emoji: downvote_award.name })).to contain_exactly(issue1, issue2)
+ end
+ end
+
context 'when searching issues' do
it 'returns correct issues' do
expect(resolve_issues(search: 'foo')).to contain_exactly(issue2)
@@ -235,6 +259,14 @@ RSpec.describe Resolvers::IssuesResolver do
it 'returns issues without the specified assignee_id' do
expect(resolve_issues(not: { assignee_id: [assignee.id] })).to contain_exactly(issue1)
end
+
+ context 'when filtering by negated author' do
+ let_it_be(:issue_by_reporter) { create(:issue, author: reporter, project: project, state: :opened) }
+
+ it 'returns issues without the specified author_username' do
+ expect(resolve_issues(not: { author_username: issue1.author.username })).to contain_exactly(issue_by_reporter)
+ end
+ end
end
describe 'sorting' do
@@ -382,6 +414,22 @@ RSpec.describe Resolvers::IssuesResolver do
end
end
end
+
+ context 'when sorting by title' do
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:issue1) { create(:issue, project: project, title: 'foo') }
+ let_it_be(:issue2) { create(:issue, project: project, title: 'bar') }
+ let_it_be(:issue3) { create(:issue, project: project, title: 'baz') }
+ let_it_be(:issue4) { create(:issue, project: project, title: 'Baz 2') }
+
+ it 'sorts issues ascending' do
+ expect(resolve_issues(sort: :title_asc).to_a).to eq [issue2, issue3, issue4, issue1]
+ end
+
+ it 'sorts issues descending' do
+ expect(resolve_issues(sort: :title_desc).to_a).to eq [issue1, issue4, issue3, issue2]
+ end
+ end
end
it 'returns issues user can see' do
diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
index 64ee0d4f9cc..a897acf7eba 100644
--- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
@@ -294,16 +294,6 @@ RSpec.describe Resolvers::MergeRequestsResolver do
nils_last(mr.metrics.merged_at)
end
- context 'when label filter is given and the optimized_issuable_label_filter feature flag is off' do
- before do
- stub_feature_flags(optimized_issuable_label_filter: false)
- end
-
- it 'does not raise PG::GroupingError' do
- expect { resolve_mr(project, sort: :merged_at_desc, labels: %w[a b]) }.not_to raise_error
- end
- end
-
context 'when sorting by closed at' do
before do
merge_request_1.metrics.update!(latest_closed_at: 10.days.ago)
diff --git a/spec/graphql/resolvers/project_resolver_spec.rb b/spec/graphql/resolvers/project_resolver_spec.rb
index d0661c27b95..cd3fdc788e6 100644
--- a/spec/graphql/resolvers/project_resolver_spec.rb
+++ b/spec/graphql/resolvers/project_resolver_spec.rb
@@ -25,6 +25,11 @@ RSpec.describe Resolvers::ProjectResolver do
expect(result).to be_nil
end
+
+ it 'treats project full path as case insensitive' do
+ result = batch_sync { resolve_project(project1.full_path.upcase) }
+ expect(result).to eq project1
+ end
end
it 'does not increase complexity depending on number of load limits' do
diff --git a/spec/graphql/resolvers/users/groups_resolver_spec.rb b/spec/graphql/resolvers/users/groups_resolver_spec.rb
new file mode 100644
index 00000000000..0fdb6da5ae9
--- /dev/null
+++ b/spec/graphql/resolvers/users/groups_resolver_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Users::GroupsResolver do
+ include GraphqlHelpers
+ include AdminModeHelper
+
+ describe '#resolve' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:guest_group) { create(:group, name: 'public guest', path: 'public-guest') }
+ let_it_be(:private_maintainer_group) { create(:group, :private, name: 'b private maintainer', path: 'b-private-maintainer') }
+ let_it_be(:public_developer_group) { create(:group, project_creation_level: nil, name: 'c public developer', path: 'c-public-developer') }
+ let_it_be(:public_maintainer_group) { create(:group, name: 'a public maintainer', path: 'a-public-maintainer') }
+
+ subject(:resolved_items) { resolve_groups(args: group_arguments, current_user: current_user, obj: resolver_object) }
+
+ let(:group_arguments) { {} }
+ let(:current_user) { user }
+ let(:resolver_object) { user }
+
+ before_all do
+ guest_group.add_guest(user)
+ private_maintainer_group.add_maintainer(user)
+ public_developer_group.add_developer(user)
+ public_maintainer_group.add_maintainer(user)
+ end
+
+ context 'when paginatable_namespace_drop_down_for_project_creation feature flag is disabled' do
+ before do
+ stub_feature_flags(paginatable_namespace_drop_down_for_project_creation: false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when resolver object is current user' do
+ context 'when permission is :create_projects' do
+ let(:group_arguments) { { permission_scope: :create_projects } }
+
+ specify do
+ is_expected.to match(
+ [
+ public_maintainer_group,
+ private_maintainer_group,
+ public_developer_group
+ ]
+ )
+ end
+ end
+
+ specify do
+ is_expected.to match(
+ [
+ public_maintainer_group,
+ private_maintainer_group,
+ public_developer_group,
+ guest_group
+ ]
+ )
+ end
+
+ context 'when search is provided' do
+ let(:group_arguments) { { search: 'maintainer' } }
+
+ specify do
+ is_expected.to match(
+ [
+ public_maintainer_group,
+ private_maintainer_group
+ ]
+ )
+ end
+ end
+ end
+
+ context 'when resolver object is different from current user' do
+ let(:current_user) { create(:user) }
+
+ it { is_expected.to be_nil }
+
+ context 'when current_user is admin' do
+ let(:current_user) { create(:user, :admin) }
+
+ before do
+ enable_admin_mode!(current_user)
+ end
+
+ specify do
+ is_expected.to match(
+ [
+ public_maintainer_group,
+ private_maintainer_group,
+ public_developer_group,
+ guest_group
+ ]
+ )
+ end
+ end
+ end
+ end
+
+ def resolve_groups(args:, current_user:, obj:)
+ resolve(described_class, args: args, ctx: { current_user: current_user }, obj: obj)&.items
+ end
+end
diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb
index 54fe0c4b707..e95a7da4fe5 100644
--- a/spec/graphql/types/ci/job_type_spec.rb
+++ b/spec/graphql/types/ci/job_type_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe Types::Ci::JobType do
specify { expect(described_class.graphql_name).to eq('CiJob') }
- specify { expect(described_class).to require_graphql_authorizations(:read_commit_status) }
specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Ci::Job) }
it 'exposes the expected fields' do
diff --git a/spec/graphql/types/customer_relations/contact_type_spec.rb b/spec/graphql/types/customer_relations/contact_type_spec.rb
new file mode 100644
index 00000000000..a51ee705fb0
--- /dev/null
+++ b/spec/graphql/types/customer_relations/contact_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CustomerRelationsContact'] do
+ let(:fields) { %i[id organization first_name last_name phone email description created_at updated_at] }
+
+ it { expect(described_class.graphql_name).to eq('CustomerRelationsContact') }
+ it { expect(described_class).to have_graphql_fields(fields) }
+ it { expect(described_class).to require_graphql_authorizations(:read_contact) }
+end
diff --git a/spec/graphql/types/customer_relations/organization_type_spec.rb b/spec/graphql/types/customer_relations/organization_type_spec.rb
new file mode 100644
index 00000000000..2562748477c
--- /dev/null
+++ b/spec/graphql/types/customer_relations/organization_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CustomerRelationsOrganization'] do
+ let(:fields) { %i[id name default_rate description created_at updated_at] }
+
+ it { expect(described_class.graphql_name).to eq('CustomerRelationsOrganization') }
+ it { expect(described_class).to have_graphql_fields(fields) }
+ it { expect(described_class).to require_graphql_authorizations(:read_organization) }
+end
diff --git a/spec/graphql/types/dependency_proxy/blob_type_spec.rb b/spec/graphql/types/dependency_proxy/blob_type_spec.rb
new file mode 100644
index 00000000000..e1c8471975e
--- /dev/null
+++ b/spec/graphql/types/dependency_proxy/blob_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['DependencyProxyBlob'] do
+ it 'includes dependency proxy blob fields' do
+ expected_fields = %w[
+ file_name size created_at updated_at
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/dependency_proxy/group_setting_type_spec.rb b/spec/graphql/types/dependency_proxy/group_setting_type_spec.rb
new file mode 100644
index 00000000000..7c6d7b8aece
--- /dev/null
+++ b/spec/graphql/types/dependency_proxy/group_setting_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['DependencyProxySetting'] do
+ it 'includes dependency proxy blob fields' do
+ expected_fields = %w[
+ enabled
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/dependency_proxy/image_ttl_group_policy_type_spec.rb b/spec/graphql/types/dependency_proxy/image_ttl_group_policy_type_spec.rb
new file mode 100644
index 00000000000..46347e0434f
--- /dev/null
+++ b/spec/graphql/types/dependency_proxy/image_ttl_group_policy_type_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['DependencyProxyImageTtlGroupPolicy'] do
+ it { expect(described_class.graphql_name).to eq('DependencyProxyImageTtlGroupPolicy') }
+
+ it { expect(described_class.description).to eq('Group-level Dependency Proxy TTL policy settings') }
+
+ it { expect(described_class).to require_graphql_authorizations(:read_dependency_proxy) }
+
+ it 'includes dependency proxy image ttl policy fields' do
+ expected_fields = %w[enabled ttl created_at updated_at]
+
+ expect(described_class).to have_graphql_fields(*expected_fields).only
+ end
+end
diff --git a/spec/graphql/types/dependency_proxy/manifest_type_spec.rb b/spec/graphql/types/dependency_proxy/manifest_type_spec.rb
new file mode 100644
index 00000000000..18cc89adfcb
--- /dev/null
+++ b/spec/graphql/types/dependency_proxy/manifest_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['DependencyProxyManifest'] do
+ it 'includes dependency proxy manifest fields' do
+ expected_fields = %w[
+ file_name image_name size created_at updated_at digest
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb
index 33250f8e6af..dca2c930eea 100644
--- a/spec/graphql/types/group_type_spec.rb
+++ b/spec/graphql/types/group_type_spec.rb
@@ -18,7 +18,11 @@ RSpec.describe GitlabSchema.types['Group'] do
two_factor_grace_period auto_devops_enabled emails_disabled
mentions_disabled parent boards milestones group_members
merge_requests container_repositories container_repositories_count
- packages shared_runners_setting timelogs
+ packages dependency_proxy_setting dependency_proxy_manifests
+ dependency_proxy_blobs dependency_proxy_image_count
+ dependency_proxy_blob_count dependency_proxy_total_size
+ dependency_proxy_image_prefix dependency_proxy_image_ttl_policy
+ shared_runners_setting timelogs organizations contacts
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/issue_type_spec.rb b/spec/graphql/types/issue_type_spec.rb
index b0aa11ee5ad..559f347810b 100644
--- a/spec/graphql/types/issue_type_spec.rb
+++ b/spec/graphql/types/issue_type_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe GitlabSchema.types['Issue'] do
it 'has specific fields' do
fields = %i[id iid title description state reference author assignees updated_by participants labels milestone due_date
- confidential discussion_locked upvotes downvotes merge_requests_count user_notes_count user_discussions_count web_path web_url relative_position
+ confidential hidden discussion_locked upvotes downvotes merge_requests_count user_notes_count user_discussions_count web_path web_url relative_position
emails_disabled subscribed time_estimate total_time_spent human_time_estimate human_total_time_spent closed_at created_at updated_at task_completion_status
design_collection alert_management_alert severity current_user_todos moved moved_to
create_note_email timelogs project_id]
@@ -201,4 +201,54 @@ RSpec.describe GitlabSchema.types['Issue'] do
end
end
end
+
+ describe 'hidden', :enable_admin_mode do
+ let_it_be(:admin) { create(:user, :admin)}
+ let_it_be(:banned_user) { create(:user, :banned) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:hidden_issue) { create(:issue, project: project, author: banned_user) }
+ let_it_be(:visible_issue) { create(:issue, project: project, author: user) }
+
+ let(:issue) { hidden_issue }
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ issue(iid: "#{issue.iid}") {
+ hidden
+ }
+ }
+ }
+ )
+ end
+
+ subject { GitlabSchema.execute(query, context: { current_user: admin }).as_json }
+
+ context 'when `ban_user_feature_flag` is enabled' do
+ context 'when issue is hidden' do
+ it 'returns `true`' do
+ expect(subject.dig('data', 'project', 'issue', 'hidden')).to eq(true)
+ end
+ end
+
+ context 'when issue is visible' do
+ let(:issue) { visible_issue }
+
+ it 'returns `false`' do
+ expect(subject.dig('data', 'project', 'issue', 'hidden')).to eq(false)
+ end
+ end
+ end
+
+ context 'when `ban_user_feature_flag` is disabled' do
+ before do
+ stub_feature_flags(ban_user_feature_flag: false)
+ end
+
+ it 'returns `nil`' do
+ expect(subject.dig('data', 'project', 'issue', 'hidden')).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/graphql/types/merge_requests/reviewer_type_spec.rb b/spec/graphql/types/merge_requests/reviewer_type_spec.rb
index 4ede8e5788f..4d357a922f8 100644
--- a/spec/graphql/types/merge_requests/reviewer_type_spec.rb
+++ b/spec/graphql/types/merge_requests/reviewer_type_spec.rb
@@ -33,6 +33,7 @@ RSpec.describe GitlabSchema.types['MergeRequestReviewer'] do
merge_request_interaction
namespace
timelogs
+ groups
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/project_statistics_type_spec.rb b/spec/graphql/types/project_statistics_type_spec.rb
index 407ce82e73a..f515907b6a8 100644
--- a/spec/graphql/types/project_statistics_type_spec.rb
+++ b/spec/graphql/types/project_statistics_type_spec.rb
@@ -6,6 +6,6 @@ RSpec.describe GitlabSchema.types['ProjectStatistics'] 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, :commit_count,
- :wiki_size, :snippets_size, :uploads_size)
+ :wiki_size, :snippets_size, :pipeline_artifacts_size, :uploads_size)
end
end
diff --git a/spec/graphql/types/terraform/state_version_type_spec.rb b/spec/graphql/types/terraform/state_version_type_spec.rb
index 18f869e4f1f..b015a2045da 100644
--- a/spec/graphql/types/terraform/state_version_type_spec.rb
+++ b/spec/graphql/types/terraform/state_version_type_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['TerraformStateVersion'] do
+ include GraphqlHelpers
+
it { expect(described_class.graphql_name).to eq('TerraformStateVersion') }
it { expect(described_class).to require_graphql_authorizations(:read_terraform_state) }
@@ -19,4 +21,60 @@ RSpec.describe GitlabSchema.types['TerraformStateVersion'] do
it { expect(described_class.fields['createdAt'].type).to be_non_null }
it { expect(described_class.fields['updatedAt'].type).to be_non_null }
end
+
+ describe 'query' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:terraform_state) { create(:terraform_state, :with_version, :locked, project: project) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ let(:query) do
+ <<~GRAPHQL
+ query {
+ project(fullPath: "#{project.full_path}") {
+ terraformState(name: "#{terraform_state.name}") {
+ latestVersion {
+ id
+ job {
+ name
+ }
+ }
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ subject(:execute) { GitlabSchema.execute(query, context: { current_user: user }).as_json }
+
+ shared_examples 'returning latest version' do
+ it 'returns latest version of terraform state' do
+ expect(execute.dig('data', 'project', 'terraformState', 'latestVersion', 'id')).to eq(
+ global_id_of(terraform_state.latest_version)
+ )
+ end
+ end
+
+ it_behaves_like 'returning latest version'
+
+ it 'returns job of the latest version' do
+ expect(execute.dig('data', 'project', 'terraformState', 'latestVersion', 'job')).to be_present
+ end
+
+ context 'when user cannot read jobs' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_commit_status, terraform_state.latest_version).and_return(false)
+ end
+
+ it_behaves_like 'returning latest version'
+
+ it 'does not return job of the latest version' do
+ expect(execute.dig('data', 'project', 'terraformState', 'latestVersion', 'job')).not_to be_present
+ end
+ end
+ end
end
diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb
index 363ccdf88b7..0bad8c95ba2 100644
--- a/spec/graphql/types/user_type_spec.rb
+++ b/spec/graphql/types/user_type_spec.rb
@@ -38,6 +38,7 @@ RSpec.describe GitlabSchema.types['User'] do
callouts
namespace
timelogs
+ groups
]
expect(described_class).to have_graphql_fields(*expected_fields)