diff options
Diffstat (limited to 'spec/controllers')
36 files changed, 800 insertions, 727 deletions
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb index 4a92911f914..e02589ddc83 100644 --- a/spec/controllers/admin/application_settings_controller_spec.rb +++ b/spec/controllers/admin/application_settings_controller_spec.rb @@ -382,6 +382,24 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set end end + describe 'PUT #reset_error_tracking_access_token' do + before do + sign_in(admin) + end + + subject { put :reset_error_tracking_access_token } + + it 'resets error_tracking_access_token' do + expect { subject }.to change { ApplicationSetting.current.error_tracking_access_token } + end + + it 'redirects the user to application settings page' do + subject + + expect(response).to redirect_to(general_admin_application_settings_path) + end + end + describe 'GET #lets_encrypt_terms_of_service' do include LetsEncryptHelpers diff --git a/spec/controllers/admin/hooks_controller_spec.rb b/spec/controllers/admin/hooks_controller_spec.rb index 17c4222530d..14f4a2f40e7 100644 --- a/spec/controllers/admin/hooks_controller_spec.rb +++ b/spec/controllers/admin/hooks_controller_spec.rb @@ -17,16 +17,46 @@ RSpec.describe Admin::HooksController do url: "http://example.com", push_events: true, - tag_push_events: true, + tag_push_events: false, repository_update_events: true, - merge_requests_events: true + merge_requests_events: false, + url_variables: [{ key: 'token', value: 'some secret value' }] } post :create, params: { hook: hook_params } expect(response).to have_gitlab_http_status(:found) expect(SystemHook.all.size).to eq(1) - expect(SystemHook.first).to have_attributes(hook_params) + expect(SystemHook.first).to have_attributes(hook_params.except(:url_variables)) + expect(SystemHook.first).to have_attributes(url_variables: { 'token' => 'some secret value' }) + end + end + + describe 'POST #update' do + let!(:hook) { create(:system_hook) } + + it 'sets all parameters' do + hook.update!(url_variables: { 'foo' => 'bar', 'baz' => 'woo' }) + + hook_params = { + url: 'http://example.com/{baz}?token={token}', + enable_ssl_verification: false, + url_variables: [ + { key: 'token', value: 'some secret value' }, + { key: 'foo', value: nil } + ] + } + + put :update, params: { id: hook.id, hook: hook_params } + + hook.reload + + expect(response).to have_gitlab_http_status(:found) + expect(flash[:notice]).to include('successfully updated') + expect(hook).to have_attributes(hook_params.except(:url_variables)) + expect(hook).to have_attributes( + url_variables: { 'token' => 'some secret value', 'baz' => 'woo' } + ) end end diff --git a/spec/controllers/admin/topics_controller_spec.rb b/spec/controllers/admin/topics_controller_spec.rb index 67943525687..ee36d5f1def 100644 --- a/spec/controllers/admin/topics_controller_spec.rb +++ b/spec/controllers/admin/topics_controller_spec.rb @@ -151,4 +151,26 @@ RSpec.describe Admin::TopicsController do end end end + + describe 'DELETE #destroy' do + it 'removes topic' do + delete :destroy, params: { id: topic.id } + + expect(response).to redirect_to(admin_topics_path) + expect { topic.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + context 'as a normal user' do + before do + sign_in(user) + end + + it 'renders a 404 error' do + delete :destroy, params: { id: topic.id } + + expect(response).to have_gitlab_http_status(:not_found) + expect { topic.reload }.not_to raise_error + end + end + end end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index c5306fda0a5..1e28ef4ba93 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -559,6 +559,28 @@ RSpec.describe ApplicationController do expect(controller.last_payload[:target_duration_s]).to eq(0.25) end end + + it 'logs response length' do + sign_in user + + get :index + + expect(controller.last_payload[:response_bytes]).to eq('authenticated'.bytesize) + end + + context 'with log_response_length disabled' do + before do + stub_feature_flags(log_response_length: false) + end + + it 'logs response length' do + sign_in user + + get :index + + expect(controller.last_payload).not_to include(:response_bytes) + end + end end describe '#access_denied' do diff --git a/spec/controllers/concerns/harbor/artifact_spec.rb b/spec/controllers/concerns/harbor/artifact_spec.rb new file mode 100644 index 00000000000..6716d615a3b --- /dev/null +++ b/spec/controllers/concerns/harbor/artifact_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Harbor::Artifact do + controller(ActionController::Base) do + include ::Harbor::Artifact + end + it_behaves_like 'raises NotImplementedError when calling #container' +end diff --git a/spec/controllers/concerns/harbor/repository_spec.rb b/spec/controllers/concerns/harbor/repository_spec.rb new file mode 100644 index 00000000000..cae038ceed2 --- /dev/null +++ b/spec/controllers/concerns/harbor/repository_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Harbor::Repository do + controller(ActionController::Base) do + include ::Harbor::Repository + end + it_behaves_like 'raises NotImplementedError when calling #container' +end diff --git a/spec/controllers/concerns/harbor/tag_spec.rb b/spec/controllers/concerns/harbor/tag_spec.rb new file mode 100644 index 00000000000..0d72ef303b0 --- /dev/null +++ b/spec/controllers/concerns/harbor/tag_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Harbor::Tag do + controller(ActionController::Base) do + include ::Harbor::Tag + end + it_behaves_like 'raises NotImplementedError when calling #container' +end diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index e85f5b7a972..1d2f1085d3c 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -27,6 +27,18 @@ RSpec.describe GraphqlController do ) end + it 'handles a timeout nicely' do + allow(subject).to receive(:execute) do + raise ActiveRecord::QueryCanceled, '**taps wristwatch**' + end + + post :execute + + expect(json_response).to include( + 'errors' => include(a_hash_including('message' => /Request timed out/)) + ) + end + it 'handles StandardError' do allow(subject).to receive(:execute) do raise StandardError, message diff --git a/spec/controllers/groups/group_links_controller_spec.rb b/spec/controllers/groups/group_links_controller_spec.rb index 28febd786de..7322ca5e522 100644 --- a/spec/controllers/groups/group_links_controller_spec.rb +++ b/spec/controllers/groups/group_links_controller_spec.rb @@ -131,8 +131,24 @@ RSpec.describe Groups::GroupLinksController do expect { subject }.to change(GroupGroupLink, :count).by(-1) end - it 'updates project permissions', :sidekiq_inline do - expect { subject }.to change { group_member.can?(:create_release, project) }.from(true).to(false) + context 'with skip_group_share_unlink_auth_refresh feature flag disabled' do + before do + stub_feature_flags(skip_group_share_unlink_auth_refresh: false) + end + + it 'updates project permissions', :sidekiq_inline do + expect { subject }.to change { group_member.can?(:create_release, project) }.from(true).to(false) + end + end + + context 'with skip_group_share_unlink_auth_refresh feature flag enabled' do + before do + stub_feature_flags(skip_group_share_unlink_auth_refresh: true) + end + + it 'maintains project authorization', :sidekiq_inline do + expect(Ability.allowed?(user, :read_project, project)).to be_truthy + end end end diff --git a/spec/controllers/groups/variables_controller_spec.rb b/spec/controllers/groups/variables_controller_spec.rb index 8c0aa83b9c4..6dbe75bb1df 100644 --- a/spec/controllers/groups/variables_controller_spec.rb +++ b/spec/controllers/groups/variables_controller_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Groups::VariablesController do before do sign_in(user) - group.add_user(user, access_level) + group.add_member(user, access_level) end describe 'GET #show' do diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index aabceda7187..c4e4eeec953 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -1112,9 +1112,11 @@ RSpec.describe GroupsController, factory_default: :keep do before do sign_in(admin) - allow(Gitlab::ApplicationRateLimiter) - .to receive(:increment) - .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:group_export][:threshold].call + 1) + allow_next_instance_of(Gitlab::ApplicationRateLimiter::BaseStrategy) do |strategy| + allow(strategy) + .to receive(:increment) + .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:group_export][:threshold].call + 1) + end end it 'throttles the endpoint' do @@ -1194,9 +1196,11 @@ RSpec.describe GroupsController, factory_default: :keep do before do sign_in(admin) - allow(Gitlab::ApplicationRateLimiter) + allow_next_instance_of(Gitlab::ApplicationRateLimiter::BaseStrategy) do |strategy| + allow(strategy) .to receive(:increment) .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:group_download_export][:threshold].call + 1) + end end it 'throttles the endpoint' do diff --git a/spec/controllers/import/available_namespaces_controller_spec.rb b/spec/controllers/import/available_namespaces_controller_spec.rb index 0f98d649338..26ea1d92189 100644 --- a/spec/controllers/import/available_namespaces_controller_spec.rb +++ b/spec/controllers/import/available_namespaces_controller_spec.rb @@ -25,7 +25,7 @@ RSpec.describe Import::AvailableNamespacesController do it "does not include group with access level #{params[:role]} in list" do group = create(:group, project_creation_level: group_project_creation_level) - group.add_user(user, role) + group.add_member(user, role) get :index expect(response).to have_gitlab_http_status(:ok) @@ -52,7 +52,7 @@ RSpec.describe Import::AvailableNamespacesController do it "does not include group with access level #{params[:role]} in list" do group = create(:group, project_creation_level: group_project_creation_level) - group.add_user(user, role) + group.add_member(user, role) get :index expect(response).to have_gitlab_http_status(:ok) @@ -81,7 +81,7 @@ RSpec.describe Import::AvailableNamespacesController do it "#{params[:is_visible] ? 'includes' : 'does not include'} group with access level #{params[:role]} in list" do group = create(:group, project_creation_level: project_creation_level) - group.add_user(user, :developer) + group.add_member(user, :developer) get :index diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb index 6d24830af27..af220e2d515 100644 --- a/spec/controllers/import/bitbucket_controller_spec.rb +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -45,24 +45,27 @@ RSpec.describe Import::BitbucketController do end context "when auth state param is valid" do + let(:expires_at) { Time.current + 1.day } + let(:expires_in) { 1.day } + let(:access_token) do + double(token: token, + secret: secret, + expires_at: expires_at, + expires_in: expires_in, + refresh_token: refresh_token) + end + before do session[:bitbucket_auth_state] = 'state' end it "updates access token" do - expires_at = Time.current + 1.day - expires_in = 1.day - access_token = double(token: token, - secret: secret, - expires_at: expires_at, - expires_in: expires_in, - refresh_token: refresh_token) allow_any_instance_of(OAuth2::Client) .to receive(:get_token) .with(hash_including( 'grant_type' => 'authorization_code', 'code' => code, - redirect_uri: users_import_bitbucket_callback_url), + 'redirect_uri' => users_import_bitbucket_callback_url), {}) .and_return(access_token) stub_omniauth_provider('bitbucket') @@ -75,6 +78,18 @@ RSpec.describe Import::BitbucketController do expect(session[:bitbucket_expires_in]).to eq(expires_in) expect(controller).to redirect_to(status_import_bitbucket_url) end + + it "passes namespace_id query param to status if provided" do + namespace_id = 30 + + allow_any_instance_of(OAuth2::Client) + .to receive(:get_token) + .and_return(access_token) + + get :callback, params: { code: code, state: 'state', namespace_id: namespace_id } + + expect(controller).to redirect_to(status_import_bitbucket_url(namespace_id: namespace_id)) + end end end @@ -82,7 +97,6 @@ RSpec.describe Import::BitbucketController do before do @repo = double(name: 'vim', slug: 'vim', owner: 'asd', full_name: 'asd/vim', clone_url: 'http://test.host/demo/url.git', 'valid?' => true) @invalid_repo = double(name: 'mercurialrepo', slug: 'mercurialrepo', owner: 'asd', full_name: 'asd/mercurialrepo', clone_url: 'http://test.host/demo/mercurialrepo.git', 'valid?' => false) - allow(controller).to receive(:provider_url).and_return('http://demobitbucket.org') end context "when token does not exists" do @@ -109,10 +123,6 @@ RSpec.describe Import::BitbucketController do end it_behaves_like 'import controller status' do - before do - allow(controller).to receive(:provider_url).and_return('http://demobitbucket.org') - end - let(:repo) { @repo } let(:repo_id) { @repo.full_name } let(:import_source) { @repo.full_name } diff --git a/spec/controllers/import/bitbucket_server_controller_spec.rb b/spec/controllers/import/bitbucket_server_controller_spec.rb index d5f94be65b6..ac56d3af54f 100644 --- a/spec/controllers/import/bitbucket_server_controller_spec.rb +++ b/spec/controllers/import/bitbucket_server_controller_spec.rb @@ -134,6 +134,15 @@ RSpec.describe Import::BitbucketServerController do expect(response).to have_gitlab_http_status(:found) expect(response).to redirect_to(status_import_bitbucket_server_path) end + + it 'passes namespace_id to status page if provided' do + namespace_id = 5 + allow(controller).to receive(:allow_local_requests?).and_return(true) + + post :configure, params: { personal_access_token: token, bitbucket_server_username: username, bitbucket_server_url: url, namespace_id: namespace_id } + + expect(response).to redirect_to(status_import_bitbucket_server_path(namespace_id: namespace_id)) + end end describe 'GET status' do @@ -160,6 +169,14 @@ RSpec.describe Import::BitbucketServerController do expect(json_response.dig("provider_repos", 0, "id")).to eq(@repo.full_name) end + it 'redirects to connection form if session is missing auth data' do + session[:bitbucket_server_url] = nil + + get :status, format: :html + + expect(response).to redirect_to(new_import_bitbucket_server_path) + end + it_behaves_like 'import controller status' do let(:repo) { @repo } let(:repo_id) { "#{@repo.project_key}/#{@repo.slug}" } diff --git a/spec/controllers/import/bulk_imports_controller_spec.rb b/spec/controllers/import/bulk_imports_controller_spec.rb index a7089005abf..7177c8c10a6 100644 --- a/spec/controllers/import/bulk_imports_controller_spec.rb +++ b/spec/controllers/import/bulk_imports_controller_spec.rb @@ -48,15 +48,25 @@ RSpec.describe Import::BulkImportsController do expect(session[:bulk_import_gitlab_access_token]).to eq(token) expect(controller).to redirect_to(status_import_bulk_imports_url) end + + it 'passes namespace_id to status' do + namespace_id = 5 + token = 'token' + url = 'https://gitlab.example' + + post :configure, params: { bulk_import_gitlab_access_token: token, bulk_import_gitlab_url: url, namespace_id: namespace_id } + + expect(controller).to redirect_to(status_import_bulk_imports_url(namespace_id: namespace_id)) + end end describe 'GET status' do - def get_status(params_override = {}) + def get_status(params_override = {}, format = :json) params = { page: 1, per_page: 20, filter: '' }.merge(params_override) get :status, params: params, - format: :json, + format: format, session: { bulk_import_gitlab_url: 'https://gitlab.example.com', bulk_import_gitlab_access_token: 'demo-pat' @@ -169,6 +179,25 @@ RSpec.describe Import::BulkImportsController do end end end + + context 'when namespace_id is provided' do + let_it_be(:group) { create(:group) } + + it 'renders 404 if user does not have access to namespace' do + get_status({ namespace_id: group.id }, :html) + + expect(response).to have_gitlab_http_status(:not_found) + end + + it 'passes namespace to template' do + group.add_owner(user) + + get_status({ namespace_id: group.id }, :html) + + expect(response).to have_gitlab_http_status(:ok) + expect(assigns(:namespace)).to eq(group) + end + end end context 'when connection error occurs' do diff --git a/spec/controllers/import/fogbugz_controller_spec.rb b/spec/controllers/import/fogbugz_controller_spec.rb index 8f8cc9590a5..ed2a588eadf 100644 --- a/spec/controllers/import/fogbugz_controller_spec.rb +++ b/spec/controllers/import/fogbugz_controller_spec.rb @@ -8,6 +8,7 @@ RSpec.describe Import::FogbugzController do let(:user) { create(:user) } let(:token) { FFaker::Lorem.characters(8) } let(:uri) { 'https://example.com' } + let(:namespace_id) { 5 } before do sign_in(user) @@ -16,9 +17,11 @@ RSpec.describe Import::FogbugzController do describe 'POST #callback' do let(:xml_response) { %Q(<?xml version=\"1.0\" encoding=\"UTF-8\"?><response><token><![CDATA[#{token}]]></token></response>) } - it 'attempts to contact Fogbugz server' do + before do stub_request(:post, "https://example.com/api.asp").to_return(status: 200, body: xml_response, headers: {}) + end + it 'attempts to contact Fogbugz server' do post :callback, params: { uri: uri, email: 'test@example.com', password: 'mypassword' } expect(session[:fogbugz_token]).to eq(token) @@ -26,6 +29,29 @@ RSpec.describe Import::FogbugzController do expect(response).to redirect_to(new_user_map_import_fogbugz_path) end + it 'preserves namespace_id query param on success' do + post :callback, params: { uri: uri, email: 'test@example.com', password: 'mypassword', namespace_id: namespace_id } + + expect(response).to redirect_to(new_user_map_import_fogbugz_path(namespace_id: namespace_id)) + end + + it 'redirects to new page maintaining namespace_id when client raises standard error' do + namespace_id = 5 + allow(::Gitlab::FogbugzImport::Client).to receive(:new).and_raise(StandardError) + + post :callback, params: { uri: uri, email: 'test@example.com', password: 'mypassword', namespace_id: namespace_id } + + expect(response).to redirect_to(new_import_fogbugz_url(namespace_id: namespace_id)) + end + + it 'redirects to new page form when client raises authentication exception' do + allow(::Gitlab::FogbugzImport::Client).to receive(:new).and_raise(::Fogbugz::AuthenticationException) + + post :callback, params: { uri: uri, email: 'test@example.com', password: 'mypassword' } + + expect(response).to redirect_to(new_import_fogbugz_url) + end + context 'verify url' do shared_examples 'denies local request' do |reason| it 'does not allow requests' do @@ -76,6 +102,16 @@ RSpec.describe Import::FogbugzController do expect(session[:fogbugz_user_map]).to eq(user_map) expect(response).to redirect_to(status_import_fogbugz_path) end + + it 'preserves namespace_id query param' do + client = double(user_map: {}) + expect(controller).to receive(:client).and_return(client) + + post :create_user_map, params: { users: user_map, namespace_id: namespace_id } + + expect(session[:fogbugz_user_map]).to eq(user_map) + expect(response).to redirect_to(status_import_fogbugz_path(namespace_id: namespace_id)) + end end describe 'GET status' do @@ -84,11 +120,19 @@ RSpec.describe Import::FogbugzController do id: 'demo', name: 'vim', safe_name: 'vim', path: 'vim') end - before do - stub_client(valid?: true) + it 'redirects to new page form when client is invalid' do + stub_client(valid?: false) + + get :status + + expect(response).to redirect_to(new_import_fogbugz_path) end it_behaves_like 'import controller status' do + before do + stub_client(valid?: true) + end + let(:repo_id) { repo.id } let(:import_source) { repo.name } let(:provider_name) { 'fogbugz' } diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index 56e55c45e66..46160aac0c1 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -83,11 +83,10 @@ RSpec.describe Import::GithubController do expect(flash[:alert]).to eq('Access denied to your GitHub account.') end - it "includes namespace_id from session if it is present" do + it "includes namespace_id from query params if it is present" do namespace_id = 1 - session[:namespace_id] = 1 - get :callback, params: { state: valid_auth_state } + get :callback, params: { state: valid_auth_state, namespace_id: namespace_id } expect(controller).to redirect_to(status_import_github_url(namespace_id: namespace_id)) end diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb index 117c934ad5d..7b3978297fb 100644 --- a/spec/controllers/import/gitlab_controller_spec.rb +++ b/spec/controllers/import/gitlab_controller_spec.rb @@ -38,21 +38,47 @@ RSpec.describe Import::GitlabController do expect(controller.send(:importable_repos)).to be_an_instance_of(Array) end + + it "passes namespace_id query param to status if provided" do + namespace_id = 30 + + allow_next_instance_of(Gitlab::GitlabImport::Client) do |instance| + allow(instance).to receive(:get_token).and_return(token) + end + + get :callback, params: { namespace_id: namespace_id } + + expect(controller).to redirect_to(status_import_gitlab_url(namespace_id: namespace_id)) + end end describe "GET status" do let(:repo_fake) { Struct.new(:id, :path, :path_with_namespace, :web_url, keyword_init: true) } let(:repo) { repo_fake.new(id: 1, path: 'vim', path_with_namespace: 'asd/vim', web_url: 'https://gitlab.com/asd/vim') } - before do - assign_session_token + context 'when session contains access token' do + before do + assign_session_token + end + + it_behaves_like 'import controller status' do + let(:repo_id) { repo.id } + let(:import_source) { repo.path_with_namespace } + let(:provider_name) { 'gitlab' } + let(:client_repos_field) { :projects } + end end - it_behaves_like 'import controller status' do - let(:repo_id) { repo.id } - let(:import_source) { repo.path_with_namespace } - let(:provider_name) { 'gitlab' } - let(:client_repos_field) { :projects } + it 'redirects to auth if session does not contain access token' do + remote_gitlab_url = 'https://test.host/auth/gitlab' + + allow(Gitlab::GitlabImport::Client) + .to receive(:new) + .and_return(double(authorize_url: remote_gitlab_url)) + + get :status + + expect(response).to redirect_to(remote_gitlab_url) end end diff --git a/spec/controllers/profiles/emails_controller_spec.rb b/spec/controllers/profiles/emails_controller_spec.rb index b63db831462..818aba77354 100644 --- a/spec/controllers/profiles/emails_controller_spec.rb +++ b/spec/controllers/profiles/emails_controller_spec.rb @@ -20,9 +20,9 @@ RSpec.describe Profiles::EmailsController do before do allowed_threshold = Gitlab::ApplicationRateLimiter.rate_limits[action][:threshold] - allow(Gitlab::ApplicationRateLimiter) - .to receive(:increment) - .and_return(allowed_threshold + 1) + allow_next_instance_of(Gitlab::ApplicationRateLimiter::BaseStrategy) do |strategy| + allow(strategy).to receive(:increment).and_return(allowed_threshold + 1) + end end it 'does not send any email' do diff --git a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb index 48c747bf074..aafea0050d3 100644 --- a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb +++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb @@ -65,5 +65,42 @@ RSpec.describe Profiles::PersonalAccessTokensController do scopes: contain_exactly(:api, :read_user) ) end + + context "access_token_pagination feature flag is enabled" do + before do + stub_feature_flags(access_token_pagination: true) + allow(Kaminari.config).to receive(:default_per_page).and_return(1) + create(:personal_access_token, user: user) + end + + it "returns paginated response" do + get :index, params: { page: 1 } + expect(assigns(:active_personal_access_tokens).count).to eq(1) + end + + it 'adds appropriate headers' do + get :index, params: { page: 1 } + expect_header('X-Per-Page', '1') + expect_header('X-Page', '1') + expect_header('X-Next-Page', '2') + expect_header('X-Total', '2') + end + end + + context "access_token_pagination feature flag is disabled" do + before do + stub_feature_flags(access_token_pagination: false) + create(:personal_access_token, user: user) + end + + it "returns all tokens in system" do + get :index, params: { page: 1 } + expect(assigns(:active_personal_access_tokens).count).to eq(2) + end + end + end + + def expect_header(header_name, header_val) + expect(response.headers[header_name]).to eq(header_val) end end diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb index ebcf35a7ecd..a275bc28631 100644 --- a/spec/controllers/projects/hooks_controller_spec.rb +++ b/spec/controllers/projects/hooks_controller_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Projects::HooksController do + include AfterNextHelpers + let_it_be(:project) { create(:project) } let(:user) { project.first_owner } @@ -20,6 +22,36 @@ RSpec.describe Projects::HooksController do end end + describe '#update' do + let_it_be(:hook) { create(:project_hook, project: project) } + + let(:params) do + { namespace_id: project.namespace, project_id: project, id: hook.id } + end + + it 'adds, updates and deletes URL variables' do + hook.update!(url_variables: { 'a' => 'bar', 'b' => 'woo' }) + + params[:hook] = { + url_variables: [ + { key: 'a', value: 'updated' }, + { key: 'b', value: nil }, + { key: 'c', value: 'new' } + ] + } + + put :update, params: params + + expect(response).to have_gitlab_http_status(:found) + expect(flash[:notice]).to include('successfully updated') + + expect(hook.reload.url_variables).to eq( + 'a' => 'updated', + 'c' => 'new' + ) + end + end + describe '#edit' do let_it_be(:hook) { create(:project_hook, project: project) } @@ -87,14 +119,30 @@ RSpec.describe Projects::HooksController do job_events: true, pipeline_events: true, wiki_page_events: true, - deployment_events: true + deployment_events: true, + + url_variables: [{ key: 'token', value: 'some secret value' }] } post :create, params: { namespace_id: project.namespace, project_id: project, hook: hook_params } expect(response).to have_gitlab_http_status(:found) - expect(ProjectHook.all.size).to eq(1) - expect(ProjectHook.first).to have_attributes(hook_params) + expect(flash[:alert]).to be_blank + expect(ProjectHook.count).to eq(1) + expect(ProjectHook.first).to have_attributes(hook_params.except(:url_variables)) + expect(ProjectHook.first).to have_attributes(url_variables: { 'token' => 'some secret value' }) + end + + it 'alerts the user if the new hook is invalid' do + hook_params = { + token: "TEST\nTOKEN", + url: "http://example.com" + } + + post :create, params: { namespace_id: project.namespace, project_id: project, hook: hook_params } + + expect(flash[:alert]).to be_present + expect(ProjectHook.count).to eq(0) end end @@ -109,6 +157,45 @@ RSpec.describe Projects::HooksController do describe '#test' do let(:hook) { create(:project_hook, project: project) } + context 'when the hook executes successfully' do + before do + stub_request(:post, hook.url).to_return(status: 200) + end + + it 'informs the user' do + post :test, params: { namespace_id: project.namespace, project_id: project, id: hook } + + expect(flash[:notice]).to include('executed successfully') + expect(flash[:notice]).to include('HTTP 200') + end + end + + context 'when the hook runs, but fails' do + before do + stub_request(:post, hook.url).to_return(status: 400) + end + + it 'informs the user' do + post :test, params: { namespace_id: project.namespace, project_id: project, id: hook } + + expect(flash[:alert]).to include('executed successfully but') + expect(flash[:alert]).to include('HTTP 400') + end + end + + context 'when the hook fails completely' do + before do + allow_next(::TestHooks::ProjectService) + .to receive(:execute).and_return({ message: 'All is woe' }) + end + + it 'informs the user' do + post :test, params: { namespace_id: project.namespace, project_id: project, id: hook } + + expect(flash[:alert]).to include('failed: All is woe') + end + end + context 'when the endpoint receives requests above the limit', :freeze_time, :clean_gitlab_redis_rate_limiting do before do allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits) diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 1305693372c..badac688229 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1818,7 +1818,7 @@ RSpec.describe Projects::IssuesController do context 'user is allowed access' do before do - project.add_user(user, :maintainer) + project.add_member(user, :maintainer) end it 'displays all available notes' do diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 107eb1ed3a3..e4e3151dd12 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -752,28 +752,6 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do end end - describe 'GET status.json' do - let(:job) { create(:ci_build, pipeline: pipeline) } - let(:status) { job.detailed_status(double('user')) } - - before do - get :status, params: { - namespace_id: project.namespace, - project_id: project, - id: job.id - }, - format: :json - end - - it 'return a detailed job status in json' do - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['text']).to eq status.text - expect(json_response['label']).to eq status.label - expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png" - end - end - describe 'POST retry' do before do project.add_developer(user) diff --git a/spec/controllers/projects/logs_controller_spec.rb b/spec/controllers/projects/logs_controller_spec.rb deleted file mode 100644 index 1c81ae93b42..00000000000 --- a/spec/controllers/projects/logs_controller_spec.rb +++ /dev/null @@ -1,214 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::LogsController do - include KubernetesHelpers - - let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project) } - - let_it_be(:environment) do - create(:environment, name: 'production', project: project) - end - - let(:pod_name) { "foo" } - let(:container) { 'container-1' } - - before do - sign_in(user) - end - - describe 'GET #index' do - let(:empty_project) { create(:project) } - - it 'returns 404 with reporter access' do - project.add_reporter(user) - - get :index, params: environment_params - - expect(response).to have_gitlab_http_status(:not_found) - end - - it 'renders empty logs page if no environment exists' do - empty_project.add_developer(user) - - get :index, params: { namespace_id: empty_project.namespace, project_id: empty_project } - - expect(response).to be_ok - expect(response).to render_template 'empty_logs' - end - - it 'renders index template' do - project.add_developer(user) - - get :index, params: environment_params - - expect(response).to be_ok - expect(response).to render_template 'index' - end - - context 'with feature flag disabled' do - before do - stub_feature_flags(monitor_logging: false) - end - - it 'returns 404 with reporter access' do - project.add_developer(user) - - get :index, params: environment_params - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - - shared_examples 'pod logs service' do |endpoint, service| - let(:service_result) do - { - status: :success, - logs: ['Log 1', 'Log 2', 'Log 3'], - pods: [pod_name], - pod_name: pod_name, - container_name: container - } - end - - let(:service_result_json) { Gitlab::Json.parse(service_result.to_json) } - - let_it_be(:cluster) { create(:cluster, :provided_by_gcp, environment_scope: '*', projects: [project]) } - - before do - allow_next_instance_of(service) do |instance| - allow(instance).to receive(:execute).and_return(service_result) - end - end - - it 'returns 404 with reporter access' do - project.add_reporter(user) - - get endpoint, params: environment_params(pod_name: pod_name, format: :json) - - expect(response).to have_gitlab_http_status(:not_found) - end - - context 'with developer access' do - before do - project.add_developer(user) - end - - it 'returns the service result' do - get endpoint, params: environment_params(pod_name: pod_name, format: :json) - - expect(response).to have_gitlab_http_status(:success) - expect(json_response).to eq(service_result_json) - end - end - - context 'with maintainer access' do - before do - project.add_maintainer(user) - end - - it 'returns the service result' do - get endpoint, params: environment_params(pod_name: pod_name, format: :json) - - expect(response).to have_gitlab_http_status(:success) - expect(json_response).to eq(service_result_json) - end - - it 'sets the polling header' do - get endpoint, params: environment_params(pod_name: pod_name, format: :json) - - expect(response).to have_gitlab_http_status(:success) - expect(response.headers['Poll-Interval']).to eq('3000') - end - - context 'with gitlab managed apps logs' do - it 'uses cluster finder services to select cluster', :aggregate_failures do - cluster_list = [cluster] - service_params = { params: ActionController::Parameters.new(pod_name: pod_name).permit! } - request_params = { - namespace_id: project.namespace, - project_id: project, - cluster_id: cluster.id, - pod_name: pod_name, - format: :json - } - - expect_next_instance_of(ClusterAncestorsFinder, project, user) do |finder| - expect(finder).to receive(:execute).and_return(cluster_list) - expect(cluster_list).to receive(:find).and_call_original - end - - expect_next_instance_of(service, cluster, Gitlab::Kubernetes::Helm::NAMESPACE, service_params) do |instance| - expect(instance).to receive(:execute).and_return(service_result) - end - - get endpoint, params: request_params - - expect(response).to have_gitlab_http_status(:success) - expect(json_response).to eq(service_result_json) - end - end - - context 'when service is processing' do - let(:service_result) { nil } - - it 'returns a 202' do - get endpoint, params: environment_params(pod_name: pod_name, format: :json) - - expect(response).to have_gitlab_http_status(:accepted) - end - end - - shared_examples 'unsuccessful execution response' do |message| - let(:service_result) do - { - status: :error, - message: message - } - end - - it 'returns the error' do - get endpoint, params: environment_params(pod_name: pod_name, format: :json) - - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response).to eq(service_result_json) - end - end - - context 'when service is failing' do - it_behaves_like 'unsuccessful execution response', 'some error' - end - - context 'when cluster is nil' do - let!(:cluster) { nil } - - it_behaves_like 'unsuccessful execution response', 'Environment does not have deployments' - end - - context 'when namespace is empty' do - before do - allow(environment).to receive(:deployment_namespace).and_return('') - end - - it_behaves_like 'unsuccessful execution response', 'Environment does not have deployments' - end - end - end - - describe 'GET #k8s' do - it_behaves_like 'pod logs service', :k8s, PodLogs::KubernetesService - end - - describe 'GET #elasticsearch' do - it_behaves_like 'pod logs service', :elasticsearch, PodLogs::ElasticsearchService - end - - def environment_params(opts = {}) - opts.reverse_merge(namespace_id: project.namespace, - project_id: project, - environment_name: environment.name) - end -end diff --git a/spec/controllers/projects/mirrors_controller_spec.rb b/spec/controllers/projects/mirrors_controller_spec.rb index 686effd799e..d33bc215cfc 100644 --- a/spec/controllers/projects/mirrors_controller_spec.rb +++ b/spec/controllers/projects/mirrors_controller_spec.rb @@ -211,7 +211,7 @@ RSpec.describe Projects::MirrorsController do context 'data in the cache' do let(:ssh_key) { 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf' } - let(:ssh_fp) { { type: 'ed25519', bits: 256, fingerprint: '2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16', index: 0 } } + let(:ssh_fp) { { type: 'ed25519', bits: 256, fingerprint: '2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16', fingerprint_sha256: 'SHA256:eUXGGm1YGsMAS7vkcx6JOJdOGHPem5gQp4taiCfCLB8', index: 0 } } it 'returns the data with a 200 response' do stub_reactive_cache(cache, known_hosts: ssh_key) diff --git a/spec/controllers/projects/pipelines/tests_controller_spec.rb b/spec/controllers/projects/pipelines/tests_controller_spec.rb index 2db54dbe671..ddcab8b048e 100644 --- a/spec/controllers/projects/pipelines/tests_controller_spec.rb +++ b/spec/controllers/projects/pipelines/tests_controller_spec.rb @@ -45,11 +45,26 @@ RSpec.describe Projects::Pipelines::TestsController do pipeline.job_artifacts.first.update!(expire_at: Date.yesterday) 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') + expect(json_response['total_count']).to eq(3) + expect(json_response['test_cases'].size).to eq(3) + end + end + + context 'when artifacts do not exist' do + before do + pipeline.job_artifacts.each(&:destroy) + end + it 'renders not_found errors', :aggregate_failures do get_tests_show_json(build_ids) expect(response).to have_gitlab_http_status(:not_found) - expect(json_response['errors']).to eq('Test report artifacts have expired') + expect(json_response['errors']).to eq('Test report artifacts not found') end end @@ -68,7 +83,6 @@ RSpec.describe Projects::Pipelines::TestsController do expect(response).to have_gitlab_http_status(:ok) expect(json_response['name']).to eq('test') - expect(json_response['artifacts_expired']).to be_falsey # Each test failure in this pipeline has a matching failure in the default branch recent_failures = json_response['test_cases'].map { |tc| tc['recent_failures'] } diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index b3b803649d1..06930d8727b 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -827,6 +827,14 @@ RSpec.describe Projects::PipelinesController do { chart_param: 'lead-time', event: 'p_analytics_ci_cd_lead_time' + }, + { + chart_param: 'time-to-restore-service', + event: 'p_analytics_ci_cd_time_to_restore_service' + }, + { + chart_param: 'change-failure-rate', + event: 'p_analytics_ci_cd_change_failure_rate' } ].each do |tab| it_behaves_like 'tracking unique visits', :charts do diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 9bb34a38005..46eb340cbba 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -68,27 +68,6 @@ RSpec.describe Projects::ProjectMembersController do end end - context 'group links' do - let_it_be(:project_group_link) { create(:project_group_link, project: project, group: group) } - - it 'lists group links' do - get :index, params: { namespace_id: project.namespace, project_id: project } - - expect(assigns(:group_links).map(&:id)).to contain_exactly(project_group_link.id) - end - - context 'when `search_groups` param is present' do - let(:group_2) { create(:group, :public, name: 'group_2') } - let!(:project_group_link_2) { create(:project_group_link, project: project, group: group_2) } - - it 'lists group links that match search' do - get :index, params: { namespace_id: project.namespace, project_id: project, search_groups: 'group_2' } - - expect(assigns(:group_links).map(&:id)).to contain_exactly(project_group_link_2.id) - end - end - end - context 'invited members' do let_it_be(:invited_member) { create(:project_member, :invited, project: project) } diff --git a/spec/controllers/projects/service_ping_controller_spec.rb b/spec/controllers/projects/service_ping_controller_spec.rb index fa92efee079..22fb18edc80 100644 --- a/spec/controllers/projects/service_ping_controller_spec.rb +++ b/spec/controllers/projects/service_ping_controller_spec.rb @@ -80,16 +80,24 @@ RSpec.describe Projects::ServicePingController do it_behaves_like 'counter is not increased' it_behaves_like 'counter is increased', 'WEB_IDE_PREVIEWS_SUCCESS_COUNT' - context 'when the user has access to the project' do + context 'when the user has access to the project', :snowplow do let(:user) { project.owner } it 'increases the live preview view counter' do - expect(Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_live_preview_edit_action).with(author: user) + expect(Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_live_preview_edit_action).with(author: user, project: project) subject expect(response).to have_gitlab_http_status(:ok) end + + it_behaves_like 'Snowplow event tracking' do + let(:project) { create(:project) } + let(:category) { 'ide_edit' } + let(:action) { 'g_edit_by_live_preview' } + let(:namespace) { project.namespace } + let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } + end 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 d50f1aa1dd8..e5ae1b04a86 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -8,305 +8,294 @@ RSpec.describe Projects::Settings::CiCdController do let(:project) { project_auto_devops.project } - before do - project.add_maintainer(user) - sign_in(user) - end - - describe 'GET show' do - let_it_be(:parent_group) { create(:group) } - let_it_be(:group) { create(:group, parent: parent_group) } - let_it_be(:other_project) { create(:project, group: group) } + context 'as a maintainer' do + before do + project.add_maintainer(user) + sign_in(user) + end - it 'renders show with 200 status code' do - get :show, params: { namespace_id: project.namespace, project_id: project } + describe 'GET show' do + let_it_be(:parent_group) { create(:group) } + let_it_be(:group) { create(:group, parent: parent_group) } + let_it_be(:other_project) { create(:project, group: group) } - expect(response).to have_gitlab_http_status(:ok) - expect(response).to render_template(:show) - end + it 'renders show with 200 status code' do + get :show, params: { namespace_id: project.namespace, project_id: project } - context 'with CI/CD disabled' do - before do - project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED) + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:show) end - it 'renders show with 404 status code' do - get :show, params: { namespace_id: project.namespace, project_id: project } - expect(response).to have_gitlab_http_status(:not_found) + context 'with CI/CD disabled' do + before do + project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED) + end + + it 'renders show with 404 status code' do + get :show, params: { namespace_id: project.namespace, project_id: project } + expect(response).to have_gitlab_http_status(:not_found) + end end - end - context 'with group runners' do - let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) } - let_it_be(:project_runner) { create(:ci_runner, :project, projects: [other_project]) } - let_it_be(:shared_runner) { create(:ci_runner, :instance) } + context 'with group runners' do + let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) } + let_it_be(:project_runner) { create(:ci_runner, :project, projects: [other_project]) } + let_it_be(:shared_runner) { create(:ci_runner, :instance) } - it 'sets assignable project runners only' do - group.add_maintainer(user) + it 'sets assignable project runners only' do + group.add_maintainer(user) - get :show, params: { namespace_id: project.namespace, project_id: project } + get :show, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:assignable_runners)).to contain_exactly(project_runner) + expect(assigns(:assignable_runners)).to contain_exactly(project_runner) + end end - end - context 'prevents N+1 queries for tags' do - render_views + context 'prevents N+1 queries for tags' do + render_views - def show - get :show, params: { namespace_id: project.namespace, project_id: project } - end + def show + get :show, params: { namespace_id: project.namespace, project_id: project } + end - it 'has the same number of queries with one tag or with many tags', :request_store do - group.add_maintainer(user) + it 'has the same number of queries with one tag or with many tags', :request_store do + group.add_maintainer(user) - show # warmup + show # warmup - # with one tag - create(:ci_runner, :instance, tag_list: %w(shared_runner)) - create(:ci_runner, :project, projects: [other_project], tag_list: %w(project_runner)) - create(:ci_runner, :group, groups: [group], tag_list: %w(group_runner)) - control = ActiveRecord::QueryRecorder.new { show } + # with one tag + create(:ci_runner, :instance, tag_list: %w(shared_runner)) + create(:ci_runner, :project, projects: [other_project], tag_list: %w(project_runner)) + create(:ci_runner, :group, groups: [group], tag_list: %w(group_runner)) + control = ActiveRecord::QueryRecorder.new { show } - # with several tags - create(:ci_runner, :instance, tag_list: %w(shared_runner tag2 tag3)) - create(:ci_runner, :project, projects: [other_project], tag_list: %w(project_runner tag2 tag3)) - create(:ci_runner, :group, groups: [group], tag_list: %w(group_runner tag2 tag3)) + # with several tags + create(:ci_runner, :instance, tag_list: %w(shared_runner tag2 tag3)) + create(:ci_runner, :project, projects: [other_project], tag_list: %w(project_runner tag2 tag3)) + create(:ci_runner, :group, groups: [group], tag_list: %w(group_runner tag2 tag3)) - expect { show }.not_to exceed_query_limit(control) + expect { show }.not_to exceed_query_limit(control) + end end end - end - describe '#reset_cache' do - before do - sign_in(user) - - project.add_maintainer(user) + describe '#reset_cache' do + before do + sign_in(user) - allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(true) - end + project.add_maintainer(user) - subject { post :reset_cache, params: { namespace_id: project.namespace, project_id: project }, format: :json } + allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(true) + end - it 'calls reset project cache service' do - expect(ResetProjectCacheService).to receive_message_chain(:new, :execute) + subject { post :reset_cache, params: { namespace_id: project.namespace, project_id: project }, format: :json } - subject - end + it 'calls reset project cache service' do + expect(ResetProjectCacheService).to receive_message_chain(:new, :execute) - context 'when service returns successfully' do - it 'returns a success header' do subject - - expect(response).to have_gitlab_http_status(:ok) end - end - context 'when service does not return successfully' do - before do - allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(false) + context 'when service returns successfully' do + it 'returns a success header' do + subject + + expect(response).to have_gitlab_http_status(:ok) + end end - it 'returns an error header' do - subject + context 'when service does not return successfully' do + before do + allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(false) + end - expect(response).to have_gitlab_http_status(:bad_request) + it 'returns an error header' do + subject + + expect(response).to have_gitlab_http_status(:bad_request) + end end end - end - describe 'PUT #reset_registration_token' do - subject { put :reset_registration_token, params: { namespace_id: project.namespace, project_id: project } } + describe 'PUT #reset_registration_token' do + subject { put :reset_registration_token, params: { namespace_id: project.namespace, project_id: project } } - it 'resets runner registration token' do - expect { subject }.to change { project.reload.runners_token } - expect(flash[:toast]).to eq('New runners registration token has been generated!') - end + it 'resets runner registration token' do + expect { subject }.to change { project.reload.runners_token } + expect(flash[:toast]).to eq('New runners registration token has been generated!') + end - it 'redirects the user to admin runners page' do - subject + it 'redirects the user to admin runners page' do + subject - expect(response).to redirect_to(namespace_project_settings_ci_cd_path) + expect(response).to redirect_to(namespace_project_settings_ci_cd_path) + end end - end - describe 'PATCH update' do - let(:params) { { ci_config_path: '' } } - - subject do - patch :update, - params: { - namespace_id: project.namespace.to_param, - project_id: project, - project: params - } - end + describe 'PATCH update' do + let(:params) { { ci_config_path: '' } } - it 'redirects to the settings page' do - subject + subject do + patch :update, + params: { + namespace_id: project.namespace.to_param, + project_id: project, + project: params + } + end - expect(response).to have_gitlab_http_status(:found) - expect(flash[:toast]).to eq("Pipelines settings for '#{project.name}' were successfully updated.") - end + it 'redirects to the settings page' do + subject - context 'when updating the auto_devops settings' do - let(:params) { { auto_devops_attributes: { enabled: '' } } } + expect(response).to have_gitlab_http_status(:found) + expect(flash[:toast]).to eq("Pipelines settings for '#{project.name}' were successfully updated.") + end - context 'following the instance default' do + context 'when updating the auto_devops settings' do let(:params) { { auto_devops_attributes: { enabled: '' } } } - it 'allows enabled to be set to nil' do - subject - project_auto_devops.reload + context 'following the instance default' do + let(:params) { { auto_devops_attributes: { enabled: '' } } } - expect(project_auto_devops.enabled).to be_nil - end - end + it 'allows enabled to be set to nil' do + subject + project_auto_devops.reload - context 'when run_auto_devops_pipeline is true' do - before do - expect_next_instance_of(Projects::UpdateService) do |instance| - expect(instance).to receive(:run_auto_devops_pipeline?).and_return(true) + expect(project_auto_devops.enabled).to be_nil end end - context 'when the project repository is empty' do - it 'sets a notice flash' do - subject - - expect(controller).to set_flash[:notice] + context 'when run_auto_devops_pipeline is true' do + before do + expect_next_instance_of(Projects::UpdateService) do |instance| + expect(instance).to receive(:run_auto_devops_pipeline?).and_return(true) + end end - it 'does not queue a CreatePipelineWorker' do - expect(CreatePipelineWorker).not_to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args) + context 'when the project repository is empty' do + it 'sets a notice flash' do + subject - subject + expect(controller).to set_flash[:notice] + end + + it 'does not queue a CreatePipelineWorker' do + expect(CreatePipelineWorker).not_to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args) + + subject + end end - end - context 'when the project repository is not empty' do - let(:project) { create(:project, :repository) } + context 'when the project repository is not empty' do + let(:project) { create(:project, :repository) } - it 'displays a toast message' do - allow(CreatePipelineWorker).to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args) + it 'displays a toast message' do + allow(CreatePipelineWorker).to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args) - subject + subject - expect(controller).to set_flash[:toast] - end + expect(controller).to set_flash[:toast] + end - it 'queues a CreatePipelineWorker' do - expect(CreatePipelineWorker).to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args) + it 'queues a CreatePipelineWorker' do + expect(CreatePipelineWorker).to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args) - subject - end + subject + end - it 'creates a pipeline', :sidekiq_inline do - project.repository.create_file(user, 'Gemfile', 'Gemfile contents', - message: 'Add Gemfile', - branch_name: 'master') + it 'creates a pipeline', :sidekiq_inline do + project.repository.create_file(user, 'Gemfile', 'Gemfile contents', + message: 'Add Gemfile', + branch_name: 'master') - expect { subject }.to change { Ci::Pipeline.count }.by(1) + expect { subject }.to change { Ci::Pipeline.count }.by(1) + end end end - end - context 'when run_auto_devops_pipeline is not true' do - before do - expect_next_instance_of(Projects::UpdateService) do |instance| - expect(instance).to receive(:run_auto_devops_pipeline?).and_return(false) + context 'when run_auto_devops_pipeline is not true' do + before do + expect_next_instance_of(Projects::UpdateService) do |instance| + expect(instance).to receive(:run_auto_devops_pipeline?).and_return(false) + end end - end - it 'does not queue a CreatePipelineWorker' do - expect(CreatePipelineWorker).not_to receive(:perform_async).with(project.id, user.id, :web, any_args) + it 'does not queue a CreatePipelineWorker' do + expect(CreatePipelineWorker).not_to receive(:perform_async).with(project.id, user.id, :web, any_args) - subject + subject + end end end - end - context 'when updating general settings' do - context 'when build_timeout_human_readable is not specified' do - let(:params) { { build_timeout_human_readable: '' } } + context 'when updating general settings' do + context 'when build_timeout_human_readable is not specified' do + let(:params) { { build_timeout_human_readable: '' } } - it 'set default timeout' do - subject + it 'set default timeout' do + subject - project.reload - expect(project.build_timeout).to eq(3600) + project.reload + expect(project.build_timeout).to eq(3600) + end end - end - context 'when build_timeout_human_readable is specified' do - let(:params) { { build_timeout_human_readable: '1h 30m' } } + context 'when build_timeout_human_readable is specified' do + let(:params) { { build_timeout_human_readable: '1h 30m' } } - it 'set specified timeout' do - subject + it 'set specified timeout' do + subject - project.reload - expect(project.build_timeout).to eq(5400) + project.reload + expect(project.build_timeout).to eq(5400) + end end - end - - context 'when build_timeout_human_readable is invalid' do - let(:params) { { build_timeout_human_readable: '5m' } } - it 'set specified timeout' do - subject - - expect(controller).to set_flash[:alert] - expect(response).to redirect_to(namespace_project_settings_ci_cd_path) - end - end + context 'when build_timeout_human_readable is invalid' do + let(:params) { { build_timeout_human_readable: '5m' } } - context 'when default_git_depth is not specified' do - let(:params) { { ci_cd_settings_attributes: { default_git_depth: 10 } } } + it 'set specified timeout' do + subject - before do - project.ci_cd_settings.update!(default_git_depth: nil) + expect(controller).to set_flash[:alert] + expect(response).to redirect_to(namespace_project_settings_ci_cd_path) + end end - it 'set specified git depth' do - subject + context 'when default_git_depth is not specified' do + let(:params) { { ci_cd_settings_attributes: { default_git_depth: 10 } } } - project.reload - expect(project.ci_default_git_depth).to eq(10) - end - end + before do + project.ci_cd_settings.update!(default_git_depth: nil) + end - context 'when forward_deployment_enabled is not specified' do - let(:params) { { ci_cd_settings_attributes: { forward_deployment_enabled: false } } } + it 'set specified git depth' do + subject - before do - project.ci_cd_settings.update!(forward_deployment_enabled: nil) + project.reload + expect(project.ci_default_git_depth).to eq(10) + end end - it 'sets forward deployment enabled' do - subject - - project.reload - expect(project.ci_forward_deployment_enabled).to eq(false) - end - end + context 'when forward_deployment_enabled is not specified' do + let(:params) { { ci_cd_settings_attributes: { forward_deployment_enabled: false } } } - context 'when max_artifacts_size is specified' do - let(:params) { { max_artifacts_size: 10 } } + before do + project.ci_cd_settings.update!(forward_deployment_enabled: nil) + end - context 'and user is not an admin' do - it 'does not set max_artifacts_size' do + it 'sets forward deployment enabled' do subject project.reload - expect(project.max_artifacts_size).to be_nil + expect(project.ci_forward_deployment_enabled).to eq(false) end end - context 'and user is an admin' do - let(:user) { create(:admin) } + context 'when max_artifacts_size is specified' do + let(:params) { { max_artifacts_size: 10 } } - context 'with admin mode disabled' do + context 'and user is not an admin' do it 'does not set max_artifacts_size' do subject @@ -315,33 +304,81 @@ RSpec.describe Projects::Settings::CiCdController do end end - context 'with admin mode enabled', :enable_admin_mode do - it 'sets max_artifacts_size' do - subject + context 'and user is an admin' do + let(:user) { create(:admin) } - project.reload - expect(project.max_artifacts_size).to eq(10) + context 'with admin mode disabled' do + it 'does not set max_artifacts_size' do + subject + + project.reload + expect(project.max_artifacts_size).to be_nil + end + end + + context 'with admin mode enabled', :enable_admin_mode do + it 'sets max_artifacts_size' do + subject + + project.reload + expect(project.max_artifacts_size).to eq(10) + end end end end end end + + describe 'GET #runner_setup_scripts' do + it 'renders the setup scripts' do + get :runner_setup_scripts, params: { os: 'linux', arch: 'amd64', namespace_id: project.namespace, project_id: project } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to have_key("install") + expect(json_response).to have_key("register") + end + + it 'renders errors if they occur' do + get :runner_setup_scripts, params: { os: 'foo', arch: 'bar', namespace_id: project.namespace, project_id: project } + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to have_key("errors") + end + end end - describe 'GET #runner_setup_scripts' do - it 'renders the setup scripts' do - get :runner_setup_scripts, params: { os: 'linux', arch: 'amd64', namespace_id: project.namespace, project_id: project } + context 'as a developer' do + before do + sign_in(user) + project.add_developer(user) + get :show, params: { namespace_id: project.namespace, project_id: project } + end - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to have_key("install") - expect(json_response).to have_key("register") + it 'responds with 404' do + expect(response).to have_gitlab_http_status(:not_found) end + end - it 'renders errors if they occur' do - get :runner_setup_scripts, params: { os: 'foo', arch: 'bar', namespace_id: project.namespace, project_id: project } + context 'as a reporter' do + before do + sign_in(user) + project.add_reporter(user) + get :show, params: { namespace_id: project.namespace, project_id: project } + end - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response).to have_key("errors") + it 'responds with 404' do + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'as an unauthenticated user' do + before do + get :show, params: { namespace_id: project.namespace, project_id: project } + end + + it 'redirects to sign in' do + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to('/users/sign_in') end end end diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb index e6ca088a533..8ee9f22aa7f 100644 --- a/spec/controllers/projects/settings/integrations_controller_spec.rb +++ b/spec/controllers/projects/settings/integrations_controller_spec.rb @@ -138,7 +138,7 @@ RSpec.describe Projects::Settings::IntegrationsController do end end - context 'when unsuccessful' do + context 'when unsuccessful', :clean_gitlab_redis_rate_limiting 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) @@ -148,7 +148,7 @@ RSpec.describe Projects::Settings::IntegrationsController do expect(response).to be_successful expect(json_response).to eq( 'error' => true, - 'message' => 'Connection failed. Please check your settings.', + 'message' => 'Connection failed. Check your integration settings.', 'service_response' => '', 'test_failed' => true ) @@ -163,7 +163,7 @@ RSpec.describe Projects::Settings::IntegrationsController do expect(response).to be_successful expect(json_response).to eq( 'error' => true, - 'message' => 'Connection failed. Please check your settings.', + 'message' => 'Connection failed. Check your integration settings.', 'service_response' => "URL 'http://127.0.0.1' is blocked: Requests to localhost are not allowed", 'test_failed' => true ) @@ -177,13 +177,33 @@ RSpec.describe Projects::Settings::IntegrationsController do expect(response).to be_successful expect(json_response).to eq( 'error' => true, - 'message' => 'Connection failed. Please check your settings.', + 'message' => 'Connection failed. Check your integration settings.', 'service_response' => 'Connection refused', 'test_failed' => true ) end end end + + context 'when the endpoint receives requests above the limit', :freeze_time, :clean_gitlab_redis_rate_limiting do + before do + allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits) + .and_return(project_testing_integration: { threshold: 1, interval: 1.minute }) + end + + it 'prevents making test requests' do + stub_jira_integration_test + + expect_next_instance_of(::Integrations::Test::ProjectService) do |service| + expect(service).to receive(:execute).and_return(http_status: 200) + end + + 2.times { post :test, params: project_params(service: integration_params) } + + expect(response.body).to eq(_('This endpoint has been requested too many times. Try again later.')) + expect(response).to have_gitlab_http_status(:too_many_requests) + end + end end describe 'PUT #update' do diff --git a/spec/controllers/projects/settings/operations_controller_spec.rb b/spec/controllers/projects/settings/operations_controller_spec.rb index c1fa91e9f8b..76d8191e342 100644 --- a/spec/controllers/projects/settings/operations_controller_spec.rb +++ b/spec/controllers/projects/settings/operations_controller_spec.rb @@ -437,108 +437,6 @@ RSpec.describe Projects::Settings::OperationsController do end end - context 'tracing integration' do - describe 'GET #show' do - context 'with existing setting' do - let_it_be(:setting) do - create(:project_tracing_setting, project: project) - end - - it 'loads existing setting' do - get :show, params: project_params(project) - - expect(controller.helpers.tracing_setting).to eq(setting) - end - end - - context 'without an existing setting' do - it 'builds a new setting' do - get :show, params: project_params(project) - - expect(controller.helpers.tracing_setting).to be_new_record - end - end - end - - describe 'PATCH #update' do - let_it_be(:external_url) { 'https://gitlab.com' } - - let(:params) do - { - tracing_setting_attributes: { - external_url: external_url - } - } - end - - it_behaves_like 'PATCHable' - - describe 'gitlab tracking', :snowplow do - shared_examples 'event tracking' do - it 'tracks an event' do - expect_snowplow_event( - category: 'project:operations:tracing', - action: 'external_url_populated', - user: user, - project: project, - namespace: project.namespace - ) - end - end - - shared_examples 'no event tracking' do - it 'does not track an event' do - expect_no_snowplow_event - end - end - - before do - make_request - end - - subject(:make_request) do - patch :update, params: project_params(project, params), format: :json - end - - context 'without existing setting' do - context 'when creating a new setting' do - it_behaves_like 'event tracking' - end - - context 'with invalid external_url' do - let_it_be(:external_url) { nil } - - it_behaves_like 'no event tracking' - end - end - - context 'with existing setting' do - let_it_be(:existing_setting) do - create(:project_tracing_setting, - project: project, - external_url: external_url) - end - - context 'when changing external_url' do - let_it_be(:external_url) { 'https://example.com' } - - it_behaves_like 'no event tracking' - end - - context 'with unchanged external_url' do - it_behaves_like 'no event tracking' - end - - context 'with invalid external_url' do - let_it_be(:external_url) { nil } - - it_behaves_like 'no event tracking' - end - end - end - end - end - private def project_params(project, params = {}) diff --git a/spec/controllers/projects/tracings_controller_spec.rb b/spec/controllers/projects/tracings_controller_spec.rb deleted file mode 100644 index 80e21349e20..00000000000 --- a/spec/controllers/projects/tracings_controller_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::TracingsController do - let_it_be(:user) { create(:user) } - - describe 'GET show' do - shared_examples 'user with read access' do |visibility_level| - let(:project) { create(:project, visibility_level) } - - %w[developer maintainer].each do |role| - context "with a #{visibility_level} project and #{role} role" do - before do - project.add_role(user, role) - end - - it 'renders OK' 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 - end - end - - shared_examples 'user without read access' do |visibility_level| - let(:project) { create(:project, visibility_level) } - - %w[guest reporter].each do |role| - context "with a #{visibility_level} project and #{role} role" do - before do - project.add_role(user, role) - end - - it 'returns 404' do - get :show, params: { namespace_id: project.namespace, project_id: project } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - end - - before do - sign_in(user) - end - - context 'with maintainer role' do - it_behaves_like 'user with read access', :public - it_behaves_like 'user with read access', :internal - it_behaves_like 'user with read access', :private - - context 'feature flag disabled' do - before do - stub_feature_flags(monitor_tracing: false) - end - - it_behaves_like 'user without read access', :public - it_behaves_like 'user without read access', :internal - it_behaves_like 'user without read access', :private - end - end - - context 'without maintainer role' do - it_behaves_like 'user without read access', :public - it_behaves_like 'user without read access', :internal - it_behaves_like 'user without read access', :private - end - end -end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 537f7aa5fee..34477a7bb68 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -409,7 +409,7 @@ RSpec.describe ProjectsController do before do project.update!(visibility: project_visibility.to_s) - project.team.add_user(user, :guest) if user_type == :member + project.team.add_member(user, :guest) if user_type == :member sign_in(user) unless user_type == :anonymous end @@ -1432,9 +1432,11 @@ RSpec.describe ProjectsController do shared_examples 'rate limits project export endpoint' do before do - allow(Gitlab::ApplicationRateLimiter) - .to receive(:increment) - .and_return(Gitlab::ApplicationRateLimiter.rate_limits["project_#{action}".to_sym][:threshold].call + 1) + allow_next_instance_of(Gitlab::ApplicationRateLimiter::BaseStrategy) do |strategy| + allow(strategy) + .to receive(:increment) + .and_return(Gitlab::ApplicationRateLimiter.rate_limits["project_#{action}".to_sym][:threshold].call + 1) + end end it 'prevents requesting project export' do @@ -1546,9 +1548,11 @@ RSpec.describe ProjectsController do context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_rate_limiting do before do - allow(Gitlab::ApplicationRateLimiter) - .to receive(:increment) - .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:project_download_export][:threshold].call + 1) + allow_next_instance_of(Gitlab::ApplicationRateLimiter::BaseStrategy) do |strategy| + allow(strategy) + .to receive(:increment) + .and_return(Gitlab::ApplicationRateLimiter.rate_limits[:project_download_export][:threshold].call + 1) + end end it 'prevents requesting project export' do diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index 36b230103db..c5a97812d1f 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -178,7 +178,8 @@ RSpec.describe RegistrationsController do category: 'RegistrationsController', action: 'accepted', label: 'invite_email', - property: member.id.to_s + property: member.id.to_s, + user: member.reload.user ) end end diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 4abcd414e51..b4d4e01e972 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -67,7 +67,7 @@ RSpec.describe SearchController do end end - describe 'GET #show' do + describe 'GET #show', :snowplow do it_behaves_like 'when the user cannot read cross project', :show, { search: 'hello' } do it 'still allows accessing the search page' do get :show @@ -257,6 +257,16 @@ RSpec.describe SearchController do end end + it_behaves_like 'Snowplow event tracking' do + subject { get :show, params: { group_id: namespace.id, scope: 'blobs', search: 'term' } } + + let(:project) { nil } + let(:category) { described_class.to_s } + let(:action) { 'i_search_total' } + let(:namespace) { create(:group) } + let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } + end + context 'on restricted projects' do context 'when signed out' do before do @@ -398,10 +408,11 @@ RSpec.describe SearchController do expect(payload[:metadata]['meta.search.filters.confidential']).to eq('true') expect(payload[:metadata]['meta.search.filters.state']).to eq('true') expect(payload[:metadata]['meta.search.project_ids']).to eq(%w(456 789)) - expect(payload[:metadata]['meta.search.search_level']).to eq('multi-project') + expect(payload[:metadata]['meta.search.type']).to eq('basic') + expect(payload[:metadata]['meta.search.level']).to eq('global') end - get :show, params: { scope: 'issues', search: 'hello world', group_id: '123', project_id: '456', project_ids: %w(456 789), search_level: 'multi-project', confidential: true, state: true, force_search_results: true } + get :show, params: { scope: 'issues', search: 'hello world', group_id: '123', project_id: '456', project_ids: %w(456 789), confidential: true, state: true, force_search_results: true } end it 'appends the default scope in meta.search.scope' do @@ -413,6 +424,16 @@ RSpec.describe SearchController do get :show, params: { search: 'hello world', group_id: '123', project_id: '456' } end + + it 'appends the search time based on the search' do + expect(controller).to receive(:append_info_to_payload).and_wrap_original do |method, payload| + method.call(payload) + + expect(payload[:metadata][:global_search_duration_s]).to be_a_kind_of(Numeric) + end + + get :show, params: { search: 'hello world', group_id: '123', project_id: '456' } + end end context 'abusive searches', :aggregate_failures do @@ -430,18 +451,6 @@ RSpec.describe SearchController do make_abusive_request expect(response).to have_gitlab_http_status(:ok) end - - context 'when the feature flag is disabled' do - before do - stub_feature_flags(prevent_abusive_searches: false) - end - - it 'returns a regular search result' do - expect(Gitlab::EmptySearchResults).not_to receive(:new) - make_abusive_request - expect(response).to have_gitlab_http_status(:ok) - end - end end end |