diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 14:10:13 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 14:10:13 +0300 |
commit | 0ea3fcec397b69815975647f5e2aa5fe944a8486 (patch) | |
tree | 7979381b89d26011bcf9bdc989a40fcc2f1ed4ff /spec/controllers | |
parent | 72123183a20411a36d607d70b12d57c484394c8e (diff) |
Add latest changes from gitlab-org/gitlab@15-1-stable-eev15.1.0-rc42
Diffstat (limited to 'spec/controllers')
49 files changed, 974 insertions, 1250 deletions
diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index ca2b50b529c..c432adb6ae3 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -210,63 +210,6 @@ RSpec.describe Admin::ClustersController do end end - describe 'POST authorize AWS role for EKS cluster' do - let!(:role) { create(:aws_role, user: admin) } - - let(:role_arn) { 'arn:new-role' } - let(:params) do - { - cluster: { - role_arn: role_arn - } - } - end - - def go - post :authorize_aws_role, params: params - end - - include_examples ':certificate_based_clusters feature flag controller responses' do - let(:subject) { go } - end - - before do - allow(Clusters::Aws::FetchCredentialsService).to receive(:new) - .and_return(double(execute: double)) - end - - it 'updates the associated role with the supplied ARN' do - go - - expect(response).to have_gitlab_http_status(:ok) - expect(role.reload.role_arn).to eq(role_arn) - end - - context 'supplied role is invalid' do - let(:role_arn) { 'invalid-role' } - - it 'does not update the associated role' do - expect { go }.not_to change { role.role_arn } - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - - describe 'security' do - before do - allow_next_instance_of(Clusters::Aws::AuthorizeRoleService) do |service| - response = double(status: :ok, body: double) - - allow(service).to receive(:execute).and_return(response) - end - end - - it { expect { go }.to be_allowed_for(:admin) } - it { expect { go }.to be_denied_for(:user) } - it { expect { go }.to be_denied_for(:external) } - end - end - describe 'DELETE clear cluster cache' do let(:cluster) { create(:cluster, :instance) } let!(:kubernetes_namespace) do diff --git a/spec/controllers/admin/integrations_controller_spec.rb b/spec/controllers/admin/integrations_controller_spec.rb index 410bc0ddc1d..0e456858b49 100644 --- a/spec/controllers/admin/integrations_controller_spec.rb +++ b/spec/controllers/admin/integrations_controller_spec.rb @@ -43,7 +43,7 @@ RSpec.describe Admin::IntegrationsController do end describe '#update' do - include JiraServiceHelper + include JiraIntegrationHelpers let(:integration) { create(:jira_integration, :instance) } diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index ddd80b67639..c5306fda0a5 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -105,10 +105,6 @@ RSpec.describe ApplicationController do describe 'session expiration' do controller(described_class) do - # The anonymous controller will report 401 and fail to run any actions. - # Normally, GitLab will just redirect you to sign in. - skip_before_action :authenticate_user!, only: :index - def index render html: 'authenticated' end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 0a809e80fcd..e874df62cd7 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -411,6 +411,7 @@ RSpec.describe AutocompleteController do expect(json_response.count).to eq(1) expect(json_response.first['title']).to eq(deploy_key.title) expect(json_response.first['owner']['id']).to eq(deploy_key.user.id) + expect(json_response.first['deploy_keys_projects']).to be_nil end context 'with an unknown project' do @@ -433,6 +434,7 @@ RSpec.describe AutocompleteController do expect(json_response.count).to eq(1) expect(json_response.first['title']).to eq(deploy_key.title) expect(json_response.first['owner']).to be_nil + expect(json_response.first['deploy_keys_projects']).to be_nil end end end diff --git a/spec/controllers/chaos_controller_spec.rb b/spec/controllers/chaos_controller_spec.rb index 26ae4a6b693..36ccf868d82 100644 --- a/spec/controllers/chaos_controller_spec.rb +++ b/spec/controllers/chaos_controller_spec.rb @@ -147,8 +147,8 @@ RSpec.describe ChaosController do let(:gc_stat) { GC.stat.stringify_keys } it 'runs a full GC on the current web worker' do - expect(Prometheus::PidProvider).to receive(:worker_id).and_return('worker-0') - expect(Gitlab::Chaos).to receive(:run_gc).and_return(gc_stat) + allow(Prometheus::PidProvider).to receive(:worker_id).and_return('worker-0') + allow(Gitlab::Chaos).to receive(:run_gc).and_return(gc_stat) post :gc diff --git a/spec/controllers/concerns/sorting_preference_spec.rb b/spec/controllers/concerns/sorting_preference_spec.rb index c0091e8b694..82a920215ca 100644 --- a/spec/controllers/concerns/sorting_preference_spec.rb +++ b/spec/controllers/concerns/sorting_preference_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe SortingPreference do let(:user) { create(:user) } + let(:params) { {} } let(:controller_class) do Class.new do @@ -23,6 +24,46 @@ RSpec.describe SortingPreference do allow(controller).to receive(:sorting_field).and_return(:issues_sort) end + describe '#set_sort_order' do + let(:group) { build(:group) } + let(:issue_weights_available) { true } + + before do + allow(controller).to receive(:default_sort_order).and_return('updated_desc') + allow(controller).to receive(:action_name).and_return('issues') + allow(controller).to receive(:can_sort_by_issue_weight?).and_return(issue_weights_available) + user.user_preference.update!(issues_sort: sorting_field) + end + + subject { controller.send(:set_sort_order) } + + context 'when user preference contains allowed sorting' do + let(:sorting_field) { 'updated_asc' } + + it 'sets sort order from user_preference' do + is_expected.to eq('updated_asc') + end + end + + context 'when user preference contains weight sorting' do + let(:sorting_field) { 'weight_desc' } + + context 'when user can sort by issue weight' do + it 'sets sort order from user_preference' do + is_expected.to eq('weight_desc') + end + end + + context 'when user cannot sort by issue weight' do + let(:issue_weights_available) { false } + + it 'sets default sort order' do + is_expected.to eq('updated_desc') + end + end + end + end + describe '#set_sort_order_from_user_preference' do subject { controller.send(:set_sort_order_from_user_preference) } @@ -49,8 +90,6 @@ RSpec.describe SortingPreference do end context 'when a user sorting preference exists' do - let(:params) { {} } - before do user.user_preference.update!(issues_sort: 'updated_asc') end @@ -81,7 +120,6 @@ RSpec.describe SortingPreference do context 'when cookie exists' do let(:cookies) { { 'issue_sort' => 'id_asc' } } - let(:params) { {} } it 'sets the cookie with the right values and flags' do subject diff --git a/spec/controllers/confirmations_controller_spec.rb b/spec/controllers/confirmations_controller_spec.rb index 3b5afbcebca..5b137ada141 100644 --- a/spec/controllers/confirmations_controller_spec.rb +++ b/spec/controllers/confirmations_controller_spec.rb @@ -146,13 +146,26 @@ RSpec.describe ConfirmationsController do stub_application_setting(recaptcha_enabled: true) end - it 'displays an error when the reCAPTCHA is not solved' do - Recaptcha.configuration.skip_verify_env.delete('test') + context 'when the reCAPTCHA is not solved' do + before do + Recaptcha.configuration.skip_verify_env.delete('test') + end - perform_request + it 'displays an error' do + perform_request - expect(response).to render_template(:new) - expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') + expect(response).to render_template(:new) + expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') + end + + it 'sets gon variables' do + Gon.clear + + perform_request + + expect(response).to render_template(:new) + expect(Gon.all_variables).not_to be_empty + end end it 'successfully sends password reset when reCAPTCHA is solved' do diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index 0818dce776d..e85f5b7a972 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -5,6 +5,9 @@ require 'spec_helper' RSpec.describe GraphqlController do include GraphqlHelpers + # two days is enough to make timezones irrelevant + let_it_be(:last_activity_on) { 2.days.ago.to_date } + before do stub_feature_flags(graphql: true) end @@ -40,7 +43,7 @@ RSpec.describe GraphqlController do describe 'POST #execute' do context 'when user is logged in' do - let(:user) { create(:user, last_activity_on: Date.yesterday) } + let(:user) { create(:user, last_activity_on: last_activity_on) } before do sign_in(user) @@ -161,7 +164,7 @@ RSpec.describe GraphqlController do end context 'when 2FA is required for the user' do - let(:user) { create(:user, last_activity_on: Date.yesterday) } + let(:user) { create(:user, last_activity_on: last_activity_on) } before do group = create(:group, require_two_factor_authentication: true) @@ -186,14 +189,14 @@ RSpec.describe GraphqlController do end context 'when user uses an API token' do - let(:user) { create(:user, last_activity_on: Date.yesterday) } + let(:user) { create(:user, last_activity_on: last_activity_on) } let(:token) { create(:personal_access_token, user: user, scopes: [:api]) } let(:query) { '{ __typename }' } subject { post :execute, params: { query: query, access_token: token.token } } context 'when the user is a project bot' do - let(:user) { create(:user, :project_bot, last_activity_on: Date.yesterday) } + let(:user) { create(:user, :project_bot, last_activity_on: last_activity_on) } it 'updates the users last_activity_on field' do expect { subject }.to change { user.reload.last_activity_on } diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index 4b82c5ceb1c..eb3fe4bc330 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -262,142 +262,6 @@ RSpec.describe Groups::ClustersController do end end - describe 'POST #create_aws' do - let(:params) do - { - cluster: { - name: 'new-cluster', - provider_aws_attributes: { - key_name: 'key', - role_arn: 'arn:role', - region: 'region', - vpc_id: 'vpc', - instance_type: 'instance type', - num_nodes: 3, - security_group_id: 'security group', - subnet_ids: %w(subnet1 subnet2) - } - } - } - end - - def post_create_aws - post :create_aws, params: params.merge(group_id: group) - end - - include_examples ':certificate_based_clusters feature flag controller responses' do - let(:subject) { post_create_aws } - end - - it 'creates a new cluster' do - expect(ClusterProvisionWorker).to receive(:perform_async) - expect { post_create_aws }.to change { Clusters::Cluster.count } - .and change { Clusters::Providers::Aws.count } - - cluster = group.clusters.first - - expect(response).to have_gitlab_http_status(:created) - expect(response.location).to eq(group_cluster_path(group, cluster)) - expect(cluster).to be_aws - expect(cluster).to be_kubernetes - end - - context 'params are invalid' do - let(:params) do - { - cluster: { name: '' } - } - end - - it 'does not create a cluster' do - expect { post_create_aws }.not_to change { Clusters::Cluster.count } - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(response.media_type).to eq('application/json') - expect(response.body).to include('is invalid') - end - end - - describe 'security' do - before do - allow(WaitForClusterCreationWorker).to receive(:perform_in) - end - - it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { expect { post_create_aws }.to be_allowed_for(:admin) } - it('is denied for admin when admin mode is disabled') { expect { post_create_aws }.to be_denied_for(:admin) } - it { expect { post_create_aws }.to be_allowed_for(:owner).of(group) } - it { expect { post_create_aws }.to be_allowed_for(:maintainer).of(group) } - it { expect { post_create_aws }.to be_denied_for(:developer).of(group) } - it { expect { post_create_aws }.to be_denied_for(:reporter).of(group) } - it { expect { post_create_aws }.to be_denied_for(:guest).of(group) } - it { expect { post_create_aws }.to be_denied_for(:user) } - it { expect { post_create_aws }.to be_denied_for(:external) } - end - end - - describe 'POST authorize AWS role for EKS cluster' do - let!(:role) { create(:aws_role, user: user) } - - let(:role_arn) { 'arn:new-role' } - let(:params) do - { - cluster: { - role_arn: role_arn - } - } - end - - def go - post :authorize_aws_role, params: params.merge(group_id: group) - end - - include_examples ':certificate_based_clusters feature flag controller responses' do - let(:subject) { go } - end - - before do - allow(Clusters::Aws::FetchCredentialsService).to receive(:new) - .and_return(double(execute: double)) - end - - it 'updates the associated role with the supplied ARN' do - go - - expect(response).to have_gitlab_http_status(:ok) - expect(role.reload.role_arn).to eq(role_arn) - end - - context 'supplied role is invalid' do - let(:role_arn) { 'invalid-role' } - - it 'does not update the associated role' do - expect { go }.not_to change { role.role_arn } - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - - describe 'security' do - before do - allow_next_instance_of(Clusters::Aws::AuthorizeRoleService) do |service| - response = double(status: :ok, body: double) - - allow(service).to receive(:execute).and_return(response) - end - end - - it('is allowed for admin when admin mode is enabled', :enable_admin_mode) { expect { go }.to be_allowed_for(:admin) } - it('is denied for admin when admin mode is disabled') { expect { go }.to be_denied_for(:admin) } - it { expect { go }.to be_allowed_for(:owner).of(group) } - it { expect { go }.to be_allowed_for(:maintainer).of(group) } - it { expect { go }.to be_denied_for(:developer).of(group) } - it { expect { go }.to be_denied_for(:reporter).of(group) } - it { expect { go }.to be_denied_for(:guest).of(group) } - it { expect { go }.to be_denied_for(:user) } - it { expect { go }.to be_denied_for(:external) } - end - end - describe 'DELETE clear cluster cache' do let(:cluster) { create(:cluster, :group, groups: [group]) } let!(:kubernetes_namespace) do diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index 25d32436d58..c6fd184ede0 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -305,11 +305,37 @@ RSpec.describe Groups::GroupMembersController do group.add_owner(user) end - it 'cannot removes himself from the group' do + it 'cannot remove user from the group' do delete :leave, params: { group_id: group } expect(response).to have_gitlab_http_status(:forbidden) end + + context 'and there is a group project bot owner' do + before do + create(:group_member, :owner, source: group, user: create(:user, :project_bot)) + end + + it 'cannot remove user from the group' do + delete :leave, params: { group_id: group } + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'and there is another owner' do + before do + create(:group_member, :owner, source: group) + end + + it 'removes user from members', :aggregate_failures do + delete :leave, params: { group_id: group } + + expect(controller).to set_flash.to "You left the \"#{group.name}\" group." + expect(response).to redirect_to(dashboard_groups_path) + expect(group.users).not_to include user + end + end end context 'and is a requester' do diff --git a/spec/controllers/groups/settings/integrations_controller_spec.rb b/spec/controllers/groups/settings/integrations_controller_spec.rb index c070094babd..377c38ce087 100644 --- a/spec/controllers/groups/settings/integrations_controller_spec.rb +++ b/spec/controllers/groups/settings/integrations_controller_spec.rb @@ -76,7 +76,7 @@ RSpec.describe Groups::Settings::IntegrationsController do end describe '#update' do - include JiraServiceHelper + include JiraIntegrationHelpers let(:integration) { create(:jira_integration, :group, group: group) } diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 4a74eff90dc..aabceda7187 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -18,7 +18,6 @@ RSpec.describe GroupsController, factory_default: :keep do let_it_be(:guest) { group.add_guest(create(:user)).user } before do - stub_feature_flags(vue_issues_list: true) enable_admin_mode!(admin_with_admin_mode) end @@ -373,13 +372,26 @@ RSpec.describe GroupsController, factory_default: :keep do end end - it 'displays an error when the reCAPTCHA is not solved' do - allow(controller).to receive(:verify_recaptcha).and_return(false) + context 'when the reCAPTCHA is not solved' do + before do + allow(controller).to receive(:verify_recaptcha).and_return(false) + end - post :create, params: { group: { name: 'new_group', path: "new_group" } } + it 'displays an error' do + post :create, params: { group: { name: 'new_group', path: "new_group" } } + + expect(response).to render_template(:new) + expect(flash[:alert]).to eq(_('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')) + end + + it 'sets gon variables' do + Gon.clear + + post :create, params: { group: { name: 'new_group', path: "new_group" } } - expect(response).to render_template(:new) - expect(flash[:alert]).to eq(_('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')) + expect(response).to render_template(:new) + expect(Gon.all_variables).not_to be_empty + end end it 'allows creating a group when the reCAPTCHA is solved' do diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb index 70dc710f604..26e65711e9f 100644 --- a/spec/controllers/help_controller_spec.rb +++ b/spec/controllers/help_controller_spec.rb @@ -4,34 +4,35 @@ require 'spec_helper' RSpec.describe HelpController do include StubVersion + include DocUrlHelper let(:user) { create(:user) } shared_examples 'documentation pages local render' do it 'renders HTML' do aggregate_failures do - is_expected.to render_template('show.html.haml') + is_expected.to render_template('help/show') expect(response.media_type).to eq 'text/html' end end end shared_examples 'documentation pages redirect' do |documentation_base_url| - let(:gitlab_version) { '13.4.0-ee' } + let(:gitlab_version) { version } before do stub_version(gitlab_version, 'ignored_revision_value') end it 'redirects user to custom documentation url with a specified version' do - is_expected.to redirect_to("#{documentation_base_url}/13.4/ee/#{path}.html") + is_expected.to redirect_to(doc_url(documentation_base_url)) end context 'when it is a pre-release' do let(:gitlab_version) { '13.4.0-pre' } it 'redirects user to custom documentation url without a version' do - is_expected.to redirect_to("#{documentation_base_url}/ee/#{path}.html") + is_expected.to redirect_to(doc_url_without_version(documentation_base_url)) end end end @@ -43,7 +44,7 @@ RSpec.describe HelpController do describe 'GET #index' do context 'with absolute url' do it 'keeps the URL absolute' do - stub_readme("[API](/api/README.md)") + stub_doc_file_read(content: "[API](/api/README.md)") get :index @@ -53,7 +54,7 @@ RSpec.describe HelpController do context 'with relative url' do it 'prefixes it with /help/' do - stub_readme("[API](api/README.md)") + stub_doc_file_read(content: "[API](api/README.md)") get :index @@ -63,7 +64,7 @@ RSpec.describe HelpController do context 'when url is an external link' do it 'does not change it' do - stub_readme("[external](https://some.external.link)") + stub_doc_file_read(content: "[external](https://some.external.link)") get :index @@ -73,7 +74,7 @@ RSpec.describe HelpController do context 'when relative url with external on same line' do it 'prefix it with /help/' do - stub_readme("[API](api/README.md) [external](https://some.external.link)") + stub_doc_file_read(content: "[API](api/README.md) [external](https://some.external.link)") get :index @@ -83,7 +84,7 @@ RSpec.describe HelpController do context 'when relative url with http:// in query' do it 'prefix it with /help/' do - stub_readme("[API](api/README.md?go=https://example.com/)") + stub_doc_file_read(content: "[API](api/README.md?go=https://example.com/)") get :index @@ -93,7 +94,7 @@ RSpec.describe HelpController do context 'when mailto URL' do it 'do not change it' do - stub_readme("[report bug](mailto:bugs@example.com)") + stub_doc_file_read(content: "[report bug](mailto:bugs@example.com)") get :index @@ -103,7 +104,7 @@ RSpec.describe HelpController do context 'when protocol-relative link' do it 'do not change it' do - stub_readme("[protocol-relative](//example.com)") + stub_doc_file_read(content: "[protocol-relative](//example.com)") get :index @@ -146,7 +147,7 @@ RSpec.describe HelpController do context 'when requested file exists' do before do - expect_file_read(File.join(Rails.root, 'doc/user/ssh.md'), content: fixture_file('blockquote_fence_after.md')) + stub_doc_file_read(file_name: 'user/ssh.md', content: fixture_file('blockquote_fence_after.md')) subject end @@ -265,10 +266,6 @@ RSpec.describe HelpController do end end - def stub_readme(content) - expect_file_read(Rails.root.join('doc', 'index.md'), content: content) - end - def stub_two_factor_required allow(controller).to receive(:two_factor_authentication_required?).and_return(true) allow(controller).to receive(:current_user_requires_two_factor?).and_return(true) diff --git a/spec/controllers/import/fogbugz_controller_spec.rb b/spec/controllers/import/fogbugz_controller_spec.rb index d351e1cc3f3..8f8cc9590a5 100644 --- a/spec/controllers/import/fogbugz_controller_spec.rb +++ b/spec/controllers/import/fogbugz_controller_spec.rb @@ -6,14 +6,14 @@ RSpec.describe Import::FogbugzController do include ImportSpecHelper let(:user) { create(:user) } + let(:token) { FFaker::Lorem.characters(8) } + let(:uri) { 'https://example.com' } before do sign_in(user) end describe 'POST #callback' do - let(:token) { FFaker::Lorem.characters(8) } - let(:uri) { 'https://example.com' } let(:xml_response) { %Q(<?xml version=\"1.0\" encoding=\"UTF-8\"?><response><token><![CDATA[#{token}]]></token></response>) } it 'attempts to contact Fogbugz server' do @@ -97,6 +97,38 @@ RSpec.describe Import::FogbugzController do end describe 'POST create' do + let(:repo_id) { 'FOGBUGZ_REPO_ID' } + let(:project) { create(:project) } + let(:client) { instance_double(Gitlab::FogbugzImport::Client, user_map: {}) } + + before do + allow(controller).to receive(:client).and_return(client) + end + + it 'returns the new project' do + expect(Import::FogbugzService).to receive(:new).and_return( + instance_double(Import::FogbugzService, execute: ServiceResponse.success) + ) + + post :create, format: :json + + expect(response).to have_gitlab_http_status(:ok) + end + + it 'returns an error when service reports an error' do + message = 'Error message' + status = :unprocessable_entity + + expect(Import::FogbugzService).to receive(:new).and_return( + instance_double(Import::FogbugzService, execute: ServiceResponse.error(message: message, http_status: status)) + ) + + post :create, format: :json + + expect(response).to have_gitlab_http_status(status) + expect(json_response).to eq({ 'errors' => message }) + end + it_behaves_like 'project import rate limiter' end end diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index ef66124bff1..56e55c45e66 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -96,19 +96,6 @@ RSpec.describe Import::GithubController do describe "POST personal_access_token" do it_behaves_like 'a GitHub-ish import controller: POST personal_access_token' - - it 'passes namespace_id param as query param if it was present' do - namespace_id = 5 - status_import_url = public_send("status_import_#{provider}_url", { namespace_id: namespace_id }) - - allow_next_instance_of(Gitlab::LegacyGithubImport::Client) do |client| - allow(client).to receive(:user).and_return(true) - end - - post :personal_access_token, params: { personal_access_token: 'some-token', namespace_id: 5 } - - expect(controller).to redirect_to(status_import_url) - end end describe "GET status" do diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb index 9fa90dde997..6fffa607bf8 100644 --- a/spec/controllers/metrics_controller_spec.rb +++ b/spec/controllers/metrics_controller_spec.rb @@ -94,8 +94,8 @@ RSpec.describe MetricsController, :request_store do end it 'renders system stats JSON' do - expect(Prometheus::PidProvider).to receive(:worker_id).and_return('worker-0') - expect(Gitlab::Metrics::System).to receive(:summary).and_return(summary) + allow(Prometheus::PidProvider).to receive(:worker_id).and_return('worker-0') + allow(Gitlab::Metrics::System).to receive(:summary).and_return(summary) get :system diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb index 7489f506674..fb90a70d91d 100644 --- a/spec/controllers/oauth/authorizations_controller_spec.rb +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -195,6 +195,24 @@ RSpec.describe Oauth::AuthorizationsController do end end end + + context 'when the user is not signed in' do + before do + sign_out(user) + end + + it 'sets a lower session expiry and redirects to the sign in page' do + subject + + expect(request.env['rack.session.options'][:expire_after]).to eq( + Settings.gitlab['unauthenticated_session_expire_delay'] + ) + + expect(request.session['user_return_to']).to eq("/oauth/authorize?#{params.to_query}") + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(new_user_session_path) + end + end end describe 'POST #create' do diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb index e70b8af2068..9ecef8b7450 100644 --- a/spec/controllers/omniauth_callbacks_controller_spec.rb +++ b/spec/controllers/omniauth_callbacks_controller_spec.rb @@ -222,10 +222,36 @@ RSpec.describe OmniauthCallbacksController, type: :controller do context 'sign up' do include_context 'sign_up' - it 'is allowed' do - post provider + context 'when intent to register is added to omniauth params' do + before do + request.env['omniauth.params'] = { 'intent' => 'register' } + end - expect(request.env['warden']).to be_authenticated + it 'is allowed' do + post provider + + expect(request.env['warden']).to be_authenticated + end + + it 'redirects to welcome path' do + post provider + + expect(response).to redirect_to(users_sign_up_welcome_path) + end + end + + context 'when intent to register is not added to omniauth params' do + it 'is allowed' do + post provider + + expect(request.env['warden']).to be_authenticated + end + + it 'redirects to root path' do + post provider + + expect(response).to redirect_to(root_path) + end end end diff --git a/spec/controllers/passwords_controller_spec.rb b/spec/controllers/passwords_controller_spec.rb index 82014282c6e..e4be2fbef3c 100644 --- a/spec/controllers/passwords_controller_spec.rb +++ b/spec/controllers/passwords_controller_spec.rb @@ -115,13 +115,26 @@ RSpec.describe PasswordsController do stub_application_setting(recaptcha_enabled: true) end - it 'displays an error when the reCAPTCHA is not solved' do - Recaptcha.configuration.skip_verify_env.delete('test') + context 'when the reCAPTCHA is not solved' do + before do + Recaptcha.configuration.skip_verify_env.delete('test') + end - perform_request + it 'displays an error' do + perform_request + + expect(response).to render_template(:new) + expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') + end - expect(response).to render_template(:new) - expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') + it 'sets gon variables' do + Gon.clear + + perform_request + + expect(response).to render_template(:new) + expect(Gon.all_variables).not_to be_empty + end end it 'successfully sends password reset when reCAPTCHA is solved' do diff --git a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb index 3859af66292..48c747bf074 100644 --- a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb +++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb @@ -39,30 +39,19 @@ RSpec.describe Profiles::PersonalAccessTokensController do describe '#index' do let!(:active_personal_access_token) { create(:personal_access_token, user: user) } - let!(:inactive_personal_access_token) { create(:personal_access_token, :revoked, user: user) } - let!(:impersonation_personal_access_token) { create(:personal_access_token, :impersonation, user: user) } - let(:token_value) { 's3cr3t' } before do - PersonalAccessToken.redis_store!(user.id, token_value) + # Impersonation and inactive personal tokens are ignored + create(:personal_access_token, :impersonation, user: user) + create(:personal_access_token, :revoked, user: user) get :index end - it "retrieves active personal access tokens" do - expect(assigns(:active_personal_access_tokens)).to include(active_personal_access_token) - end - - it "retrieves inactive personal access tokens" do - expect(assigns(:inactive_personal_access_tokens)).to include(inactive_personal_access_token) - end - - it "does not retrieve impersonation personal access tokens" do - expect(assigns(:active_personal_access_tokens)).not_to include(impersonation_personal_access_token) - expect(assigns(:inactive_personal_access_tokens)).not_to include(impersonation_personal_access_token) - end + it "only includes details of the active personal access token" do + active_personal_access_tokens_detail = ::API::Entities::PersonalAccessTokenWithDetails + .represent([active_personal_access_token]) - it "retrieves newly created personal access token value" do - expect(assigns(:new_personal_access_token)).to eql(token_value) + expect(assigns(:active_personal_access_tokens).to_json).to eq(active_personal_access_tokens_detail.to_json) end it "sets PAT name and scopes" do diff --git a/spec/controllers/projects/autocomplete_sources_controller_spec.rb b/spec/controllers/projects/autocomplete_sources_controller_spec.rb index 79edc261809..a5274b6543e 100644 --- a/spec/controllers/projects/autocomplete_sources_controller_spec.rb +++ b/spec/controllers/projects/autocomplete_sources_controller_spec.rb @@ -114,17 +114,5 @@ RSpec.describe Projects::AutocompleteSourcesController do end end end - - context 'when feature flag is disabled' do - before do - stub_feature_flags(customer_relations: false) - end - - it 'renders 404' do - get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path } - - expect(response).to have_gitlab_http_status(:not_found) - end - end end end diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 01420e30d24..d45ea268e64 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -272,150 +272,6 @@ RSpec.describe Projects::ClustersController do end end - describe 'POST #create_aws' do - let(:params) do - { - cluster: { - name: 'new-cluster', - provider_aws_attributes: { - key_name: 'key', - role_arn: 'arn:role', - region: 'region', - vpc_id: 'vpc', - instance_type: 'instance type', - num_nodes: 3, - security_group_id: 'security group', - subnet_ids: %w(subnet1 subnet2) - } - } - } - end - - def post_create_aws - post :create_aws, params: params.merge(namespace_id: project.namespace, project_id: project) - end - - include_examples ':certificate_based_clusters feature flag controller responses' do - let(:subject) { post_create_aws } - end - - it 'creates a new cluster' do - expect(ClusterProvisionWorker).to receive(:perform_async) - expect { post_create_aws }.to change { Clusters::Cluster.count } - .and change { Clusters::Providers::Aws.count } - - cluster = project.clusters.first - - expect(response).to have_gitlab_http_status(:created) - expect(response.location).to eq(project_cluster_path(project, cluster)) - expect(cluster).to be_aws - expect(cluster).to be_kubernetes - end - - context 'params are invalid' do - let(:params) do - { - cluster: { name: '' } - } - end - - it 'does not create a cluster' do - expect { post_create_aws }.not_to change { Clusters::Cluster.count } - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(response.media_type).to eq('application/json') - expect(response.body).to include('is invalid') - end - end - - describe 'security' do - before do - allow(WaitForClusterCreationWorker).to receive(:perform_in) - end - - it 'is allowed for admin when admin mode enabled', :enable_admin_mode do - expect { post_create_aws }.to be_allowed_for(:admin) - end - it 'is disabled for admin when admin mode disabled' do - expect { post_create_aws }.to be_denied_for(:admin) - end - it { expect { post_create_aws }.to be_allowed_for(:owner).of(project) } - it { expect { post_create_aws }.to be_allowed_for(:maintainer).of(project) } - it { expect { post_create_aws }.to be_denied_for(:developer).of(project) } - it { expect { post_create_aws }.to be_denied_for(:reporter).of(project) } - it { expect { post_create_aws }.to be_denied_for(:guest).of(project) } - it { expect { post_create_aws }.to be_denied_for(:user) } - it { expect { post_create_aws }.to be_denied_for(:external) } - end - end - - describe 'POST authorize AWS role for EKS cluster' do - let!(:role) { create(:aws_role, user: user) } - - let(:role_arn) { 'arn:new-role' } - let(:params) do - { - cluster: { - role_arn: role_arn - } - } - end - - def go - post :authorize_aws_role, params: params.merge(namespace_id: project.namespace, project_id: project) - end - - before do - allow(Clusters::Aws::FetchCredentialsService).to receive(:new) - .and_return(double(execute: double)) - end - - include_examples ':certificate_based_clusters feature flag controller responses' do - let(:subject) { go } - end - - it 'updates the associated role with the supplied ARN' do - go - - expect(response).to have_gitlab_http_status(:ok) - expect(role.reload.role_arn).to eq(role_arn) - end - - context 'supplied role is invalid' do - let(:role_arn) { 'invalid-role' } - - it 'does not update the associated role' do - expect { go }.not_to change { role.role_arn } - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - - describe 'security' do - before do - allow_next_instance_of(Clusters::Aws::AuthorizeRoleService) do |service| - response = double(status: :ok, body: double) - - allow(service).to receive(:execute).and_return(response) - end - end - - it 'is allowed for admin when admin mode enabled', :enable_admin_mode do - expect { go }.to be_allowed_for(:admin) - end - it 'is disabled for admin when admin mode disabled' do - expect { go }.to be_denied_for(:admin) - end - it { expect { go }.to be_allowed_for(:owner).of(project) } - it { expect { go }.to be_allowed_for(:maintainer).of(project) } - it { expect { go }.to be_denied_for(:developer).of(project) } - it { expect { go }.to be_denied_for(:reporter).of(project) } - it { expect { go }.to be_denied_for(:guest).of(project) } - it { expect { go }.to be_denied_for(:user) } - it { expect { go }.to be_denied_for(:external) } - end - end - describe 'DELETE clear cluster cache' do let(:cluster) { create(:cluster, :project, projects: [project]) } let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) } diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index c7f98406201..26d4725656f 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -166,6 +166,14 @@ RSpec.describe Projects::CommitsController do end end end + + context 'with markdown cache' do + it 'preloads markdown cache for commits' do + expect(Commit).to receive(:preload_markdown_cache!).and_call_original + + get :show, params: { namespace_id: project.namespace, project_id: project, id: 'master/README.md' } + end + end end describe "GET /commits/:id/signatures" do diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index 9821618df8d..e6e0307d0ca 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -44,6 +44,14 @@ RSpec.describe Projects::CompareController do expect(response).to be_successful end end + + context 'with missing parameters' do + let(:params) { super().merge(from: '', to: '') } + + it 'returns successfully' do + expect(response).to be_successful + end + end end describe 'GET show' do @@ -102,6 +110,23 @@ RSpec.describe Projects::CompareController do end end + context 'when refs have CI::Pipeline' do + let(:from_project_id) { nil } + let(:from_ref) { '08f22f25' } + let(:to_ref) { '59e29889' } + + before do + create(:ci_pipeline, project: project) + end + + it 'avoids N+1 queries' do + control = ActiveRecord::QueryRecorder.new { show_request } + + # Only 1 query to ci/pipeline.rb is allowed + expect(control.find_query(/pipeline\.rb/, 1)).to be_empty + end + end + context 'when the refs exist in different projects that the user can see' do let(:from_project_id) { public_fork.id } let(:from_ref) { 'improve%2Fmore-awesome' } @@ -434,7 +459,7 @@ RSpec.describe Projects::CompareController do expect(CompareService).to receive(:new).with(project, escaped_to_ref).and_return(compare_service) expect(compare_service).to receive(:execute).with(project, escaped_from_ref).and_return(compare) - expect(compare).to receive(:commits).and_return([signature_commit, non_signature_commit]) + expect(compare).to receive(:commits).and_return(CommitCollection.new(project, [signature_commit, non_signature_commit])) expect(non_signature_commit).to receive(:has_signature?).and_return(false) end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index f63e0cea04c..f4cad5790a3 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -208,17 +208,6 @@ RSpec.describe Projects::EnvironmentsController do expect(response).to have_gitlab_http_status(:not_found) end end - - it_behaves_like 'avoids N+1 queries on environment detail page' - - def create_deployment_with_associations(sequence:) - commit = project.commit("HEAD~#{sequence}") - create(:user, email: commit.author_email) - - deployer = create(:user) - build = create(:ci_build, environment: environment.name, pipeline: create(:ci_pipeline, project: environment.project), user: deployer) - create(:deployment, :success, environment: environment, deployable: build, user: deployer, project: project, sha: commit.sha) - end end describe 'GET edit' do diff --git a/spec/controllers/projects/import/jira_controller_spec.rb b/spec/controllers/projects/import/jira_controller_spec.rb index 5288c0fcf21..3f149afbb02 100644 --- a/spec/controllers/projects/import/jira_controller_spec.rb +++ b/spec/controllers/projects/import/jira_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Projects::Import::JiraController do - include JiraServiceHelper + include JiraIntegrationHelpers let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } diff --git a/spec/controllers/projects/incidents_controller_spec.rb b/spec/controllers/projects/incidents_controller_spec.rb index 20cf0dcfd3a..460821634b0 100644 --- a/spec/controllers/projects/incidents_controller_spec.rb +++ b/spec/controllers/projects/incidents_controller_spec.rb @@ -43,7 +43,6 @@ RSpec.describe Projects::IncidentsController do expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:index) - expect(Gon.features).to include('incidentEscalations' => true) end context 'when user is unauthorized' do diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 8a03c1e709b..1305693372c 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -12,10 +12,6 @@ RSpec.describe Projects::IssuesController do let(:issue) { create(:issue, project: project) } let(:spam_action_response_fields) { { 'stub_spam_action_response_fields' => true } } - before do - stub_feature_flags(vue_issues_list: true) - end - describe "GET #index" do context 'external issue tracker' do before do @@ -145,13 +141,104 @@ RSpec.describe Projects::IssuesController do project.add_developer(user) end - it "returns issue_email_participants" do + it "returns issue attributes" do participants = create_list(:issue_email_participant, 2, issue: issue) get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }, format: :json expect(response).to have_gitlab_http_status(:ok) - expect(json_response['issue_email_participants']).to contain_exactly({ "email" => participants[0].email }, { "email" => participants[1].email }) + expect(json_response).to include( + 'issue_email_participants' => contain_exactly( + { "email" => participants[0].email }, { "email" => participants[1].email } + ), + 'type' => 'ISSUE' + ) + end + + context 'when issue is not a task and work items feature flag is enabled' do + it 'does not redirect to work items route' do + get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid } + + expect(response).to render_template(:show) + end + end + + context 'when issue is of type task' do + let(:query) { {} } + + let_it_be(:task) { create(:issue, :task, project: project) } + + context 'when work_items feature flag is enabled' do + shared_examples 'redirects to show work item page' do + it 'redirects to work item page' do + expect(response).to redirect_to(project_work_items_path(project, task.id, query)) + end + end + + context 'show action' do + let(:query) { { query: 'any' } } + + before do + get :show, params: { namespace_id: project.namespace, project_id: project, id: task.iid, **query } + end + + it_behaves_like 'redirects to show work item page' + end + + context 'edit action' do + let(:query) { { query: 'any' } } + + before do + get :edit, params: { namespace_id: project.namespace, project_id: project, id: task.iid, **query } + end + + it_behaves_like 'redirects to show work item page' + end + + context 'update action' do + before do + put :update, params: { namespace_id: project.namespace, project_id: project, id: task.iid, issue: { title: 'New title' } } + end + + it_behaves_like 'redirects to show work item page' + end + end + + context 'when work_items feature flag is disabled' do + before do + stub_feature_flags(work_items: false) + end + + shared_examples 'renders 404' do + it 'renders 404 for show action' do + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'show action' do + before do + get :show, params: { namespace_id: project.namespace, project_id: project, id: task.iid } + end + + it_behaves_like 'renders 404' + end + + context 'edit action' do + before do + get :edit, params: { namespace_id: project.namespace, project_id: project, id: task.iid } + end + + it_behaves_like 'renders 404' + end + + context 'update action' do + before do + put :update, params: { namespace_id: project.namespace, project_id: project, id: task.iid, issue: { title: 'New title' } } + end + + it_behaves_like 'renders 404' + end + end end end diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index f0fbbb65fa5..107eb1ed3a3 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -1075,63 +1075,81 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do before do project.add_role(user, role) sign_in(user) - - post_erase end - shared_examples_for 'erases' do - it 'redirects to the erased job page' do - expect(response).to have_gitlab_http_status(:found) - expect(response).to redirect_to(namespace_project_job_path(id: job.id)) + context 'when project is not undergoing stats refresh' do + before do + post_erase end - it 'erases artifacts' do - expect(job.artifacts_file.present?).to be_falsey - expect(job.artifacts_metadata.present?).to be_falsey - end + shared_examples_for 'erases' do + it 'redirects to the erased job page' do + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(namespace_project_job_path(id: job.id)) + end - it 'erases trace' do - expect(job.trace.exist?).to be_falsey + it 'erases artifacts' do + expect(job.artifacts_file.present?).to be_falsey + expect(job.artifacts_metadata.present?).to be_falsey + end + + it 'erases trace' do + expect(job.trace.exist?).to be_falsey + end end - end - context 'when job is successful and has artifacts' do - let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) } + context 'when job is successful and has artifacts' do + let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) } - it_behaves_like 'erases' - end + it_behaves_like 'erases' + end - context 'when job has live trace and unarchived artifact' do - let(:job) { create(:ci_build, :success, :trace_live, :unarchived_trace_artifact, pipeline: pipeline) } + context 'when job has live trace and unarchived artifact' do + let(:job) { create(:ci_build, :success, :trace_live, :unarchived_trace_artifact, pipeline: pipeline) } - it_behaves_like 'erases' - end + it_behaves_like 'erases' + end - context 'when job is erased' do - let(:job) { create(:ci_build, :erased, pipeline: pipeline) } + context 'when job is erased' do + let(:job) { create(:ci_build, :erased, pipeline: pipeline) } - it 'returns unprocessable_entity' do - expect(response).to have_gitlab_http_status(:unprocessable_entity) + it 'returns unprocessable_entity' do + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end end - end - context 'when user is developer' do - let(:role) { :developer } - let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) } + context 'when user is developer' do + let(:role) { :developer } + let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) } - context 'when triggered by same user' do - let(:triggered_by) { user } + context 'when triggered by same user' do + let(:triggered_by) { user } - it 'has successful status' do - expect(response).to have_gitlab_http_status(:found) + it 'has successful status' do + expect(response).to have_gitlab_http_status(:found) + end + end + + context 'when triggered by different user' do + let(:triggered_by) { create(:user) } + + it 'does not have successful status' do + expect(response).not_to have_gitlab_http_status(:found) + end end end + end + + context 'when project is undergoing stats refresh' do + it_behaves_like 'preventing request because of ongoing project stats refresh' do + let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) } + let(:make_request) { post_erase } - context 'when triggered by different user' do - let(:triggered_by) { create(:user) } + it 'does not erase artifacts' do + make_request - it 'does not have successful status' do - expect(response).not_to have_gitlab_http_status(:found) + expect(job.artifacts_file).to be_present + expect(job.artifacts_metadata).to be_present end end end diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb index 596cd5c1a20..19a04654114 100644 --- a/spec/controllers/projects/mattermosts_controller_spec.rb +++ b/spec/controllers/projects/mattermosts_controller_spec.rb @@ -62,7 +62,7 @@ RSpec.describe Projects::MattermostsController do subject integration = project.integrations.last - expect(subject).to redirect_to(edit_project_integration_path(project, integration)) + expect(subject).to redirect_to(edit_project_settings_integration_path(project, integration)) end end end diff --git a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb index 222bb977beb..b9ede84157d 100644 --- a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb @@ -385,6 +385,38 @@ RSpec.describe Projects::MergeRequests::DraftsController do expect(discussion.resolved?).to eq(false) end end + + context 'publish with note' do + before do + create(:draft_note, merge_request: merge_request, author: user) + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(mr_review_submit_comment: false) + end + + it 'does not create note' do + post :publish, params: params.merge!(note: 'Hello world') + + expect(merge_request.notes.reload.size).to be(1) + end + end + + context 'when feature flag is enabled' do + it 'creates note' do + post :publish, params: params.merge!(note: 'Hello world') + + expect(merge_request.notes.reload.size).to be(2) + end + + it 'does not create note when note param is empty' do + post :publish, params: params.merge!(note: '') + + expect(merge_request.notes.reload.size).to be(1) + end + end + end end describe 'DELETE #destroy' do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index f6db809c2e3..8ccbc0d3fe2 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -1094,7 +1094,7 @@ RSpec.describe Projects::MergeRequestsController do end context 'when processing coverage reports is completed' do - let(:report) { { status: :parsed, data: pipeline.coverage_reports } } + let(:report) { { status: :parsed, data: { 'files' => {} } } } it 'returns coverage reports' do subject @@ -1730,7 +1730,7 @@ RSpec.describe Projects::MergeRequestsController do describe 'POST remove_wip' do before do - merge_request.title = merge_request.wip_title + merge_request.title = merge_request.draft_title merge_request.save! post :remove_wip, @@ -1743,8 +1743,8 @@ RSpec.describe Projects::MergeRequestsController do xhr: true end - it 'removes the wip status' do - expect(merge_request.reload.title).to eq(merge_request.wipless_title) + it 'removes the draft status' do + expect(merge_request.reload.title).to eq(merge_request.draftless_title) end it 'renders MergeRequest as JSON' do diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 07874c8a8af..85e5de46afd 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -84,100 +84,6 @@ RSpec.describe Projects::NotesController do end end - context 'for multiple pages of notes', :aggregate_failures do - # 3 pages worth: 1 normal page, 1 oversized due to clashing updated_at, - # and a final, short page - let!(:page_1) { create_list(:note, 2, noteable: issue, project: project, updated_at: 3.days.ago) } - let!(:page_2) { create_list(:note, 3, noteable: issue, project: project, updated_at: 2.days.ago) } - let!(:page_3) { create_list(:note, 2, noteable: issue, project: project, updated_at: 1.day.ago) } - - # Include a resource event in the middle page as well - let!(:resource_event) { create(:resource_state_event, issue: issue, user: user, created_at: 2.days.ago) } - - let(:page_1_boundary) { microseconds(page_1.last.updated_at + NotesFinder::FETCH_OVERLAP) } - let(:page_2_boundary) { microseconds(page_2.last.updated_at + NotesFinder::FETCH_OVERLAP) } - - around do |example| - freeze_time do - example.run - end - end - - before do - stub_const('Gitlab::UpdatedNotesPaginator::LIMIT', 2) - end - - context 'feature flag enabled' do - before do - stub_feature_flags(paginated_notes: true) - end - - it 'returns the first page of notes' do - expect(Gitlab::EtagCaching::Middleware).to receive(:skip!) - - get :index, params: request_params - - expect(json_response['notes'].count).to eq(page_1.count) - expect(json_response['more']).to be_truthy - expect(json_response['last_fetched_at']).to eq(page_1_boundary) - expect(response.headers['Poll-Interval'].to_i).to eq(1) - end - - it 'returns the second page of notes' do - expect(Gitlab::EtagCaching::Middleware).to receive(:skip!) - - request.headers['X-Last-Fetched-At'] = page_1_boundary - - get :index, params: request_params - - expect(json_response['notes'].count).to eq(page_2.count + 1) # resource event - expect(json_response['more']).to be_truthy - expect(json_response['last_fetched_at']).to eq(page_2_boundary) - expect(response.headers['Poll-Interval'].to_i).to eq(1) - end - - it 'returns the final page of notes' do - expect(Gitlab::EtagCaching::Middleware).to receive(:skip!) - - request.headers['X-Last-Fetched-At'] = page_2_boundary - - get :index, params: request_params - - expect(json_response['notes'].count).to eq(page_3.count) - expect(json_response['more']).to be_falsy - expect(json_response['last_fetched_at']).to eq(microseconds(Time.zone.now)) - expect(response.headers['Poll-Interval'].to_i).to be > 1 - end - - it 'returns an empty page of notes' do - expect(Gitlab::EtagCaching::Middleware).not_to receive(:skip!) - - request.headers['X-Last-Fetched-At'] = microseconds(Time.zone.now) - - get :index, params: request_params - - expect(json_response['notes']).to be_empty - expect(json_response['more']).to be_falsy - expect(json_response['last_fetched_at']).to eq(microseconds(Time.zone.now)) - expect(response.headers['Poll-Interval'].to_i).to be > 1 - end - end - - context 'feature flag disabled' do - before do - stub_feature_flags(paginated_notes: false) - end - - it 'returns all notes' do - get :index, params: request_params - - expect(json_response['notes'].count).to eq((page_1 + page_2 + page_3).size + 1) - expect(json_response['more']).to be_falsy - expect(json_response['last_fetched_at']).to eq(microseconds(Time.zone.now)) - end - end - end - context 'for a discussion note' do let(:project) { create(:project, :repository) } let!(:note) { create(:discussion_note_on_merge_request, project: project) } diff --git a/spec/controllers/projects/pipelines/tests_controller_spec.rb b/spec/controllers/projects/pipelines/tests_controller_spec.rb index 113781bab7c..2db54dbe671 100644 --- a/spec/controllers/projects/pipelines/tests_controller_spec.rb +++ b/spec/controllers/projects/pipelines/tests_controller_spec.rb @@ -51,18 +51,6 @@ RSpec.describe Projects::Pipelines::TestsController do expect(response).to have_gitlab_http_status(:not_found) expect(json_response['errors']).to eq('Test report artifacts have expired') end - - context 'when ci_test_report_artifacts_expired is disabled' do - before do - stub_feature_flags(ci_test_report_artifacts_expired: false) - end - it 'renders test suite', :aggregate_failures do - get_tests_show_json(build_ids) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['name']).to eq('test') - end - end end context 'when artifacts are not expired' do diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 1be4177acd1..b3b803649d1 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -1289,6 +1289,18 @@ RSpec.describe Projects::PipelinesController do expect(response).to have_gitlab_http_status(:not_found) end end + + context 'and project is undergoing stats refresh' do + it_behaves_like 'preventing request because of ongoing project stats refresh' do + let(:make_request) { delete_pipeline } + + it 'does not delete the pipeline' do + make_request + + expect(Ci::Pipeline.exists?(pipeline.id)).to be_truthy + end + end + end end context 'when user has no privileges' do diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 20a114bbe8c..9bb34a38005 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -170,6 +170,46 @@ RSpec.describe Projects::ProjectMembersController do expect(requester.reload.human_access).to eq(label) end end + + describe 'managing project direct owners' do + context 'when a Maintainer tries to elevate another user to OWNER' do + it 'does not allow the operation' do + params = { + project_member: { access_level: Gitlab::Access::OWNER }, + namespace_id: project.namespace, + project_id: project, + id: requester + } + + put :update, params: params, xhr: true + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'when a user with OWNER access tries to elevate another user to OWNER' do + # inherited owner role via personal project association + let(:user) { project.first_owner } + + before do + sign_in(user) + end + + it 'returns success' do + params = { + project_member: { access_level: Gitlab::Access::OWNER }, + namespace_id: project.namespace, + project_id: project, + id: requester + } + + put :update, params: params, xhr: true + + expect(response).to have_gitlab_http_status(:ok) + expect(requester.reload.access_level).to eq(Gitlab::Access::OWNER) + end + end + end end context 'access expiry date' do @@ -275,19 +315,40 @@ RSpec.describe Projects::ProjectMembersController do context 'when member is found' do context 'when user does not have enough rights' do - before do - project.add_developer(user) + context 'when user does not have rights to manage other members' do + before do + project.add_developer(user) + end + + it 'returns 404', :aggregate_failures do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to have_gitlab_http_status(:not_found) + expect(project.members).to include member + end end - it 'returns 404', :aggregate_failures do - delete :destroy, params: { - namespace_id: project.namespace, - project_id: project, - id: member - } + context 'when user does not have rights to manage Owner members' do + let_it_be(:member) { create(:project_member, project: project, access_level: Gitlab::Access::OWNER) } - expect(response).to have_gitlab_http_status(:not_found) - expect(project.members).to include member + before do + project.add_maintainer(user) + end + + it 'returns 403', :aggregate_failures do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to have_gitlab_http_status(:forbidden) + expect(project.members).to include member + end end end @@ -434,7 +495,7 @@ RSpec.describe Projects::ProjectMembersController do end context 'when member is found' do - context 'when user does not have enough rights' do + context 'when user does not have rights to manage other members' do before do project.add_developer(user) end diff --git a/spec/controllers/projects/prometheus/alerts_controller_spec.rb b/spec/controllers/projects/prometheus/alerts_controller_spec.rb index f42119e7811..2c2c8180143 100644 --- a/spec/controllers/projects/prometheus/alerts_controller_spec.rb +++ b/spec/controllers/projects/prometheus/alerts_controller_spec.rb @@ -53,112 +53,6 @@ RSpec.describe Projects::Prometheus::AlertsController do end end - describe 'GET #index' do - def make_request(opts = {}) - get :index, params: request_params(opts, environment_id: environment) - end - - context 'when project has no prometheus alert' do - it 'returns an empty response' do - make_request - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_empty - end - end - - context 'when project has prometheus alerts' do - let(:production) { create(:environment, project: project) } - let(:staging) { create(:environment, project: project) } - let(:json_alert_ids) { json_response.map { |alert| alert['id'] } } - - let!(:production_alerts) do - create_list(:prometheus_alert, 2, project: project, environment: production) - end - - let!(:staging_alerts) do - create_list(:prometheus_alert, 1, project: project, environment: staging) - end - - it 'contains prometheus alerts only for the production environment' do - make_request(environment_id: production) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.count).to eq(2) - expect(json_alert_ids).to eq(production_alerts.map(&:id)) - end - - it 'contains prometheus alerts only for the staging environment' do - make_request(environment_id: staging) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.count).to eq(1) - expect(json_alert_ids).to eq(staging_alerts.map(&:id)) - end - - it 'does not return prometheus alerts without environment' do - make_request(environment_id: nil) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_empty - end - end - - it_behaves_like 'unprivileged' - it_behaves_like 'project non-specific environment', :ok - end - - describe 'GET #show' do - let(:alert) do - create(:prometheus_alert, - :with_runbook_url, - project: project, - environment: environment, - prometheus_metric: metric) - end - - def make_request(opts = {}) - get :show, params: request_params( - opts, - id: alert.prometheus_metric_id, - environment_id: environment - ) - end - - context 'when alert does not exist' do - it 'returns not_found' do - make_request(id: 0) - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when alert exists' do - let(:alert_params) do - { - 'id' => alert.id, - 'title' => alert.title, - 'query' => alert.query, - 'operator' => alert.computed_operator, - 'threshold' => alert.threshold, - 'runbook_url' => alert.runbook_url, - 'alert_path' => alert_path(alert) - } - end - - it 'renders the alert' do - make_request - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to include(alert_params) - end - - it_behaves_like 'unprivileged' - it_behaves_like 'project non-specific environment', :not_found - it_behaves_like 'project non-specific metric', :not_found - end - end - describe 'POST #notify' do let(:alert_1) { build(:alert_management_alert, :prometheus, project: project) } let(:alert_2) { build(:alert_management_alert, :prometheus, project: project) } diff --git a/spec/controllers/projects/prometheus/metrics_controller_spec.rb b/spec/controllers/projects/prometheus/metrics_controller_spec.rb index 7dfa283195e..cd195b95100 100644 --- a/spec/controllers/projects/prometheus/metrics_controller_spec.rb +++ b/spec/controllers/projects/prometheus/metrics_controller_spec.rb @@ -141,7 +141,7 @@ RSpec.describe Projects::Prometheus::MetricsController do expect(flash[:notice]).to include('Metric was successfully added.') - expect(response).to redirect_to(edit_project_integration_path(project, ::Integrations::Prometheus)) + expect(response).to redirect_to(edit_project_settings_integration_path(project, ::Integrations::Prometheus)) end end @@ -168,7 +168,7 @@ RSpec.describe Projects::Prometheus::MetricsController do expect(metric.reload.title).to eq('new_title') expect(flash[:notice]).to include('Metric was successfully updated.') - expect(response).to redirect_to(edit_project_integration_path(project, ::Integrations::Prometheus)) + expect(response).to redirect_to(edit_project_settings_integration_path(project, ::Integrations::Prometheus)) end end end @@ -180,7 +180,7 @@ RSpec.describe Projects::Prometheus::MetricsController do it 'destroys the metric' do delete :destroy, params: project_params(id: metric.id) - expect(response).to redirect_to(edit_project_integration_path(project, ::Integrations::Prometheus)) + expect(response).to redirect_to(edit_project_settings_integration_path(project, ::Integrations::Prometheus)) expect(PrometheusMetric.find_by(id: metric.id)).to be_nil end end diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb index 0dba7dab643..ad6682601f3 100644 --- a/spec/controllers/projects/releases_controller_spec.rb +++ b/spec/controllers/projects/releases_controller_spec.rb @@ -115,15 +115,6 @@ RSpec.describe Projects::ReleasesController do expect(json_response.map { |release| release["id"] } ).to eq([release_2.id, release_1.id]) end - # TODO: remove in https://gitlab.com/gitlab-org/gitlab/-/issues/360903 - it "returns release sha when remove_sha_from_releases_json is disabled" do - stub_feature_flags(remove_sha_from_releases_json: false) - - get_index - - expect(json_response).to eq([release_2, release_1].as_json) - end - it_behaves_like 'common access controls' context 'when the project is private and the user is not logged in' do @@ -157,19 +148,19 @@ RSpec.describe Projects::ReleasesController do end let(:release) { create(:release, project: project) } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } it_behaves_like 'successful request' context 'when tag name contains slash' do let(:release) { create(:release, project: project, tag: 'awesome/v1.0') } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } it_behaves_like 'successful request' it 'is accesible at a URL encoded path' do expect(edit_project_release_path(project, release)) - .to eq("/#{project.namespace.path}/#{project.name}/-/releases/awesome%252Fv1.0/edit") + .to eq("/#{project.namespace.path}/#{project.name}/-/releases/awesome%2Fv1.0/edit") end end @@ -196,19 +187,19 @@ RSpec.describe Projects::ReleasesController do end let(:release) { create(:release, project: project) } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } it_behaves_like 'successful request' context 'when tag name contains slash' do let(:release) { create(:release, project: project, tag: 'awesome/v1.0') } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } it_behaves_like 'successful request' it 'is accesible at a URL encoded path' do expect(project_release_path(project, release)) - .to eq("/#{project.namespace.path}/#{project.name}/-/releases/awesome%252Fv1.0") + .to eq("/#{project.namespace.path}/#{project.name}/-/releases/awesome%2Fv1.0") end end @@ -248,7 +239,7 @@ RSpec.describe Projects::ReleasesController do end let(:release) { create(:release, project: project) } - let(:tag) { CGI.escape(release.tag) } + let(:tag) { release.tag } context 'when user is a guest' do let(:project) { private_project } diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb deleted file mode 100644 index 6802ebeb63e..00000000000 --- a/spec/controllers/projects/services_controller_spec.rb +++ /dev/null @@ -1,356 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::ServicesController do - include JiraServiceHelper - include AfterNextHelpers - - let_it_be(:project) { create(:project, :repository) } - let_it_be(:user) { create(:user) } - let_it_be(:jira_integration) { create(:jira_integration, project: project) } - - let(:integration) { jira_integration } - let(:integration_params) { { username: 'username', password: 'password', url: 'http://example.com' } } - - before do - sign_in(user) - project.add_maintainer(user) - end - - it_behaves_like Integrations::Actions do - let(:integration_attributes) { { project: project } } - - let(:routing_params) do - { - namespace_id: project.namespace, - project_id: project, - id: integration.to_param - } - end - end - - describe '#test' do - context 'when the integration is not testable' do - it 'renders 404' do - allow_any_instance_of(Integration).to receive(:testable?).and_return(false) - - put :test, params: project_params - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when validations fail' do - let(:integration_params) { { active: 'true', url: '' } } - - it 'returns error messages in JSON response' do - put :test, params: project_params(service: integration_params) - - expect(json_response['message']).to eq 'Validations failed.' - expect(json_response['service_response']).to include "Url can't be blank" - expect(response).to be_successful - end - end - - context 'when successful' do - context 'with empty project' do - let_it_be(:project) { create(:project) } - - context 'with chat notification integration' do - let_it_be(:teams_integration) { project.create_microsoft_teams_integration(webhook: 'http://webhook.com') } - - let(:integration) { teams_integration } - - it 'returns success' do - allow_next(::MicrosoftTeams::Notifier).to receive(:ping).and_return(true) - - put :test, params: project_params - - expect(response).to be_successful - end - end - - it 'returns success' do - stub_jira_integration_test - - expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original - - put :test, params: project_params(service: integration_params) - - expect(response).to be_successful - end - end - - it 'returns success' do - stub_jira_integration_test - - expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original - - put :test, params: project_params(service: integration_params) - - expect(response).to be_successful - end - - context 'when service is configured for the first time' do - let(:integration_params) do - { - 'active' => '1', - 'push_events' => '1', - 'token' => 'token', - 'project_url' => 'https://buildkite.com/organization/pipeline' - } - end - - before do - allow_any_instance_of(ServiceHook).to receive(:execute).and_return(true) - end - - it 'persist the object' do - do_put - - expect(response).to be_successful - expect(json_response).to be_empty - expect(Integrations::Buildkite.first).to be_present - end - - it 'creates the ServiceHook object' do - do_put - - expect(response).to be_successful - expect(json_response).to be_empty - expect(Integrations::Buildkite.first.service_hook).to be_present - end - - def do_put - put :test, params: project_params(id: 'buildkite', - service: integration_params) - end - end - end - - context 'when unsuccessful' do - it 'returns an error response when the integration test fails' do - stub_request(:get, 'http://example.com/rest/api/2/serverInfo') - .to_return(status: 404) - - put :test, params: project_params(service: integration_params) - - expect(response).to be_successful - expect(json_response).to eq( - 'error' => true, - 'message' => 'Connection failed. Please check your settings.', - 'service_response' => '', - 'test_failed' => true - ) - end - - context 'with the Slack integration' do - let_it_be(:integration) { build(:integrations_slack) } - - it 'returns an error response when the URL is blocked' do - put :test, params: project_params(service: { webhook: 'http://127.0.0.1' }) - - expect(response).to be_successful - expect(json_response).to eq( - 'error' => true, - 'message' => 'Connection failed. Please check your settings.', - 'service_response' => "URL 'http://127.0.0.1' is blocked: Requests to localhost are not allowed", - 'test_failed' => true - ) - end - - it 'returns an error response when a network exception is raised' do - expect_next(Integrations::Slack).to receive(:test).and_raise(Errno::ECONNREFUSED) - - put :test, params: project_params - - expect(response).to be_successful - expect(json_response).to eq( - 'error' => true, - 'message' => 'Connection failed. Please check your settings.', - 'service_response' => 'Connection refused', - 'test_failed' => true - ) - end - end - end - end - - describe 'PUT #update' do - describe 'as HTML' do - let(:integration_params) { { active: true } } - let(:params) { project_params(service: integration_params) } - - let(:message) { 'Jira settings saved and active.' } - let(:redirect_url) { edit_project_integration_path(project, integration) } - - before do - stub_jira_integration_test - - put :update, params: params - end - - shared_examples 'integration update' do - it 'redirects to the correct url with a flash message' do - expect(response).to redirect_to(redirect_url) - expect(flash[:notice]).to eq(message) - end - end - - context 'when param `active` is set to true' do - let(:params) { project_params(service: integration_params, redirect_to: redirect) } - - context 'when redirect_to param is present' do - let(:redirect) { '/redirect_here' } - let(:redirect_url) { redirect } - - it_behaves_like 'integration update' - end - - context 'when redirect_to is an external domain' do - let(:redirect) { 'http://examle.com' } - - it_behaves_like 'integration update' - end - - context 'when redirect_to param is an empty string' do - let(:redirect) { '' } - - it_behaves_like 'integration update' - end - end - - context 'when param `active` is set to false' do - let(:integration_params) { { active: false } } - let(:message) { 'Jira settings saved, but not active.' } - - it_behaves_like 'integration update' - end - - context 'when param `inherit_from_id` is set to empty string' do - let(:integration_params) { { inherit_from_id: '' } } - - it 'sets inherit_from_id to nil' do - expect(integration.reload.inherit_from_id).to eq(nil) - end - end - - context 'when param `inherit_from_id` is set to an instance integration' do - let(:instance_integration) { create(:jira_integration, :instance, url: 'http://instance.com', password: 'instance') } - let(:integration_params) { { inherit_from_id: instance_integration.id, url: 'http://custom.com', password: 'custom' } } - - it 'ignores submitted params and inherits instance settings' do - expect(integration.reload).to have_attributes( - inherit_from_id: instance_integration.id, - url: instance_integration.url, - password: instance_integration.password - ) - end - end - - context 'when param `inherit_from_id` is set to a group integration' do - let_it_be(:group) { create(:group) } - let_it_be(:project) { create(:project, group: group) } - let_it_be(:jira_integration) { create(:jira_integration, project: project) } - - let(:group_integration) { create(:jira_integration, :group, group: group, url: 'http://group.com', password: 'group') } - let(:integration_params) { { inherit_from_id: group_integration.id, url: 'http://custom.com', password: 'custom' } } - - it 'ignores submitted params and inherits group settings' do - expect(integration.reload).to have_attributes( - inherit_from_id: group_integration.id, - url: group_integration.url, - password: group_integration.password - ) - end - end - - context 'when param `inherit_from_id` is set to an unrelated group' do - let_it_be(:group) { create(:group) } - - let(:group_integration) { create(:jira_integration, :group, group: group, url: 'http://group.com', password: 'group') } - let(:integration_params) { { inherit_from_id: group_integration.id, url: 'http://custom.com', password: 'custom' } } - - it 'ignores the param and saves the submitted settings' do - expect(integration.reload).to have_attributes( - inherit_from_id: nil, - url: 'http://custom.com', - password: 'custom' - ) - end - end - end - - describe 'as JSON' do - before do - stub_jira_integration_test - put :update, params: project_params(service: integration_params, format: :json) - end - - context 'when update succeeds' do - let(:integration_params) { { url: 'http://example.com', password: 'password' } } - - it 'returns success response' do - expect(response).to be_successful - expect(json_response).to include( - 'active' => true, - 'errors' => {} - ) - end - end - - context 'when update fails with missing password' do - let(:integration_params) { { url: 'http://example.com' } } - - it 'returns JSON response errors' do - expect(response).not_to be_successful - expect(json_response).to include( - 'active' => true, - 'errors' => { - 'password' => ["can't be blank"] - } - ) - end - end - - context 'when update fails with invalid URL' do - let(:integration_params) { { url: '', password: 'password' } } - - it 'returns JSON response with errors' do - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(json_response).to include( - 'active' => true, - 'errors' => { 'url' => ['must be a valid URL', "can't be blank"] } - ) - end - end - end - end - - describe 'GET #edit' do - context 'with Jira service' do - let(:integration_param) { 'jira' } - - before do - get :edit, params: project_params(id: integration_param) - end - - context 'with approved services' do - it 'renders edit page' do - expect(response).to be_successful - end - end - end - end - - private - - def project_params(opts = {}) - opts.reverse_merge( - namespace_id: project.namespace, - project_id: project, - id: integration.to_param - ) - end -end diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb index 7e96e99640a..d50f1aa1dd8 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -25,19 +25,6 @@ RSpec.describe Projects::Settings::CiCdController do expect(response).to render_template(:show) end - context 'when the FF ci_owned_runners_cross_joins_fix is disabled' do - before do - stub_feature_flags(ci_owned_runners_cross_joins_fix: false) - end - - it 'renders show with 200 status code' do - get :show, params: { namespace_id: project.namespace, project_id: project } - - expect(response).to have_gitlab_http_status(:ok) - expect(response).to render_template(:show) - end - end - context 'with CI/CD disabled' do before do project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED) diff --git a/spec/controllers/projects/service_hook_logs_controller_spec.rb b/spec/controllers/projects/settings/integration_hook_logs_controller_spec.rb index be78668aa88..8261461e8aa 100644 --- a/spec/controllers/projects/service_hook_logs_controller_spec.rb +++ b/spec/controllers/projects/settings/integration_hook_logs_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::ServiceHookLogsController do +RSpec.describe Projects::Settings::IntegrationHookLogsController do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:integration) { create(:drone_ci_integration, project: project) } @@ -44,7 +44,8 @@ RSpec.describe Projects::ServiceHookLogsController do it 'executes the hook and redirects to the service form' do expect_any_instance_of(ServiceHook).to receive(:execute) expect_any_instance_of(described_class).to receive(:set_hook_execution_notice) - expect(subject).to redirect_to(edit_project_integration_path(project, integration)) + + expect(subject).to redirect_to(edit_project_settings_integration_path(project, integration)) end it 'renders a 404 if the hook does not exist' do diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb index 0652786c787..e6ca088a533 100644 --- a/spec/controllers/projects/settings/integrations_controller_spec.rb +++ b/spec/controllers/projects/settings/integrations_controller_spec.rb @@ -3,20 +3,388 @@ require 'spec_helper' RSpec.describe Projects::Settings::IntegrationsController do - let(:project) { create(:project, :public) } - let(:user) { create(:user) } + include JiraIntegrationHelpers + include AfterNextHelpers + + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + let_it_be(:jira_integration) { create(:jira_integration, project: project) } + + let(:integration) { jira_integration } + let(:integration_params) { { username: 'username', password: 'password', url: 'http://example.com' } } before do - project.add_maintainer(user) sign_in(user) + project.add_maintainer(user) + end + + it_behaves_like Integrations::Actions do + let(:integration_attributes) { { project: project } } + + let(:routing_params) do + { + namespace_id: project.namespace, + project_id: project, + id: integration.to_param + } + end end - describe 'GET show' do - it 'renders show with 200 status code' do - get :show, params: { namespace_id: project.namespace, project_id: project } + describe 'GET index' do + it 'renders index with 200 status code' do + get :index, params: { namespace_id: project.namespace, project_id: project } expect(response).to have_gitlab_http_status(:ok) - expect(response).to render_template(:show) + expect(response).to render_template(:index) + end + end + + describe '#test' do + context 'when the integration is not testable' do + it 'renders 404' do + allow_any_instance_of(Integration).to receive(:testable?).and_return(false) + + put :test, params: project_params + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when validations fail' do + let(:integration_params) { { active: 'true', url: '' } } + + it 'returns error messages in JSON response' do + put :test, params: project_params(service: integration_params) + + expect(json_response['message']).to eq 'Validations failed.' + expect(json_response['service_response']).to include "Url can't be blank" + expect(response).to be_successful + end + end + + context 'when successful' do + context 'with empty project' do + let_it_be(:project) { create(:project) } + + context 'with chat notification integration' do + let_it_be(:teams_integration) { project.create_microsoft_teams_integration(webhook: 'http://webhook.com') } + + let(:integration) { teams_integration } + + it 'returns success' do + allow_next(::MicrosoftTeams::Notifier).to receive(:ping).and_return(true) + + put :test, params: project_params + + expect(response).to be_successful + end + end + + it 'returns success' do + stub_jira_integration_test + + expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original + + put :test, params: project_params(service: integration_params) + + expect(response).to be_successful + end + end + + it 'returns success' do + stub_jira_integration_test + + expect(Gitlab::HTTP).to receive(:get).with('/rest/api/2/serverInfo', any_args).and_call_original + + put :test, params: project_params(service: integration_params) + + expect(response).to be_successful + end + + context 'when service is configured for the first time' do + let(:integration_params) do + { + 'active' => '1', + 'push_events' => '1', + 'token' => 'token', + 'project_url' => 'https://buildkite.com/organization/pipeline' + } + end + + before do + allow_next(ServiceHook).to receive(:execute).and_return(true) + end + + it 'persist the object' do + do_put + + expect(response).to be_successful + expect(json_response).to be_empty + expect(Integrations::Buildkite.first).to be_present + end + + it 'creates the ServiceHook object' do + do_put + + expect(response).to be_successful + expect(json_response).to be_empty + expect(Integrations::Buildkite.first.service_hook).to be_present + end + + def do_put + put :test, params: project_params(id: 'buildkite', + service: integration_params) + end + end + end + + context 'when unsuccessful' do + it 'returns an error response when the integration test fails' do + stub_request(:get, 'http://example.com/rest/api/2/serverInfo') + .to_return(status: 404) + + put :test, params: project_params(service: integration_params) + + expect(response).to be_successful + expect(json_response).to eq( + 'error' => true, + 'message' => 'Connection failed. Please check your settings.', + 'service_response' => '', + 'test_failed' => true + ) + end + + context 'with the Slack integration' do + let_it_be(:integration) { build(:integrations_slack) } + + it 'returns an error response when the URL is blocked' do + put :test, params: project_params(service: { webhook: 'http://127.0.0.1' }) + + expect(response).to be_successful + expect(json_response).to eq( + 'error' => true, + 'message' => 'Connection failed. Please check your settings.', + 'service_response' => "URL 'http://127.0.0.1' is blocked: Requests to localhost are not allowed", + 'test_failed' => true + ) + end + + it 'returns an error response when a network exception is raised' do + expect_next(Integrations::Slack).to receive(:test).and_raise(Errno::ECONNREFUSED) + + put :test, params: project_params + + expect(response).to be_successful + expect(json_response).to eq( + 'error' => true, + 'message' => 'Connection failed. Please check your settings.', + 'service_response' => 'Connection refused', + 'test_failed' => true + ) + end + end + end + end + + describe 'PUT #update' do + describe 'as HTML' do + let(:integration_params) { { active: true } } + let(:params) { project_params(service: integration_params) } + + let(:message) { 'Jira settings saved and active.' } + let(:redirect_url) { edit_project_settings_integration_path(project, integration) } + + before do + stub_jira_integration_test + + put :update, params: params + end + + shared_examples 'integration update' do + it 'redirects to the correct url with a flash message' do + expect(response).to redirect_to(redirect_url) + expect(flash[:notice]).to eq(message) + end + end + + context 'when update fails' do + let(:integration_params) { { url: 'https://new.com', password: '' } } + + it 'renders the edit form' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:edit) + expect(integration.reload.url).not_to eq('https://new.com') + end + end + + context 'when param `active` is set to true' do + let(:params) { project_params(service: integration_params, redirect_to: redirect) } + + context 'when redirect_to param is present' do + let(:redirect) { '/redirect_here' } + let(:redirect_url) { redirect } + + it_behaves_like 'integration update' + end + + context 'when redirect_to is an external domain' do + let(:redirect) { 'http://examle.com' } + + it_behaves_like 'integration update' + end + + context 'when redirect_to param is an empty string' do + let(:redirect) { '' } + + it_behaves_like 'integration update' + end + end + + context 'when param `active` is set to false' do + let(:integration_params) { { active: false } } + let(:message) { 'Jira settings saved, but not active.' } + + it_behaves_like 'integration update' + end + + context 'when param `inherit_from_id` is set to empty string' do + let(:integration_params) { { inherit_from_id: '' } } + + it 'sets inherit_from_id to nil' do + expect(integration.reload.inherit_from_id).to eq(nil) + end + end + + context 'when param `inherit_from_id` is set to an instance integration' do + let(:instance_integration) do + create(:jira_integration, :instance, url: 'http://instance.com', password: 'instance') + end + + let(:integration_params) do + { inherit_from_id: instance_integration.id, url: 'http://custom.com', password: 'custom' } + end + + it 'ignores submitted params and inherits instance settings' do + expect(integration.reload).to have_attributes( + inherit_from_id: instance_integration.id, + url: instance_integration.url, + password: instance_integration.password + ) + end + end + + context 'when param `inherit_from_id` is set to a group integration' do + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:jira_integration) { create(:jira_integration, project: project) } + + let(:group_integration) do + create(:jira_integration, :group, group: group, url: 'http://group.com', password: 'group') + end + + let(:integration_params) do + { inherit_from_id: group_integration.id, url: 'http://custom.com', password: 'custom' } + end + + it 'ignores submitted params and inherits group settings' do + expect(integration.reload).to have_attributes( + inherit_from_id: group_integration.id, + url: group_integration.url, + password: group_integration.password + ) + end + end + + context 'when param `inherit_from_id` is set to an unrelated group' do + let_it_be(:group) { create(:group) } + + let(:group_integration) do + create(:jira_integration, :group, group: group, url: 'http://group.com', password: 'group') + end + + let(:integration_params) do + { inherit_from_id: group_integration.id, url: 'http://custom.com', password: 'custom' } + end + + it 'ignores the param and saves the submitted settings' do + expect(integration.reload).to have_attributes( + inherit_from_id: nil, + url: 'http://custom.com', + password: 'custom' + ) + end + end + end + + describe 'as JSON' do + before do + stub_jira_integration_test + put :update, params: project_params(service: integration_params, format: :json) + end + + context 'when update succeeds' do + let(:integration_params) { { url: 'http://example.com', password: 'password' } } + + it 'returns success response' do + expect(response).to be_successful + expect(json_response).to include( + 'active' => true, + 'errors' => {} + ) + end + end + + context 'when update fails with missing password' do + let(:integration_params) { { url: 'http://example.com' } } + + it 'returns JSON response errors' do + expect(response).not_to be_successful + expect(json_response).to include( + 'active' => true, + 'errors' => { + 'password' => ["can't be blank"] + } + ) + end + end + + context 'when update fails with invalid URL' do + let(:integration_params) { { url: '', password: 'password' } } + + it 'returns JSON response with errors' do + expect(response).to have_gitlab_http_status(:unprocessable_entity) + expect(json_response).to include( + 'active' => true, + 'errors' => { 'url' => ['must be a valid URL', "can't be blank"] } + ) + end + end + end + end + + describe 'GET #edit' do + context 'with Jira service' do + let(:integration_param) { 'jira' } + + before do + get :edit, params: project_params(id: integration_param) + end + + context 'with approved services' do + it 'renders edit page' do + expect(response).to be_successful + end + end end end + + private + + def project_params(opts = {}) + opts.reverse_merge( + namespace_id: project.namespace, + project_id: project, + id: integration.to_param + ) + end end diff --git a/spec/controllers/projects/static_site_editor_controller_spec.rb b/spec/controllers/projects/static_site_editor_controller_spec.rb deleted file mode 100644 index e1f25589eeb..00000000000 --- a/spec/controllers/projects/static_site_editor_controller_spec.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::StaticSiteEditorController do - let_it_be(:project) { create(:project, :public, :repository) } - let_it_be(:user) { create(:user) } - - let(:data) { { key: 'value' } } - - describe 'GET index' do - let(:default_params) do - { - namespace_id: project.namespace, - project_id: project - } - end - - it 'responds with 404 page' do - get :index, params: default_params - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - describe 'GET show' do - render_views - - let(:default_params) do - { - namespace_id: project.namespace, - project_id: project, - id: 'master/README.md', - return_url: 'http://example.com' - } - end - - let(:service_response) do - ServiceResponse.success(payload: data) - end - - before do - allow_next_instance_of(::StaticSiteEditor::ConfigService) do |instance| - allow(instance).to receive(:execute).and_return(service_response) - end - end - - context 'User roles' do - context 'anonymous' do - before do - get :show, params: default_params - end - - it 'redirects to sign in and returns' do - expect(response).to redirect_to(new_user_session_path) - end - end - - context 'as guest' do - before do - project.add_guest(user) - sign_in(user) - get :show, params: default_params - end - - it 'responds with 404 page' do - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context "as developer" do - before do - allow(Gitlab::UsageDataCounters::StaticSiteEditorCounter).to receive(:increment_views_count) - project.add_role(user, 'developer') - sign_in(user) - get :show, params: default_params - end - - it 'redirects to the Web IDE' do - get :show, params: default_params - - expected_path_regex = %r[-/ide/project/#{project.full_path}/edit/master/-/README.md] - expect(response).to redirect_to(expected_path_regex) - end - - it 'assigns ref and path variables' do - expect(assigns(:ref)).to eq('master') - expect(assigns(:path)).to eq('README.md') - end - - context 'when combination of ref and path is incorrect' do - let(:default_params) { super().merge(id: 'unknown') } - - it 'responds with 404 page' do - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - end - end -end diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index d0971e96910..3d1f8c12022 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -205,15 +205,13 @@ RSpec.describe Projects::TagsController do before do project.add_developer(user) sign_in(user) - end - - it 'deletes tag' do request + end - expect(response).to be_successful - expect(response.body).to include("Tag was removed") - + it 'deletes tag and redirects to tags path' do expect(project.repository.find_tag(tag.name)).not_to be_present + expect(controller).to set_flash[:notice].to(/Tag was removed/) + expect(response).to redirect_to(project_tags_path(project)) end end end diff --git a/spec/controllers/registrations/welcome_controller_spec.rb b/spec/controllers/registrations/welcome_controller_spec.rb index c444875bf74..8a5a8490a23 100644 --- a/spec/controllers/registrations/welcome_controller_spec.rb +++ b/spec/controllers/registrations/welcome_controller_spec.rb @@ -31,6 +31,7 @@ RSpec.describe Registrations::WelcomeController do context 'when role and setup_for_company is set' do before do + stub_feature_flags(about_your_company_registration_flow: false) user.update!(setup_for_company: false) sign_in(user) end @@ -60,6 +61,10 @@ RSpec.describe Registrations::WelcomeController do end describe '#update' do + before do + stub_feature_flags(about_your_company_registration_flow: false) + end + subject(:update) do patch :update, params: { user: { role: 'software_developer', setup_for_company: 'false' } } end diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index caff7bcfc7b..36b230103db 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -292,13 +292,26 @@ RSpec.describe RegistrationsController do end end - it 'displays an error when the reCAPTCHA is not solved' do - allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) + context 'when the reCAPTCHA is not solved' do + before do + allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) + end - subject + it 'displays an error' do + subject + + expect(response).to render_template(:new) + expect(flash[:alert]).to eq(_('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')) + end + + it 'sets gon variables' do + Gon.clear - expect(response).to render_template(:new) - expect(flash[:alert]).to eq(_('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')) + subject + + expect(response).to render_template(:new) + expect(Gon.all_variables).not_to be_empty + end end it 'redirects to the welcome page when the reCAPTCHA is solved' do diff --git a/spec/controllers/repositories/git_http_controller_spec.rb b/spec/controllers/repositories/git_http_controller_spec.rb index fb2637238ec..448587c937a 100644 --- a/spec/controllers/repositories/git_http_controller_spec.rb +++ b/spec/controllers/repositories/git_http_controller_spec.rb @@ -43,18 +43,6 @@ RSpec.describe Repositories::GitHttpController do post :git_upload_pack, params: params end - context 'on a read-only instance' do - before do - allow(Gitlab::Database).to receive(:read_only?).and_return(true) - end - - it 'does not update project statistics' do - expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async) - - send_request - end - end - it 'updates project statistics sync for projects' do stub_feature_flags(disable_git_http_fetch_writes: false) @@ -83,7 +71,6 @@ RSpec.describe Repositories::GitHttpController do it 'does not increment statistics' do expect(Projects::FetchStatisticsIncrementService).not_to receive(:new) - expect(ProjectDailyStatisticsWorker).not_to receive(:perform_async) send_request end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 877ca7cd6c6..0e0770fb94c 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -233,14 +233,23 @@ RSpec.describe SessionsController do request.headers[described_class::CAPTCHA_HEADER] = '1' end - it 'displays an error when the reCAPTCHA is not solved' do - # Without this, `verify_recaptcha` arbitrarily returns true in test env + context 'when the reCAPTCHA is not solved' do + it 'displays an error' do + unsuccesful_login(user_params) - unsuccesful_login(user_params) + expect(response).to render_template(:new) + expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') + expect(subject.current_user).to be_nil + end - expect(response).to redirect_to new_user_session_path - expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') - expect(subject.current_user).to be_nil + it 'sets gon variables' do + Gon.clear + + unsuccesful_login(user_params) + + expect(response).to render_template(:new) + expect(Gon.all_variables).not_to be_empty + end end it 'successfully logs in a user when reCAPTCHA is solved' do @@ -262,7 +271,7 @@ RSpec.describe SessionsController do it 'displays an error when the reCAPTCHA is not solved' do unsuccesful_login(user_params, sesion_params: { failed_login_attempts: 6 }) - expect(response).to redirect_to new_user_session_path + expect(response).to render_template(:new) expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') expect(subject.current_user).to be_nil end @@ -282,7 +291,7 @@ RSpec.describe SessionsController do it 'displays an error when the reCAPTCHA is not solved' do unsuccesful_login(user_params) - expect(response).to redirect_to new_user_session_path + expect(response).to render_template(:new) expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') expect(subject.current_user).to be_nil end |