From e8d2c2579383897a1dd7f9debd359abe8ae8373d Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 20 Jul 2021 09:55:51 +0000 Subject: Add latest changes from gitlab-org/gitlab@14-1-stable-ee --- spec/graphql/features/authorization_spec.rb | 1 + spec/graphql/features/feature_flag_spec.rb | 21 ++++-- spec/graphql/gitlab_schema_spec.rb | 1 + .../prometheus_integration/create_spec.rb | 4 +- .../prometheus_integration/reset_token_spec.rb | 2 +- .../prometheus_integration/update_spec.rb | 2 +- .../ci/job_token_scope/add_project_spec.rb | 65 ++++++++++++++++++ .../ci/job_token_scope/remove_project_spec.rb | 68 ++++++++++++++++++ spec/graphql/mutations/custom_emoji/create_spec.rb | 1 + .../mutations/discussions/toggle_resolve_spec.rb | 2 + .../environments/canary_ingress/update_spec.rb | 1 + spec/graphql/mutations/issues/create_spec.rb | 1 + .../mutations/issues/set_confidential_spec.rb | 4 ++ spec/graphql/mutations/issues/set_severity_spec.rb | 3 +- spec/graphql/mutations/issues/update_spec.rb | 4 ++ spec/graphql/mutations/labels/create_spec.rb | 2 + .../notes/reposition_image_diff_note_spec.rb | 1 + .../mutations/release_asset_links/create_spec.rb | 18 +++++ .../mutations/release_asset_links/delete_spec.rb | 25 ++++++- .../mutations/release_asset_links/update_spec.rb | 20 ++++++ spec/graphql/mutations/releases/create_spec.rb | 22 ++++++ spec/graphql/mutations/releases/delete_spec.rb | 30 +++++++- spec/graphql/mutations/releases/update_spec.rb | 22 ++++++ .../base_security_analyzer_spec.rb | 14 ++++ .../http_integrations_resolver_spec.rb | 2 +- .../alert_management/integrations_resolver_spec.rb | 4 +- spec/graphql/resolvers/ci/config_resolver_spec.rb | 20 +++++- .../resolvers/ci/job_token_scope_resolver_spec.rb | 64 +++++++++++++++++ .../resolvers/group_milestones_resolver_spec.rb | 19 +++++ spec/graphql/resolvers/issues_resolver_spec.rb | 36 ++++++++++ .../resolvers/project_milestones_resolver_spec.rb | 18 +++++ .../projects/jira_projects_resolver_spec.rb | 6 +- .../resolvers/projects/services_resolver_spec.rb | 2 +- spec/graphql/resolvers/projects_resolver_spec.rb | 24 +++---- .../prometheus_integration_type_spec.rb | 4 +- spec/graphql/types/base_field_spec.rb | 67 ++++++++++++++---- spec/graphql/types/ci/detailed_status_type_spec.rb | 16 ++++- spec/graphql/types/ci/group_type_spec.rb | 1 + spec/graphql/types/ci/job_token_scope_type_spec.rb | 75 ++++++++++++++++++++ spec/graphql/types/ci/pipeline_type_spec.rb | 2 +- spec/graphql/types/ci/runner_type_spec.rb | 1 + spec/graphql/types/ci/stage_type_spec.rb | 2 + spec/graphql/types/ci/status_action_type_spec.rb | 20 ++++++ spec/graphql/types/deployment_tier_enum_spec.rb | 15 ++++ spec/graphql/types/global_id_type_spec.rb | 3 +- .../types/issuable_searchable_field_enum_spec.rb | 13 ++++ spec/graphql/types/issue_type_spec.rb | 4 +- spec/graphql/types/merge_request_type_spec.rb | 2 +- spec/graphql/types/milestone_type_spec.rb | 2 +- spec/graphql/types/notes/discussion_type_spec.rb | 1 + .../graphql/types/notes/noteable_interface_spec.rb | 23 +++++++ spec/graphql/types/notes/noteable_type_spec.rb | 23 ------- spec/graphql/types/project_type_spec.rb | 80 +++++++++++++++++++--- spec/graphql/types/projects/service_type_spec.rb | 2 +- spec/graphql/types/projects/services_enum_spec.rb | 2 +- spec/graphql/types/query_complexity_type_spec.rb | 35 ++++++++++ spec/graphql/types/release_asset_link_type_spec.rb | 2 +- spec/graphql/types/snippets/blob_type_spec.rb | 3 +- 58 files changed, 828 insertions(+), 99 deletions(-) create mode 100644 spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb create mode 100644 spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb create mode 100644 spec/graphql/mutations/security/ci_configuration/base_security_analyzer_spec.rb create mode 100644 spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb create mode 100644 spec/graphql/types/ci/job_token_scope_type_spec.rb create mode 100644 spec/graphql/types/deployment_tier_enum_spec.rb create mode 100644 spec/graphql/types/issuable_searchable_field_enum_spec.rb create mode 100644 spec/graphql/types/notes/noteable_interface_spec.rb delete mode 100644 spec/graphql/types/notes/noteable_type_spec.rb create mode 100644 spec/graphql/types/query_complexity_type_spec.rb (limited to 'spec/graphql') diff --git a/spec/graphql/features/authorization_spec.rb b/spec/graphql/features/authorization_spec.rb index 64e423e2bf8..0dc3a9c85e7 100644 --- a/spec/graphql/features/authorization_spec.rb +++ b/spec/graphql/features/authorization_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'DeclarativePolicy authorization in GraphQL ' do include Graphql::ResolverFactories let_it_be(:user) { create(:user) } + let(:permission_single) { :foo } let(:permission_collection) { [:foo, :bar] } let(:test_object) { double(name: 'My name') } diff --git a/spec/graphql/features/feature_flag_spec.rb b/spec/graphql/features/feature_flag_spec.rb index 30238cf9cb3..e5560fccf89 100644 --- a/spec/graphql/features/feature_flag_spec.rb +++ b/spec/graphql/features/feature_flag_spec.rb @@ -28,14 +28,25 @@ RSpec.describe 'Graphql Field feature flags' do end end - it 'returns the value when feature is enabled' do - expect(subject['item']).to eq('name' => test_object.name) + it 'checks YAML definition for default_enabled' do + # Exception is indicative of a check for YAML definition + expect { subject }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{feature_flag}' does not exist/) end - it 'returns nil when the feature is disabled' do - stub_feature_flags(feature_flag => false) + context 'skipping YAML check' do + before do + skip_default_enabled_yaml_check + end + + it 'returns the value when feature is enabled' do + expect(subject['item']).to eq('name' => test_object.name) + end - expect(subject).to be_nil + it 'returns nil when the feature is disabled' do + stub_feature_flags(feature_flag => false) + + expect(subject).to be_nil + end end end end diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index 1f2c518f83c..06505536b09 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe GitlabSchema do let_it_be(:connections) { GitlabSchema.connections.all_wrappers } + let(:user) { build :user } it 'uses batch loading' do diff --git a/spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb b/spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb index 7ab0f43d674..164bd9b1e39 100644 --- a/spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb +++ b/spec/graphql/mutations/alert_management/prometheus_integration/create_spec.rb @@ -19,7 +19,7 @@ RSpec.describe Mutations::AlertManagement::PrometheusIntegration::Create do end context 'when Prometheus Integration already exists' do - let_it_be(:existing_integration) { create(:prometheus_service, project: project) } + let_it_be(:existing_integration) { create(:prometheus_integration, project: project) } it 'returns errors' do expect(resolve).to eq( @@ -32,7 +32,7 @@ RSpec.describe Mutations::AlertManagement::PrometheusIntegration::Create do context 'when UpdateService responds with success' do it 'returns the integration with no errors' do expect(resolve).to eq( - integration: ::PrometheusService.last!, + integration: ::Integrations::Prometheus.last!, errors: [] ) end diff --git a/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb b/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb index c9e1bf4162c..be07c142f4e 100644 --- a/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb +++ b/spec/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Mutations::AlertManagement::PrometheusIntegration::ResetToken do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } - let_it_be(:integration) { create(:prometheus_service, project: project) } + let_it_be(:integration) { create(:prometheus_integration, project: project) } let(:args) { { id: GitlabSchema.id_from_object(integration) } } diff --git a/spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb b/spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb index 19e0d53b75f..81d057c6ae2 100644 --- a/spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb +++ b/spec/graphql/mutations/alert_management/prometheus_integration/update_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Mutations::AlertManagement::PrometheusIntegration::Update do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } - let_it_be(:integration) { create(:prometheus_service, project: project) } + let_it_be(:integration) { create(:prometheus_integration, project: project) } let(:args) { { id: GitlabSchema.id_from_object(integration), active: false, api_url: 'http://new-url.com' } } diff --git a/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb b/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb new file mode 100644 index 00000000000..412be5f16a4 --- /dev/null +++ b/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Mutations::Ci::JobTokenScope::AddProject do + let(:mutation) do + described_class.new(object: nil, context: { current_user: current_user }, field: nil) + end + + describe '#resolve' do + let_it_be(:project) do + create(:project, ci_job_token_scope_enabled: true).tap(&:save!) + end + + let_it_be(:target_project) { create(:project) } + + let(:target_project_path) { target_project.full_path } + + subject do + mutation.resolve(project_path: project.full_path, target_project_path: target_project_path) + end + + context 'when user is not logged in' do + let(:current_user) { nil } + + it 'raises error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + context 'when user is logged in' do + let(:current_user) { create(:user) } + + context 'when user does not have permissions to admin project' do + it 'raises error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + context 'when user has permissions to admin project and read target project' do + before do + project.add_maintainer(current_user) + target_project.add_guest(current_user) + end + + it 'adds target project to the job token scope' do + expect do + expect(subject).to include(ci_job_token_scope: be_present, errors: be_empty) + end.to change { Ci::JobToken::ProjectScopeLink.count }.by(1) + end + + context 'when the service returns an error' do + let(:service) { double(:service) } + + it 'returns an error response' do + expect(::Ci::JobTokenScope::AddProjectService).to receive(:new).with(project, current_user).and_return(service) + expect(service).to receive(:execute).with(target_project).and_return(ServiceResponse.error(message: 'The error message')) + + expect(subject.fetch(:ci_job_token_scope)).to be_nil + expect(subject.fetch(:errors)).to include("The error message") + end + end + end + end + end +end diff --git a/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb b/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb new file mode 100644 index 00000000000..0e706ea6e0c --- /dev/null +++ b/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Mutations::Ci::JobTokenScope::RemoveProject do + let(:mutation) do + described_class.new(object: nil, context: { current_user: current_user }, field: nil) + end + + describe '#resolve' do + let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:target_project) { create(:project) } + + let_it_be(:link) do + create(:ci_job_token_project_scope_link, + source_project: project, + target_project: target_project) + end + + let(:target_project_path) { target_project.full_path } + + subject do + mutation.resolve(project_path: project.full_path, target_project_path: target_project_path) + end + + context 'when user is not logged in' do + let(:current_user) { nil } + + it 'raises error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + context 'when user is logged in' do + let(:current_user) { create(:user) } + + context 'when user does not have permissions to admin project' do + it 'raises error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + context 'when user has permissions to admin project and read target project' do + before do + project.add_maintainer(current_user) + target_project.add_guest(current_user) + end + + it 'removes target project from the job token scope' do + expect do + expect(subject).to include(ci_job_token_scope: be_present, errors: be_empty) + end.to change { Ci::JobToken::ProjectScopeLink.count }.by(-1) + end + + context 'when the service returns an error' do + let(:service) { double(:service) } + + it 'returns an error response' do + expect(::Ci::JobTokenScope::RemoveProjectService).to receive(:new).with(project, current_user).and_return(service) + expect(service).to receive(:execute).with(target_project).and_return(ServiceResponse.error(message: 'The error message')) + + expect(subject.fetch(:ci_job_token_scope)).to be_nil + expect(subject.fetch(:errors)).to include("The error message") + end + end + end + end + end +end diff --git a/spec/graphql/mutations/custom_emoji/create_spec.rb b/spec/graphql/mutations/custom_emoji/create_spec.rb index 118c5d67188..7c98e53a72c 100644 --- a/spec/graphql/mutations/custom_emoji/create_spec.rb +++ b/spec/graphql/mutations/custom_emoji/create_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Mutations::CustomEmoji::Create do let_it_be(:group) { create(:group) } let_it_be(:user) { create(:user) } + let(:args) { { group_path: group.full_path, name: 'tanuki', url: 'https://about.gitlab.com/images/press/logo/png/gitlab-icon-rgb.png' } } before do diff --git a/spec/graphql/mutations/discussions/toggle_resolve_spec.rb b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb index 162b1249ab5..b03c6cb094f 100644 --- a/spec/graphql/mutations/discussions/toggle_resolve_spec.rb +++ b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb @@ -140,6 +140,7 @@ RSpec.describe Mutations::Discussions::ToggleResolve do context 'when discussion is on a merge request' do let_it_be(:noteable) { create(:merge_request, source_project: project) } + let(:discussion) { create(:diff_note_on_merge_request, noteable: noteable, project: project).to_discussion } it_behaves_like 'a working resolve method' @@ -147,6 +148,7 @@ RSpec.describe Mutations::Discussions::ToggleResolve do context 'when discussion is on a design' do let_it_be(:noteable) { create(:design, :with_file, issue: create(:issue, project: project)) } + let(:discussion) { create(:diff_note_on_design, noteable: noteable, project: project).to_discussion } it_behaves_like 'a working resolve method' diff --git a/spec/graphql/mutations/environments/canary_ingress/update_spec.rb b/spec/graphql/mutations/environments/canary_ingress/update_spec.rb index c022828cf09..2715a908f85 100644 --- a/spec/graphql/mutations/environments/canary_ingress/update_spec.rb +++ b/spec/graphql/mutations/environments/canary_ingress/update_spec.rb @@ -7,6 +7,7 @@ RSpec.describe Mutations::Environments::CanaryIngress::Update do let_it_be(:environment) { create(:environment, project: project) } let_it_be(:maintainer) { create(:user) } let_it_be(:reporter) { create(:user) } + let(:user) { maintainer } subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } diff --git a/spec/graphql/mutations/issues/create_spec.rb b/spec/graphql/mutations/issues/create_spec.rb index b32f0991959..0e7ef0e55b9 100644 --- a/spec/graphql/mutations/issues/create_spec.rb +++ b/spec/graphql/mutations/issues/create_spec.rb @@ -50,6 +50,7 @@ RSpec.describe Mutations::Issues::Create do stub_licensed_features(multiple_issue_assignees: false, issue_weights: false) project.add_guest(assignee1) project.add_guest(assignee2) + stub_spam_services end subject { mutation.resolve(**mutation_params) } diff --git a/spec/graphql/mutations/issues/set_confidential_spec.rb b/spec/graphql/mutations/issues/set_confidential_spec.rb index c3269e5c0c0..495b8442d95 100644 --- a/spec/graphql/mutations/issues/set_confidential_spec.rb +++ b/spec/graphql/mutations/issues/set_confidential_spec.rb @@ -17,6 +17,10 @@ RSpec.describe Mutations::Issues::SetConfidential do subject { mutation.resolve(project_path: project.full_path, iid: issue.iid, confidential: confidential) } + before do + stub_spam_services + end + it_behaves_like 'permission level for issue mutation is correctly verified' context 'when the user can update the issue' do diff --git a/spec/graphql/mutations/issues/set_severity_spec.rb b/spec/graphql/mutations/issues/set_severity_spec.rb index 7698118ae3e..7ce9c7f6621 100644 --- a/spec/graphql/mutations/issues/set_severity_spec.rb +++ b/spec/graphql/mutations/issues/set_severity_spec.rb @@ -5,12 +5,13 @@ 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(:severity) { 'critical' } let(:mutated_incident) { subject[:issue] } subject(:resolve) { mutation.resolve(project_path: issue.project.full_path, iid: issue.iid, severity: severity) } diff --git a/spec/graphql/mutations/issues/update_spec.rb b/spec/graphql/mutations/issues/update_spec.rb index bd780477658..80f43338bb5 100644 --- a/spec/graphql/mutations/issues/update_spec.rb +++ b/spec/graphql/mutations/issues/update_spec.rb @@ -35,6 +35,10 @@ RSpec.describe Mutations::Issues::Update do subject { mutation.resolve(**mutation_params) } + before do + stub_spam_services + end + it_behaves_like 'permission level for issue mutation is correctly verified' context 'when the user can update the issue' do diff --git a/spec/graphql/mutations/labels/create_spec.rb b/spec/graphql/mutations/labels/create_spec.rb index b2dd94f31bb..53a17041125 100644 --- a/spec/graphql/mutations/labels/create_spec.rb +++ b/spec/graphql/mutations/labels/create_spec.rb @@ -45,6 +45,7 @@ RSpec.describe Mutations::Labels::Create do context 'when creating a project label' do let_it_be(:parent) { create(:project) } + let(:extra_params) { { project_path: parent.full_path } } it_behaves_like 'create labels mutation' @@ -52,6 +53,7 @@ RSpec.describe Mutations::Labels::Create do context 'when creating a group label' do let_it_be(:parent) { create(:group) } + let(:extra_params) { { group_path: parent.full_path } } it_behaves_like 'create labels mutation' diff --git a/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb b/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb index d88b196cbff..e78f755d5c7 100644 --- a/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb +++ b/spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb @@ -12,6 +12,7 @@ RSpec.describe Mutations::Notes::RepositionImageDiffNote do let_it_be(:noteable) { create(:merge_request) } let_it_be(:project) { noteable.project } + let(:note) { create(:image_diff_note_on_merge_request, noteable: noteable, project: project) } let(:mutation) do diff --git a/spec/graphql/mutations/release_asset_links/create_spec.rb b/spec/graphql/mutations/release_asset_links/create_spec.rb index 089bc3d3276..eb7cbb4b789 100644 --- a/spec/graphql/mutations/release_asset_links/create_spec.rb +++ b/spec/graphql/mutations/release_asset_links/create_spec.rb @@ -50,6 +50,24 @@ RSpec.describe Mutations::ReleaseAssetLinks::Create do end end + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'does not have errors' do + expect(subject).to include(errors: []) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'has an access error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + end + context "when the user doesn't have access to the project" do let(:current_user) { reporter } diff --git a/spec/graphql/mutations/release_asset_links/delete_spec.rb b/spec/graphql/mutations/release_asset_links/delete_spec.rb index 15d320b58ee..cda292f2ffa 100644 --- a/spec/graphql/mutations/release_asset_links/delete_spec.rb +++ b/spec/graphql/mutations/release_asset_links/delete_spec.rb @@ -7,6 +7,7 @@ RSpec.describe Mutations::ReleaseAssetLinks::Delete do let_it_be(:project) { create(:project, :private, :repository) } let_it_be_with_reload(:release) { create(:release, project: project) } + let_it_be(:reporter) { create(:user).tap { |u| project.add_reporter(u) } } let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } } let_it_be(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } } let_it_be_with_reload(:release_link) { create(:release_link, release: release) } @@ -22,7 +23,7 @@ RSpec.describe Mutations::ReleaseAssetLinks::Delete do let(:deleted_link) { subject[:link] } context 'when the current user has access to delete the link' do - let(:current_user) { maintainer } + let(:current_user) { developer } it 'deletes the link and returns it', :aggregate_failures do expect(deleted_link).to eq(release_link) @@ -30,6 +31,26 @@ RSpec.describe Mutations::ReleaseAssetLinks::Delete do expect(release.links).to be_empty end + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'does not have errors' do + subject + + expect(resolve).to include(errors: []) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'raises a resource access error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + end + context "when the link doesn't exist" do let(:mutation_arguments) { super().merge(id: "gid://gitlab/Releases::Link/#{non_existing_record_id}") } @@ -48,7 +69,7 @@ RSpec.describe Mutations::ReleaseAssetLinks::Delete do end context 'when the current user does not have access to delete the link' do - let(:current_user) { developer } + let(:current_user) { reporter } it 'raises an error' do expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) diff --git a/spec/graphql/mutations/release_asset_links/update_spec.rb b/spec/graphql/mutations/release_asset_links/update_spec.rb index 20c1c8b581c..64648687336 100644 --- a/spec/graphql/mutations/release_asset_links/update_spec.rb +++ b/spec/graphql/mutations/release_asset_links/update_spec.rb @@ -87,6 +87,26 @@ RSpec.describe Mutations::ReleaseAssetLinks::Update do end it_behaves_like 'no changes to the link except for the', :name + + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'does not have errors' do + subject + + expect(resolve).to include(errors: []) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'raises a resource access error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + end end context 'when nil is provided' do diff --git a/spec/graphql/mutations/releases/create_spec.rb b/spec/graphql/mutations/releases/create_spec.rb index 7776f968346..1f2c3ed537f 100644 --- a/spec/graphql/mutations/releases/create_spec.rb +++ b/spec/graphql/mutations/releases/create_spec.rb @@ -117,6 +117,28 @@ RSpec.describe Mutations::Releases::Create do expect(new_link.filepath).to eq(expected_link[:filepath]) end end + + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'does not have errors' do + subject + + expect(resolve).to include(errors: []) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'has an access error' do + subject + + expect(resolve).to include(errors: ['Access Denied']) + end + end + end end context "when the current user doesn't have access to create releases" do diff --git a/spec/graphql/mutations/releases/delete_spec.rb b/spec/graphql/mutations/releases/delete_spec.rb index bedb72b002c..d97f839ce87 100644 --- a/spec/graphql/mutations/releases/delete_spec.rb +++ b/spec/graphql/mutations/releases/delete_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Mutations::Releases::Delete do let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:non_project_member) { create(:user) } + let_it_be(:reporter) { create(:user) } let_it_be(:developer) { create(:user) } let_it_be(:maintainer) { create(:user) } let_it_be(:tag) { 'v1.1.0'} @@ -20,6 +21,7 @@ RSpec.describe Mutations::Releases::Delete do end before do + project.add_reporter(reporter) project.add_developer(developer) project.add_maintainer(maintainer) end @@ -36,7 +38,7 @@ RSpec.describe Mutations::Releases::Delete do end context 'when the current user has access to create releases' do - let(:current_user) { maintainer } + let(:current_user) { developer } it 'deletes the release' do expect { subject }.to change { Release.count }.by(-1) @@ -54,6 +56,28 @@ RSpec.describe Mutations::Releases::Delete do expect(subject[:errors]).to eq([]) end + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'does not have errors' do + subject + + expect(resolve).to include(errors: []) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'has an access error' do + subject + + expect(resolve).to include(errors: ['Access Denied']) + end + end + end + context 'validation' do context 'when the release does not exist' do let(:mutation_arguments) { super().merge(tag: 'not-a-real-release') } @@ -76,8 +100,8 @@ RSpec.describe Mutations::Releases::Delete do end context "when the current user doesn't have access to update releases" do - context 'when the user is a developer' do - let(:current_user) { developer } + context 'when the user is a reporter' do + let(:current_user) { reporter } it_behaves_like 'unauthorized or not found error' end diff --git a/spec/graphql/mutations/releases/update_spec.rb b/spec/graphql/mutations/releases/update_spec.rb index c541afd53a1..5ee63ac4dc2 100644 --- a/spec/graphql/mutations/releases/update_spec.rb +++ b/spec/graphql/mutations/releases/update_spec.rb @@ -107,6 +107,28 @@ RSpec.describe Mutations::Releases::Update do end it_behaves_like 'no changes to the release except for the', :name + + context 'with protected tag' do + context 'when user has access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) } + + it 'does not have errors' do + subject + + expect(resolve).to include(errors: []) + end + end + + context 'when user does not have access to the protected tag' do + let!(:protected_tag) { create(:protected_tag, :maintainers_can_create, name: '*', project: project) } + + it 'has an access error' do + subject + + expect(resolve).to include(errors: ['Access Denied']) + end + end + end end context 'when nil is provided' do diff --git a/spec/graphql/mutations/security/ci_configuration/base_security_analyzer_spec.rb b/spec/graphql/mutations/security/ci_configuration/base_security_analyzer_spec.rb new file mode 100644 index 00000000000..818a7d303bd --- /dev/null +++ b/spec/graphql/mutations/security/ci_configuration/base_security_analyzer_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::Security::CiConfiguration::BaseSecurityAnalyzer do + include GraphqlHelpers + + it 'raises a NotImplementedError error if the resolve method is called on the base class' do + user = create(:user) + project = create(:project, :public, :repository) + project.add_developer(user) + expect { resolve(described_class, args: { project_path: project.full_path }, ctx: { current_user: user }) }.to raise_error(NotImplementedError) + end +end diff --git a/spec/graphql/resolvers/alert_management/http_integrations_resolver_spec.rb b/spec/graphql/resolvers/alert_management/http_integrations_resolver_spec.rb index a4d1101bc4f..0f40565c5d3 100644 --- a/spec/graphql/resolvers/alert_management/http_integrations_resolver_spec.rb +++ b/spec/graphql/resolvers/alert_management/http_integrations_resolver_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Resolvers::AlertManagement::HttpIntegrationsResolver do let_it_be(:developer) { create(:user) } let_it_be(:maintainer) { create(:user) } let_it_be(:project) { create(:project) } - let_it_be(:prometheus_integration) { create(:prometheus_service, project: project) } + let_it_be(:prometheus_integration) { create(:prometheus_integration, project: project) } let_it_be(:active_http_integration) { create(:alert_management_http_integration, project: project) } let_it_be(:inactive_http_integration) { create(:alert_management_http_integration, :inactive, project: project) } let_it_be(:other_proj_integration) { create(:alert_management_http_integration) } diff --git a/spec/graphql/resolvers/alert_management/integrations_resolver_spec.rb b/spec/graphql/resolvers/alert_management/integrations_resolver_spec.rb index fb0fb6729d4..11114d41522 100644 --- a/spec/graphql/resolvers/alert_management/integrations_resolver_spec.rb +++ b/spec/graphql/resolvers/alert_management/integrations_resolver_spec.rb @@ -8,11 +8,11 @@ RSpec.describe Resolvers::AlertManagement::IntegrationsResolver do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:project2) { create(:project) } - let_it_be(:prometheus_integration) { create(:prometheus_service, project: project) } + let_it_be(:prometheus_integration) { create(:prometheus_integration, project: project) } let_it_be(:active_http_integration) { create(:alert_management_http_integration, project: project) } let_it_be(:inactive_http_integration) { create(:alert_management_http_integration, :inactive, project: project) } let_it_be(:other_proj_integration) { create(:alert_management_http_integration, project: project2) } - let_it_be(:other_proj_prometheus_integration) { create(:prometheus_service, project: project2) } + let_it_be(:other_proj_prometheus_integration) { create(:prometheus_integration, project: project2) } let(:params) { {} } diff --git a/spec/graphql/resolvers/ci/config_resolver_spec.rb b/spec/graphql/resolvers/ci/config_resolver_spec.rb index 73e9fab9f99..97eee749290 100644 --- a/spec/graphql/resolvers/ci/config_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/config_resolver_spec.rb @@ -15,14 +15,15 @@ RSpec.describe Resolvers::Ci::ConfigResolver do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :repository, creator: user, namespace: user.namespace) } + let_it_be(:sha) { nil } subject(:response) do resolve(described_class, - args: { project_path: project.full_path, content: content }, + args: { project_path: project.full_path, content: content, sha: sha }, ctx: { current_user: user }) end - context 'with a valid .gitlab-ci.yml' do + shared_examples 'a valid config file' do let(:fake_result) do ::Gitlab::Ci::Lint::Result.new( merged_yaml: content, @@ -37,9 +38,22 @@ RSpec.describe Resolvers::Ci::ConfigResolver do end it 'lints the ci config file and returns the merged yaml file' do - expect(response[:merged_yaml]).to eq(content) expect(response[:status]).to eq(:valid) + expect(response[:merged_yaml]).to eq(content) expect(response[:errors]).to be_empty + expect(::Gitlab::Ci::Lint).to have_received(:new).with(current_user: user, project: project, sha: sha) + end + end + + context 'with a valid .gitlab-ci.yml' do + context 'with a sha' do + let(:sha) { '1231231' } + + it_behaves_like 'a valid config file' + end + + context 'without a sha' do + it_behaves_like 'a valid config file' end end diff --git a/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb b/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb new file mode 100644 index 00000000000..8522542498d --- /dev/null +++ b/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Ci::JobTokenScopeResolver do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + + specify do + expect(described_class).to have_nullable_graphql_type(::Types::Ci::JobTokenScopeType) + end + + subject(:resolve_scope) { resolve(described_class, ctx: { current_user: current_user }, obj: project) } + + describe '#resolve' do + context 'with access to scope' do + before do + project.add_user(current_user, :maintainer) + end + + it 'returns nil when scope is not enabled' do + allow(project).to receive(:ci_job_token_scope_enabled?).and_return(false) + + expect(resolve_scope).to eq(nil) + end + + it 'returns the same project in the allow list of projects for the Ci Job Token' do + expect(resolve_scope.all_projects).to contain_exactly(project) + end + + context 'when another projects gets added to the allow list' do + let!(:link) { create(:ci_job_token_project_scope_link, source_project: project) } + + it 'returns both projects' do + expect(resolve_scope.all_projects).to contain_exactly(project, link.target_project) + end + end + + context 'when job token scope is disabled' do + before do + project.update!(ci_job_token_scope_enabled: false) + end + + it 'returns nil' do + expect(resolve_scope).to be_nil + end + end + end + + context 'without access to scope' do + before do + project.add_user(current_user, :developer) + end + + it 'raises error' do + expect do + resolve_scope + end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + end +end diff --git a/spec/graphql/resolvers/group_milestones_resolver_spec.rb b/spec/graphql/resolvers/group_milestones_resolver_spec.rb index 78d89054efd..acfc8313407 100644 --- a/spec/graphql/resolvers/group_milestones_resolver_spec.rb +++ b/spec/graphql/resolvers/group_milestones_resolver_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe Resolvers::GroupMilestonesResolver do + using RSpec::Parameterized::TableSyntax include GraphqlHelpers describe '#resolve' do @@ -79,6 +80,24 @@ RSpec.describe Resolvers::GroupMilestonesResolver do end end + context 'by sort' do + it 'calls MilestonesFinder with correct parameters' do + expect(MilestonesFinder).to receive(:new) + .with(args(group_ids: group.id, state: 'all', sort: :due_date_desc)) + .and_call_original + + resolve_group_milestones(sort: :due_date_desc) + end + + %i[expired_last_due_date_asc expired_last_due_date_desc].each do |sort_by| + it "uses offset-pagination when sorting by #{sort_by}" do + resolved = resolve_group_milestones(sort: sort_by) + + expect(resolved).to be_a(::Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection) + end + end + end + context 'by timeframe' do context 'when start_date and end_date are present' do context 'when start date is after end_date' do diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb index 7c2ceb50066..9b329e961cc 100644 --- a/spec/graphql/resolvers/issues_resolver_spec.rb +++ b/spec/graphql/resolvers/issues_resolver_spec.rb @@ -290,6 +290,42 @@ RSpec.describe Resolvers::IssuesResolver do expect(resolve_issues(sort: :severity_desc).to_a).to eq([issue_high_severity, issue_low_severity, issue_no_severity]) end end + + context 'when sorting by popularity' do + let_it_be(:project) { create(:project, :public) } + let_it_be(:issue1) { create(:issue, project: project) } # has one upvote + let_it_be(:issue2) { create(:issue, project: project) } # has two upvote + let_it_be(:issue3) { create(:issue, project: project) } + let_it_be(:issue4) { create(:issue, project: project) } # has one upvote + + before do + create(:award_emoji, :upvote, awardable: issue1) + create(:award_emoji, :upvote, awardable: issue2) + create(:award_emoji, :upvote, awardable: issue2) + create(:award_emoji, :upvote, awardable: issue4) + end + + it 'sorts issues ascending (ties broken by id in desc order)' do + expect(resolve_issues(sort: :popularity_asc).to_a).to eq([issue3, issue4, issue1, issue2]) + end + + it 'sorts issues descending (ties broken by id in desc order)' do + expect(resolve_issues(sort: :popularity_desc).to_a).to eq([issue2, issue4, issue1, issue3]) + end + end + + context 'when sorting with non-stable cursors' do + %i[priority_asc priority_desc + popularity_asc popularity_desc + label_priority_asc label_priority_desc + milestone_due_asc milestone_due_desc].each do |sort_by| + it "uses offset-pagination when sorting by #{sort_by}" do + resolved = resolve_issues(sort: sort_by) + + expect(resolved).to be_a(::Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection) + end + end + end end it 'returns issues user can see' do diff --git a/spec/graphql/resolvers/project_milestones_resolver_spec.rb b/spec/graphql/resolvers/project_milestones_resolver_spec.rb index b641a54393e..e168291c804 100644 --- a/spec/graphql/resolvers/project_milestones_resolver_spec.rb +++ b/spec/graphql/resolvers/project_milestones_resolver_spec.rb @@ -71,6 +71,24 @@ RSpec.describe Resolvers::ProjectMilestonesResolver do end end + context 'by sort' do + it 'calls MilestonesFinder with correct parameters' do + expect(MilestonesFinder).to receive(:new) + .with(args(project_ids: project.id, state: 'all', sort: :due_date_desc)) + .and_call_original + + resolve_project_milestones(sort: :due_date_desc) + end + + %i[expired_last_due_date_asc expired_last_due_date_desc].each do |sort_by| + it "uses offset-pagination when sorting by #{sort_by}" do + resolved = resolve_project_milestones(sort: sort_by) + + expect(resolved).to be_a(::Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection) + end + end + end + context 'by timeframe' do context 'when start_date and end_date are present' do it 'calls MilestonesFinder with correct parameters' do diff --git a/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb b/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb index c375345250d..8c36153d485 100644 --- a/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb +++ b/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb @@ -22,7 +22,7 @@ RSpec.describe Resolvers::Projects::JiraProjectsResolver do end context 'when project has no Jira service' do - let_it_be(:jira_service) { nil } + let_it_be(:jira_integration) { nil } context 'when user is a maintainer' do before do @@ -34,7 +34,7 @@ RSpec.describe Resolvers::Projects::JiraProjectsResolver do end context 'when project has Jira service' do - let(:jira_service) { create(:jira_service, project: project) } + let(:jira_integration) { create(:jira_integration, project: project) } context 'when user is a developer' do before do @@ -98,6 +98,6 @@ RSpec.describe Resolvers::Projects::JiraProjectsResolver do end def resolve_jira_projects(args = {}, context = { current_user: user }) - resolve(described_class, obj: jira_service, args: args, ctx: context) + resolve(described_class, obj: jira_integration, args: args, ctx: context) end end diff --git a/spec/graphql/resolvers/projects/services_resolver_spec.rb b/spec/graphql/resolvers/projects/services_resolver_spec.rb index a1b631113b2..6da99c8448e 100644 --- a/spec/graphql/resolvers/projects/services_resolver_spec.rb +++ b/spec/graphql/resolvers/projects/services_resolver_spec.rb @@ -40,7 +40,7 @@ RSpec.describe Resolvers::Projects::ServicesResolver do context 'when project has services' do let_it_be(:project) { create(:project, :private) } - let_it_be(:jira_service) { create(:jira_service, project: project) } + let_it_be(:jira_integration) { create(:jira_integration, project: project) } context 'when user cannot access services' do context 'when anonymous user' do diff --git a/spec/graphql/resolvers/projects_resolver_spec.rb b/spec/graphql/resolvers/projects_resolver_spec.rb index 2f2aacb9ad5..2685115d1a2 100644 --- a/spec/graphql/resolvers/projects_resolver_spec.rb +++ b/spec/graphql/resolvers/projects_resolver_spec.rb @@ -27,10 +27,6 @@ RSpec.describe Resolvers::ProjectsResolver do private_group.add_developer(user) end - before do - stub_feature_flags(project_finder_similarity_sort: false) - end - context 'when user is not logged in' do let(:current_user) { nil } @@ -83,6 +79,7 @@ RSpec.describe Resolvers::ProjectsResolver do context 'when user is logged in' do let(:current_user) { user } + let(:visible_projecs) { [project, other_project, group_project, private_project, private_group_project] } context 'when no filters are applied' do it 'returns all visible projects for the user' do @@ -129,21 +126,24 @@ RSpec.describe Resolvers::ProjectsResolver do end end - context 'when sort is similarity' do + context 'when sorting' do let_it_be(:named_project1) { create(:project, :public, name: 'projAB', path: 'projAB') } let_it_be(:named_project2) { create(:project, :public, name: 'projABC', path: 'projABC') } let_it_be(:named_project3) { create(:project, :public, name: 'projA', path: 'projA') } + let_it_be(:named_projects) { [named_project1, named_project2, named_project3] } - let(:filters) { { search: 'projA', sort: 'similarity' } } - - it 'returns projects in order of similarity to search' do - stub_feature_flags(project_finder_similarity_sort: current_user) + context 'when sorting by similarity' do + let(:filters) { { search: 'projA', sort: 'similarity' } } - is_expected.to eq([named_project3, named_project1, named_project2]) + it 'returns projects in order of similarity to search' do + is_expected.to eq([named_project3, named_project1, named_project2]) + end end - it 'returns projects in any order if flag is off' do - is_expected.to match_array([named_project3, named_project1, named_project2]) + context 'when no sort is provided' do + it 'returns projects in descending order by id' do + is_expected.to match_array((visible_projecs + named_projects).sort_by { |p| p[:id]}.reverse ) + end end end diff --git a/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb b/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb index d057afb331c..31cf94aef44 100644 --- a/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb +++ b/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb @@ -17,7 +17,7 @@ RSpec.describe GitlabSchema.types['AlertManagementPrometheusIntegration'] do end end - let_it_be_with_reload(:integration) { create(:prometheus_service) } + let_it_be_with_reload(:integration) { create(:prometheus_integration) } let_it_be(:user) { create(:user, maintainer_projects: [integration.project]) } it_behaves_like 'has field with value', 'name' do @@ -50,7 +50,7 @@ RSpec.describe GitlabSchema.types['AlertManagementPrometheusIntegration'] do describe 'a group integration' do let_it_be(:group) { create(:group) } - let_it_be(:integration) { create(:prometheus_service, project: nil, group: group) } + let_it_be(:integration) { create(:prometheus_integration, project: nil, group: group) } # Since it is impossible to authorize the parent here, given that the # project is nil, all fields should be redacted: diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb index 54b59317b55..c34fbf42dd8 100644 --- a/spec/graphql/types/base_field_spec.rb +++ b/spec/graphql/types/base_field_spec.rb @@ -130,14 +130,25 @@ RSpec.describe Types::BaseField do skip_feature_flags_yaml_validation end - it 'returns false if the feature is not enabled' do - stub_feature_flags(flag => false) - - expect(field.visible?(context)).to eq(false) + it 'checks YAML definition for default_enabled' do + # Exception is indicative of a check for YAML definition + expect { field.visible?(context) }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{flag}' does not exist/) end - it 'returns true if the feature is enabled' do - expect(field.visible?(context)).to eq(true) + context 'skipping YAML check' do + before do + skip_default_enabled_yaml_check + end + + it 'returns false if the feature is not enabled' do + stub_feature_flags(flag => false) + + expect(field.visible?(context)).to eq(false) + end + + it 'returns true if the feature is enabled' do + expect(field.visible?(context)).to eq(true) + end end end end @@ -149,17 +160,17 @@ RSpec.describe Types::BaseField do let(:flag) { :test_flag } it 'prepends the description' do - expect(field.description). to eq 'Test description. Available only when feature flag `test_flag` is enabled.' + expect(field.description).to start_with 'Test description. Available only when feature flag `test_flag` is enabled.' end context 'falsey feature_flag values' do using RSpec::Parameterized::TableSyntax - where(:flag, :feature_value) do - '' | false - '' | true - nil | false - nil | true + where(:flag, :feature_value, :default_enabled) do + '' | false | false + '' | true | false + nil | false | true + nil | true | false end with_them do @@ -168,6 +179,33 @@ RSpec.describe Types::BaseField do end end end + + context 'with different default_enabled values' do + using RSpec::Parameterized::TableSyntax + + where(:feature_value, :default_enabled, :expected_description) do + disabled_ff_description = "Test description. Available only when feature flag `test_flag` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice." + enabled_ff_description = "Test description. Available only when feature flag `test_flag` is enabled. This flag is enabled by default." + + false | false | disabled_ff_description + true | false | disabled_ff_description + false | true | enabled_ff_description + true | true | enabled_ff_description + end + + with_them do + before do + stub_feature_flags("#{flag}": feature_value) + + allow(Feature::Definition).to receive(:has_definition?).with(flag).and_return(true) + allow(Feature::Definition).to receive(:default_enabled?).and_return(default_enabled) + end + + it 'returns the correct availability in the description' do + expect(field.description). to eq expected_description + end + end + end end end @@ -185,9 +223,8 @@ RSpec.describe Types::BaseField do feature_flag: 'foo_flag' ) - expectation = 'Field description. Available only when feature flag `foo_flag` is enabled. Deprecated in 1.10: Deprecation reason.' - - expect(field.description).to eq(expectation) + expect(field.description).to start_with('Field description. Available only when feature flag `foo_flag` is enabled.') + expect(field.description).to end_with('Deprecated in 1.10: Deprecation reason.') end end end diff --git a/spec/graphql/types/ci/detailed_status_type_spec.rb b/spec/graphql/types/ci/detailed_status_type_spec.rb index 9fa3280657a..5ed79b73a47 100644 --- a/spec/graphql/types/ci/detailed_status_type_spec.rb +++ b/spec/graphql/types/ci/detailed_status_type_spec.rb @@ -8,14 +8,26 @@ RSpec.describe Types::Ci::DetailedStatusType do specify { expect(described_class.graphql_name).to eq('DetailedStatus') } it 'has all fields' do - expect(described_class).to have_graphql_fields(:group, :icon, :favicon, + expect(described_class).to have_graphql_fields(:id, :group, :icon, :favicon, :details_path, :has_details, :label, :text, :tooltip, :action) end + let_it_be(:stage) { create(:ci_stage_entity, status: :skipped) } + + describe 'id field' do + it 'correctly renders the field' do + parent_object = double(:parent_object, object: stage) + parent = double(:parent, object: parent_object) + status = stage.detailed_status(stage.pipeline.user) + expected_id = "#{status.id}-#{stage.id}" + + expect(resolve_field('id', status, extras: { parent: parent })).to eq(expected_id) + end + end + describe 'action field' do it 'correctly renders the field' do - stage = create(:ci_stage_entity, status: :skipped) status = stage.detailed_status(stage.pipeline.user) expected_status = { diff --git a/spec/graphql/types/ci/group_type_spec.rb b/spec/graphql/types/ci/group_type_spec.rb index d7ce5602612..f563b31342f 100644 --- a/spec/graphql/types/ci/group_type_spec.rb +++ b/spec/graphql/types/ci/group_type_spec.rb @@ -7,6 +7,7 @@ RSpec.describe Types::Ci::GroupType do it 'exposes the expected fields' do expected_fields = %i[ + id name size jobs diff --git a/spec/graphql/types/ci/job_token_scope_type_spec.rb b/spec/graphql/types/ci/job_token_scope_type_spec.rb new file mode 100644 index 00000000000..19a8cc324f9 --- /dev/null +++ b/spec/graphql/types/ci/job_token_scope_type_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['CiJobTokenScopeType'] do + specify { expect(described_class.graphql_name).to eq('CiJobTokenScopeType') } + + it 'has the correct fields' do + expected_fields = [:projects] + + expect(described_class).to have_graphql_fields(*expected_fields) + end + + describe 'query' do + let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:current_user) { create(:user) } + + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + ciJobTokenScope { + projects { + nodes { + path + } + } + } + } + } + ) + end + + subject { GitlabSchema.execute(query, context: { current_user: current_user }).as_json } + + let(:projects_field) { subject.dig('data', 'project', 'ciJobTokenScope', 'projects', 'nodes') } + let(:returned_project_paths) { projects_field.map { |project| project['path']} } + + context 'with access to scope' do + before do + project.add_user(current_user, :maintainer) + end + + context 'when multiple projects in the allow list' do + let!(:link) { create(:ci_job_token_project_scope_link, source_project: project) } + + context 'when linked projects are readable' do + before do + link.target_project.add_user(current_user, :developer) + end + + it 'returns readable projects in scope' do + expect(returned_project_paths).to contain_exactly(project.path, link.target_project.path) + end + end + + context 'when linked project is not readable' do + it 'returns readable projects in scope' do + expect(returned_project_paths).to contain_exactly(project.path) + end + end + + context 'when job token scope is disabled' do + before do + project.ci_cd_settings.update!(job_token_scope_enabled: false) + end + + it 'returns nil' do + expect(subject.dig('data', 'project', 'ciJobTokenScope')).to be_nil + end + end + end + end + end +end diff --git a/spec/graphql/types/ci/pipeline_type_spec.rb b/spec/graphql/types/ci/pipeline_type_spec.rb index 35d48229fa4..9ba4252bcd5 100644 --- a/spec/graphql/types/ci/pipeline_type_spec.rb +++ b/spec/graphql/types/ci/pipeline_type_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Types::Ci::PipelineType do coverage created_at updated_at started_at finished_at committed_at stages user retryable cancelable jobs source_job job downstream upstream path project active user_permissions warnings commit_path uses_needs - test_report_summary test_suite + test_report_summary test_suite ref ] if Gitlab.ee? diff --git a/spec/graphql/types/ci/runner_type_spec.rb b/spec/graphql/types/ci/runner_type_spec.rb index f27216f4d39..cff4c459d79 100644 --- a/spec/graphql/types/ci/runner_type_spec.rb +++ b/spec/graphql/types/ci/runner_type_spec.rb @@ -11,6 +11,7 @@ RSpec.describe GitlabSchema.types['CiRunner'] do expected_fields = %w[ id description contacted_at maximum_timeout access_level active status version short_sha revision locked run_untagged ip_address runner_type tag_list + project_count job_count ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/ci/stage_type_spec.rb b/spec/graphql/types/ci/stage_type_spec.rb index cb8c1cb02cd..48c569eca16 100644 --- a/spec/graphql/types/ci/stage_type_spec.rb +++ b/spec/graphql/types/ci/stage_type_spec.rb @@ -7,9 +7,11 @@ RSpec.describe Types::Ci::StageType do it 'exposes the expected fields' do expected_fields = %i[ + id name groups detailedStatus + status jobs ] diff --git a/spec/graphql/types/ci/status_action_type_spec.rb b/spec/graphql/types/ci/status_action_type_spec.rb index 8a99068e44f..ab7dee3dd11 100644 --- a/spec/graphql/types/ci/status_action_type_spec.rb +++ b/spec/graphql/types/ci/status_action_type_spec.rb @@ -3,10 +3,13 @@ require 'spec_helper' RSpec.describe Types::Ci::StatusActionType do + include GraphqlHelpers + specify { expect(described_class.graphql_name).to eq('StatusAction') } it 'exposes the expected fields' do expected_fields = %i[ + id buttonTitle icon path @@ -16,4 +19,21 @@ RSpec.describe Types::Ci::StatusActionType do expect(described_class).to have_graphql_fields(*expected_fields) end + + describe 'id field' do + it 'correctly renders the field' do + stage = build(:ci_stage_entity, status: :skipped) + status = stage.detailed_status(stage.pipeline.user) + + grandparent_object = double(:grandparent_object, object: stage) + parent_object = double(:parent_object, object: status) + + grandparent = double(:parent, object: grandparent_object) + parent = double(:parent, object: parent_object, parent: grandparent) + + expected_id = "#{stage.class.name}-#{status.id}" + + expect(resolve_field('id', status, extras: { parent: parent })).to eq(expected_id) + end + end end diff --git a/spec/graphql/types/deployment_tier_enum_spec.rb b/spec/graphql/types/deployment_tier_enum_spec.rb new file mode 100644 index 00000000000..752bf895d74 --- /dev/null +++ b/spec/graphql/types/deployment_tier_enum_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::DeploymentTierEnum do + it 'includes a value for each supported environment tier' do + expect(described_class.values).to match( + 'PRODUCTION' => have_attributes(value: :production), + 'STAGING' => have_attributes(value: :staging), + 'TESTING' => have_attributes(value: :testing), + 'DEVELOPMENT' => have_attributes(value: :development), + 'OTHER' => have_attributes(value: :other) + ) + end +end diff --git a/spec/graphql/types/global_id_type_spec.rb b/spec/graphql/types/global_id_type_spec.rb index 37f59770817..cdf09dd9cc9 100644 --- a/spec/graphql/types/global_id_type_spec.rb +++ b/spec/graphql/types/global_id_type_spec.rb @@ -3,7 +3,6 @@ require 'spec_helper' RSpec.describe Types::GlobalIDType do - include ::Gitlab::Graphql::Laziness include GraphqlHelpers include GlobalIDDeprecationHelpers @@ -103,7 +102,7 @@ RSpec.describe Types::GlobalIDType do end context 'with a deprecation' do - around(:all) do |example| + around do |example| # Unset all previously memoized GlobalIDTypes to allow us to define one # that will use the constants stubbed in the `before` block. previous_id_types = Types::GlobalIDType.instance_variable_get(:@id_types) diff --git a/spec/graphql/types/issuable_searchable_field_enum_spec.rb b/spec/graphql/types/issuable_searchable_field_enum_spec.rb new file mode 100644 index 00000000000..13e1b55ac7b --- /dev/null +++ b/spec/graphql/types/issuable_searchable_field_enum_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::IssuableSearchableFieldEnum do + specify { expect(described_class.graphql_name).to eq('IssuableSearchableField') } + + it 'exposes all the issuable searchable fields' do + expect(described_class.values.keys).to contain_exactly( + *Issuable::SEARCHABLE_FIELDS.map(&:upcase) + ) + end +end diff --git a/spec/graphql/types/issue_type_spec.rb b/spec/graphql/types/issue_type_spec.rb index 6908a610aae..a117741b3a2 100644 --- a/spec/graphql/types/issue_type_spec.rb +++ b/spec/graphql/types/issue_type_spec.rb @@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['Issue'] do specify { expect(described_class).to require_graphql_authorizations(:read_issue) } - specify { expect(described_class.interfaces).to include(Types::Notes::NoteableType) } + specify { expect(described_class.interfaces).to include(Types::Notes::NoteableInterface) } specify { expect(described_class.interfaces).to include(Types::CurrentUserTodos) } @@ -18,7 +18,7 @@ RSpec.describe GitlabSchema.types['Issue'] do confidential discussion_locked upvotes downvotes 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] + create_note_email timelogs project_id] fields.each do |field_name| expect(described_class).to have_graphql_field(field_name) diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb index 875a16a79e5..bc3ccb0d9ba 100644 --- a/spec/graphql/types/merge_request_type_spec.rb +++ b/spec/graphql/types/merge_request_type_spec.rb @@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['MergeRequest'] do specify { expect(described_class).to require_graphql_authorizations(:read_merge_request) } - specify { expect(described_class.interfaces).to include(Types::Notes::NoteableType) } + specify { expect(described_class.interfaces).to include(Types::Notes::NoteableInterface) } specify { expect(described_class.interfaces).to include(Types::CurrentUserTodos) } diff --git a/spec/graphql/types/milestone_type_spec.rb b/spec/graphql/types/milestone_type_spec.rb index 5c2ae5cea3c..f00acb3f7cf 100644 --- a/spec/graphql/types/milestone_type_spec.rb +++ b/spec/graphql/types/milestone_type_spec.rb @@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['Milestone'] do it 'has the expected fields' do expected_fields = %w[ - id iid title description state web_path + id iid title description state expired web_path due_date start_date created_at updated_at project_milestone group_milestone subgroup_milestone stats diff --git a/spec/graphql/types/notes/discussion_type_spec.rb b/spec/graphql/types/notes/discussion_type_spec.rb index 37ed861d069..5290c1e2eb6 100644 --- a/spec/graphql/types/notes/discussion_type_spec.rb +++ b/spec/graphql/types/notes/discussion_type_spec.rb @@ -13,6 +13,7 @@ RSpec.describe GitlabSchema.types['Discussion'] do resolved resolved_at resolved_by + noteable ] expect(described_class).to have_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/notes/noteable_interface_spec.rb b/spec/graphql/types/notes/noteable_interface_spec.rb new file mode 100644 index 00000000000..be2c30aac72 --- /dev/null +++ b/spec/graphql/types/notes/noteable_interface_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::Notes::NoteableInterface do + it 'exposes the expected fields' do + expected_fields = %i[ + discussions + notes + ] + + expect(described_class).to have_graphql_fields(*expected_fields) + end + + describe ".resolve_type" do + it 'knows the correct type for objects' do + expect(described_class.resolve_type(build(:issue), {})).to eq(Types::IssueType) + expect(described_class.resolve_type(build(:merge_request), {})).to eq(Types::MergeRequestType) + expect(described_class.resolve_type(build(:design), {})).to eq(Types::DesignManagement::DesignType) + expect(described_class.resolve_type(build(:alert_management_alert), {})).to eq(Types::AlertManagement::AlertType) + end + end +end diff --git a/spec/graphql/types/notes/noteable_type_spec.rb b/spec/graphql/types/notes/noteable_type_spec.rb deleted file mode 100644 index fad24c6fed4..00000000000 --- a/spec/graphql/types/notes/noteable_type_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Types::Notes::NoteableType do - it 'exposes the expected fields' do - expected_fields = %i[ - discussions - notes - ] - - expect(described_class).to have_graphql_fields(*expected_fields) - end - - describe ".resolve_type" do - it 'knows the correct type for objects' do - expect(described_class.resolve_type(build(:issue), {})).to eq(Types::IssueType) - expect(described_class.resolve_type(build(:merge_request), {})).to eq(Types::MergeRequestType) - expect(described_class.resolve_type(build(:design), {})).to eq(Types::DesignManagement::DesignType) - expect(described_class.resolve_type(build(:alert_management_alert), {})).to eq(Types::AlertManagement::AlertType) - end - end -end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 0f7cadbd4a7..a22110e8338 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['Project'] do include GraphqlHelpers + include Ci::TemplateHelpers specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Project) } @@ -38,6 +39,61 @@ RSpec.describe GitlabSchema.types['Project'] do expect(described_class).to include_graphql_fields(*expected_fields) end + describe 'container_registry_enabled' do + let_it_be(:project, reload: true) { create(:project, :public) } + let_it_be(:user) { create(:user) } + + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + containerRegistryEnabled + } + } + ) + end + + subject { GitlabSchema.execute(query, context: { current_user: user }).as_json } + + context 'with `enabled` visibility' do + before do + project.project_feature.update_column(:container_registry_access_level, ProjectFeature::ENABLED) + end + + context 'with non member user' do + it 'returns true' do + expect(subject.dig('data', 'project', 'containerRegistryEnabled')).to eq(true) + end + end + end + + context 'with `private` visibility' do + before do + project.project_feature.update_column(:container_registry_access_level, ProjectFeature::PRIVATE) + end + + context 'with reporter user' do + before do + project.add_reporter(user) + end + + it 'returns true' do + expect(subject.dig('data', 'project', 'containerRegistryEnabled')).to eq(true) + end + end + + context 'with guest user' do + before do + project.add_guest(user) + end + + it 'returns false' do + expect(subject.dig('data', 'project', 'containerRegistryEnabled')).to eq(false) + end + end + end + end + describe 'sast_ci_configuration' do let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } @@ -103,15 +159,14 @@ RSpec.describe GitlabSchema.types['Project'] do subject { GitlabSchema.execute(query, context: { current_user: user }).as_json } it "returns the project's sast configuration for global variables" do - secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration', 'global', 'nodes').first - expect(secure_analyzers_prefix['type']).to eq('string') - expect(secure_analyzers_prefix['field']).to eq('SECURE_ANALYZERS_PREFIX') - expect(secure_analyzers_prefix['label']).to eq('Image prefix') - expect(secure_analyzers_prefix['defaultValue']) - .to eq('registry.gitlab.com/gitlab-org/security-products/analyzers') - expect(secure_analyzers_prefix['value']).to eq('registry.gitlab.com/gitlab-org/security-products/analyzers') - expect(secure_analyzers_prefix['size']).to eq('LARGE') - expect(secure_analyzers_prefix['options']).to be_nil + secure_analyzers = subject.dig('data', 'project', 'sastCiConfiguration', 'global', 'nodes').first + expect(secure_analyzers['type']).to eq('string') + expect(secure_analyzers['field']).to eq('SECURE_ANALYZERS_PREFIX') + expect(secure_analyzers['label']).to eq('Image prefix') + expect(secure_analyzers['defaultValue']).to eq(secure_analyzers_prefix) + expect(secure_analyzers['value']).to eq(secure_analyzers_prefix) + expect(secure_analyzers['size']).to eq('LARGE') + expect(secure_analyzers['options']).to be_nil end it "returns the project's sast configuration for pipeline variables" do @@ -387,4 +442,11 @@ RSpec.describe GitlabSchema.types['Project'] do it { is_expected.to have_graphql_type(Types::Ci::TemplateType) } it { is_expected.to have_graphql_arguments(:name) } end + + describe 'ci_job_token_scope field' do + subject { described_class.fields['ciJobTokenScope'] } + + it { is_expected.to have_graphql_type(Types::Ci::JobTokenScopeType) } + it { is_expected.to have_graphql_resolver(Resolvers::Ci::JobTokenScopeResolver) } + end end diff --git a/spec/graphql/types/projects/service_type_spec.rb b/spec/graphql/types/projects/service_type_spec.rb index 567bdfaec24..cb09f1ca6cc 100644 --- a/spec/graphql/types/projects/service_type_spec.rb +++ b/spec/graphql/types/projects/service_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Types::Projects::ServiceType do describe ".resolve_type" do it 'resolves the corresponding type for objects' do - expect(described_class.resolve_type(build(:jira_service), {})).to eq(Types::Projects::Services::JiraServiceType) + expect(described_class.resolve_type(build(:jira_integration), {})).to eq(Types::Projects::Services::JiraServiceType) expect(described_class.resolve_type(build(:service), {})).to eq(Types::Projects::Services::BaseServiceType) expect(described_class.resolve_type(build(:drone_ci_integration), {})).to eq(Types::Projects::Services::BaseServiceType) expect(described_class.resolve_type(build(:custom_issue_tracker_integration), {})).to eq(Types::Projects::Services::BaseServiceType) diff --git a/spec/graphql/types/projects/services_enum_spec.rb b/spec/graphql/types/projects/services_enum_spec.rb index 39c2dcd07f6..00427e1d580 100644 --- a/spec/graphql/types/projects/services_enum_spec.rb +++ b/spec/graphql/types/projects/services_enum_spec.rb @@ -8,6 +8,6 @@ RSpec.describe GitlabSchema.types['ServiceType'] do end def available_services_enum - ::Integration.available_services_types(include_dev: false).map(&:underscore).map(&:upcase) + ::Integration.available_integration_types(include_dev: false).map(&:underscore).map(&:upcase) end end diff --git a/spec/graphql/types/query_complexity_type_spec.rb b/spec/graphql/types/query_complexity_type_spec.rb new file mode 100644 index 00000000000..6b2330f2b13 --- /dev/null +++ b/spec/graphql/types/query_complexity_type_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['QueryComplexity'] do + include GraphqlHelpers + + specify do + expect(described_class).to have_graphql_fields(:limit, :score).only + end + + it 'works when executed' do + query = <<-GQL + query { + queryComplexity { + score + limit + } + + currentUser { + name + } + } + GQL + + query_result = run_with_clean_state(query).to_h + + data = graphql_dig_at(query_result, :data, :queryComplexity) + + expect(data).to include( + 'score' => be > 0, + 'limit' => GitlabSchema::DEFAULT_MAX_COMPLEXITY + ) + 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 6800d5459c4..0c903b8d27a 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 direct_asset_url + id name url external link_type direct_asset_url direct_asset_path ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/snippets/blob_type_spec.rb b/spec/graphql/types/snippets/blob_type_spec.rb index 60c0db8e551..e20b001ba7f 100644 --- a/spec/graphql/types/snippets/blob_type_spec.rb +++ b/spec/graphql/types/snippets/blob_type_spec.rb @@ -6,7 +6,7 @@ RSpec.describe GitlabSchema.types['SnippetBlob'] do include GraphqlHelpers it 'has the correct fields' do - expected_fields = [:rich_data, :plain_data, + expected_fields = [:rich_data, :plain_data, :raw_plain_data, :raw_path, :size, :binary, :name, :path, :simple_viewer, :rich_viewer, :mode, :external_storage, :rendered_as_text] @@ -18,6 +18,7 @@ RSpec.describe GitlabSchema.types['SnippetBlob'] do { 'richData' => be_nullable, 'plainData' => be_nullable, + 'rawPlainData' => be_nullable, 'rawPath' => be_non_null, 'size' => be_non_null, 'binary' => be_non_null, -- cgit v1.2.3