diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-20 16:49:51 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-20 16:49:51 +0300 |
commit | 71786ddc8e28fbd3cb3fcc4b3ff15e5962a1c82e (patch) | |
tree | 6a2d93ef3fb2d353bb7739e4b57e6541f51cdd71 /spec/controllers | |
parent | a7253423e3403b8c08f8a161e5937e1488f5f407 (diff) |
Add latest changes from gitlab-org/gitlab@15-9-stable-eev15.9.0-rc42
Diffstat (limited to 'spec/controllers')
55 files changed, 1520 insertions, 1097 deletions
diff --git a/spec/controllers/admin/application_settings/appearances_controller_spec.rb b/spec/controllers/admin/application_settings/appearances_controller_spec.rb index 78dce4558c3..f21c93e85d2 100644 --- a/spec/controllers/admin/application_settings/appearances_controller_spec.rb +++ b/spec/controllers/admin/application_settings/appearances_controller_spec.rb @@ -11,8 +11,10 @@ RSpec.describe Admin::ApplicationSettings::AppearancesController do let(:create_params) do { title: 'Foo', - pwa_short_name: 'F', description: 'Bar', + pwa_name: 'GitLab PWA', + pwa_short_name: 'F', + pwa_description: 'This is GitLab as PWA', header_message: header_message, footer_message: footer_message } @@ -26,6 +28,11 @@ RSpec.describe Admin::ApplicationSettings::AppearancesController do post :create, params: { appearance: create_params } expect(Appearance.current).to have_attributes( + title: 'Foo', + description: 'Bar', + pwa_name: 'GitLab PWA', + pwa_short_name: 'F', + pwa_description: 'This is GitLab as PWA', header_message: header_message, footer_message: footer_message, email_header_and_footer_enabled: false, @@ -41,6 +48,11 @@ RSpec.describe Admin::ApplicationSettings::AppearancesController do post :create, params: { appearance: create_params } expect(Appearance.current).to have_attributes( + title: 'Foo', + description: 'Bar', + pwa_name: 'GitLab PWA', + pwa_short_name: 'F', + pwa_description: 'This is GitLab as PWA', header_message: header_message, footer_message: footer_message, email_header_and_footer_enabled: true diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index 86a4ac61194..8e62aeed7d0 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Admin::ClustersController do +RSpec.describe Admin::ClustersController, feature_category: :kubernetes_management do include AccessMatchersForController include GoogleApi::CloudPlatformHelpers diff --git a/spec/controllers/admin/instance_review_controller_spec.rb b/spec/controllers/admin/instance_review_controller_spec.rb index 342562618b2..6eab135b3a6 100644 --- a/spec/controllers/admin/instance_review_controller_spec.rb +++ b/spec/controllers/admin/instance_review_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Admin::InstanceReviewController do +RSpec.describe Admin::InstanceReviewController, feature_category: :service_ping do include UsageDataHelpers let(:admin) { create(:admin) } diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb index 6d58abb9d4d..a39a1f38a11 100644 --- a/spec/controllers/admin/runners_controller_spec.rb +++ b/spec/controllers/admin/runners_controller_spec.rb @@ -34,6 +34,33 @@ RSpec.describe Admin::RunnersController, feature_category: :runner_fleet do end end + describe '#new' do + context 'when create_runner_workflow is enabled' do + before do + stub_feature_flags(create_runner_workflow: true) + end + + it 'renders a :new template' do + get :new + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:new) + end + end + + context 'when create_runner_workflow is disabled' do + before do + stub_feature_flags(create_runner_workflow: false) + end + + it 'returns :not_found' do + get :new + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + describe '#edit' do render_views @@ -105,49 +132,6 @@ RSpec.describe Admin::RunnersController, feature_category: :runner_fleet do end end - describe '#destroy' do - it 'destroys the runner' do - expect_next_instance_of(Ci::Runners::UnregisterRunnerService, runner, user) do |service| - expect(service).to receive(:execute).once.and_call_original - end - - delete :destroy, params: { id: runner.id } - - expect(response).to have_gitlab_http_status(:found) - expect(Ci::Runner.find_by(id: runner.id)).to be_nil - end - end - - describe '#resume' do - it 'marks the runner as active and ticks the queue' do - runner.update!(active: false) - - expect do - post :resume, params: { id: runner.id } - end.to change { runner.ensure_runner_queue_value } - - runner.reload - - expect(response).to have_gitlab_http_status(:found) - expect(runner.active).to eq(true) - end - end - - describe '#pause' do - it 'marks the runner as inactive and ticks the queue' do - runner.update!(active: true) - - expect do - post :pause, params: { id: runner.id } - end.to change { runner.ensure_runner_queue_value } - - runner.reload - - expect(response).to have_gitlab_http_status(:found) - expect(runner.active).to eq(false) - end - end - describe 'GET #runner_setup_scripts' do it 'renders the setup scripts' do get :runner_setup_scripts, params: { os: 'linux', arch: 'amd64' } diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index eecb803fb1a..63e68118066 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -481,37 +481,22 @@ RSpec.describe Admin::UsersController do end describe 'PUT ban/:id', :aggregate_failures do - context 'when ban_user_feature_flag is enabled' do - it 'bans user' do - put :ban, params: { id: user.username } - - expect(user.reload.banned?).to be_truthy - expect(flash[:notice]).to eq _('Successfully banned') - end - - context 'when unsuccessful' do - let(:user) { create(:user, :blocked) } + it 'bans user' do + put :ban, params: { id: user.username } - it 'does not ban user' do - put :ban, params: { id: user.username } - - user.reload - expect(user.banned?).to be_falsey - expect(flash[:alert]).to eq _('Error occurred. User was not banned') - end - end + expect(user.reload.banned?).to be_truthy + expect(flash[:notice]).to eq _('Successfully banned') end - context 'when ban_user_feature_flag is not enabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end + context 'when unsuccessful' do + let(:user) { create(:user, :blocked) } - it 'does not ban user, renders 404' do + it 'does not ban user' do put :ban, params: { id: user.username } - expect(user.reload.banned?).to be_falsey - expect(response).to have_gitlab_http_status(:not_found) + user.reload + expect(user.banned?).to be_falsey + expect(flash[:alert]).to eq _('Error occurred. User was not banned') end end end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index e9b39d44e46..97729d181b1 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -6,7 +6,7 @@ RSpec.describe AutocompleteController do let(:project) { create(:project) } let(:user) { project.first_owner } - context 'GET users' do + context 'GET users', feature_category: :user_management do let!(:user2) { create(:user) } let!(:non_member) { create(:user) } @@ -248,7 +248,7 @@ RSpec.describe AutocompleteController do end end - context 'GET projects' do + context 'GET projects', feature_category: :projects do let(:authorized_project) { create(:project) } let(:authorized_search_project) { create(:project, name: 'rugged') } @@ -339,7 +339,7 @@ RSpec.describe AutocompleteController do end end - context 'GET award_emojis' do + context 'GET award_emojis', feature_category: :team_planning do let(:user2) { create(:user) } let!(:award_emoji1) { create_list(:award_emoji, 2, user: user, name: 'thumbsup') } let!(:award_emoji2) { create_list(:award_emoji, 1, user: user, name: 'thumbsdown') } @@ -377,7 +377,7 @@ RSpec.describe AutocompleteController do end end - context 'GET deploy_keys_with_owners' do + context 'GET deploy_keys_with_owners', feature_category: :continuous_delivery do let_it_be(:public_project) { create(:project, :public) } let_it_be(:user) { create(:user) } let_it_be(:deploy_key) { create(:deploy_key, user: user) } @@ -451,7 +451,7 @@ RSpec.describe AutocompleteController do end end - context 'Get merge_request_target_branches' do + context 'Get merge_request_target_branches', feature_category: :code_review_workflow do let!(:merge_request) { create(:merge_request, source_project: project, target_branch: 'feature') } context 'anonymous user' do diff --git a/spec/controllers/concerns/analytics/cycle_analytics/value_stream_actions_spec.rb b/spec/controllers/concerns/analytics/cycle_analytics/value_stream_actions_spec.rb new file mode 100644 index 00000000000..246119a8118 --- /dev/null +++ b/spec/controllers/concerns/analytics/cycle_analytics/value_stream_actions_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Analytics::CycleAnalytics::ValueStreamActions, type: :controller, +feature_category: :planning_analytics do + subject(:controller) do + Class.new(ApplicationController) do + include Analytics::CycleAnalytics::ValueStreamActions + + def call_namespace + namespace + end + end + end + + describe '#namespace' do + it 'raises NotImplementedError' do + expect { controller.new.call_namespace }.to raise_error(NotImplementedError) + end + end +end diff --git a/spec/controllers/concerns/product_analytics_tracking_spec.rb b/spec/controllers/concerns/product_analytics_tracking_spec.rb index 28b79a10624..12b4065b89c 100644 --- a/spec/controllers/concerns/product_analytics_tracking_spec.rb +++ b/spec/controllers/concerns/product_analytics_tracking_spec.rb @@ -2,22 +2,24 @@ require "spec_helper" -RSpec.describe ProductAnalyticsTracking, :snowplow do +RSpec.describe ProductAnalyticsTracking, :snowplow, feature_category: :product_analytics do include TrackingHelpers include SnowplowHelpers let(:user) { create(:user) } + let(:event_name) { 'an_event' } let!(:group) { create(:group) } before do allow(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event) + stub_const("#{described_class}::MIGRATED_EVENTS", ['an_event']) end controller(ApplicationController) do include ProductAnalyticsTracking skip_before_action :authenticate_user!, only: :show - track_event(:index, :show, name: 'g_analytics_valuestream', destinations: [:redis_hll, :snowplow], + track_event(:index, :show, name: 'an_event', destinations: [:redis_hll, :snowplow], conditions: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id } def index @@ -53,16 +55,16 @@ RSpec.describe ProductAnalyticsTracking, :snowplow do def expect_redis_hll_tracking expect(Gitlab::UsageDataCounters::HLLRedisCounter).to have_received(:track_event) - .with('g_analytics_valuestream', values: instance_of(String)) + .with(event_name, values: instance_of(String)) end def expect_snowplow_tracking(user) - context = Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'g_analytics_valuestream') + context = Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name) .to_context.to_json expect_snowplow_event( category: anything, - action: 'g_analytics_valuestream', + action: event_name, namespace: group, user: user, context: [context] @@ -89,7 +91,9 @@ RSpec.describe ProductAnalyticsTracking, :snowplow do context 'when FF is disabled' do before do - stub_feature_flags(route_hll_to_snowplow: false) + stub_const("#{described_class}::MIGRATED_EVENTS", []) + allow(Feature).to receive(:enabled?).and_call_original + allow(Feature).to receive(:enabled?).with('route_hll_to_snowplow', anything).and_return(false) end it 'doesnt track snowplow event' do diff --git a/spec/controllers/concerns/send_file_upload_spec.rb b/spec/controllers/concerns/send_file_upload_spec.rb index 0b24387483b..6acbff6e745 100644 --- a/spec/controllers/concerns/send_file_upload_spec.rb +++ b/spec/controllers/concerns/send_file_upload_spec.rb @@ -54,17 +54,18 @@ RSpec.describe SendFileUpload do FileUtils.rm_f(temp_file) end - shared_examples 'handles image resize requests' do + shared_examples 'handles image resize requests' do |mount| let(:headers) { double } let(:image_requester) { build(:user) } let(:image_owner) { build(:user) } + let(:width) { mount == :pwa_icon ? 192 : 64 } let(:params) do { attachment: 'avatar.png' } end before do allow(uploader).to receive(:image_safe_for_scaling?).and_return(true) - allow(uploader).to receive(:mounted_as).and_return(:avatar) + allow(uploader).to receive(:mounted_as).and_return(mount) allow(controller).to receive(:headers).and_return(headers) # both of these are valid cases, depending on whether we are dealing with @@ -99,11 +100,11 @@ RSpec.describe SendFileUpload do context 'with valid width parameter' do it 'renders OK with workhorse command header' do expect(controller).not_to receive(:send_file) - expect(controller).to receive(:params).at_least(:once).and_return(width: '64') + expect(controller).to receive(:params).at_least(:once).and_return(width: width.to_s) expect(controller).to receive(:head).with(:ok) expect(Gitlab::Workhorse).to receive(:send_scaled_image) - .with(a_string_matching('^(/.+|https://.+)'), 64, 'image/png') + .with(a_string_matching('^(/.+|https://.+)'), width, 'image/png') .and_return([Gitlab::Workhorse::SEND_DATA_HEADER, "send-scaled-img:faux"]) expect(headers).to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, "send-scaled-img:faux") @@ -168,7 +169,8 @@ RSpec.describe SendFileUpload do subject end - it_behaves_like 'handles image resize requests' + it_behaves_like 'handles image resize requests', :avatar + it_behaves_like 'handles image resize requests', :pwa_icon end context 'with inline image' do @@ -273,7 +275,8 @@ RSpec.describe SendFileUpload do end end - it_behaves_like 'handles image resize requests' + it_behaves_like 'handles image resize requests', :avatar + it_behaves_like 'handles image resize requests', :pwa_icon end context 'when CDN-enabled remote file is used' do diff --git a/spec/controllers/dashboard/projects_controller_spec.rb b/spec/controllers/dashboard/projects_controller_spec.rb index b4a4ac56fce..e8ee146a13a 100644 --- a/spec/controllers/dashboard/projects_controller_spec.rb +++ b/spec/controllers/dashboard/projects_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Dashboard::ProjectsController, :aggregate_failures do +RSpec.describe Dashboard::ProjectsController, :aggregate_failures, feature_category: :projects do include ExternalAuthorizationServiceHelpers let_it_be(:user) { create(:user) } diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index ea12b0c5ad7..304e08f40bd 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe DashboardController do +RSpec.describe DashboardController, feature_category: :code_review_workflow do context 'signed in' do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } @@ -46,6 +46,64 @@ RSpec.describe DashboardController do describe 'GET merge requests' do it_behaves_like 'issuables list meta-data', :merge_request, :merge_requests it_behaves_like 'issuables requiring filter', :merge_requests + + context 'when an ActiveRecord::QueryCanceled is raised' do + before do + allow_next_instance_of(Gitlab::IssuableMetadata) do |instance| + allow(instance).to receive(:data).and_raise(ActiveRecord::QueryCanceled) + end + end + + it 'sets :search_timeout_occurred' do + get :merge_requests, params: { author_id: user.id } + + expect(response).to have_gitlab_http_status(:ok) + expect(assigns(:search_timeout_occurred)).to eq(true) + end + + context 'rendering views' do + render_views + + it 'shows error message' do + get :merge_requests, params: { author_id: user.id } + + expect(response.body).to have_content('Too many results to display. Edit your search or add a filter.') + end + + it 'does not display MR counts in nav' do + get :merge_requests, params: { author_id: user.id } + + expect(response.body).to have_content('Open Merged Closed All') + expect(response.body).not_to have_content('Open 0 Merged 0 Closed 0 All 0') + end + end + + it 'logs the exception' do + expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original + + get :merge_requests, params: { author_id: user.id } + end + end + + context 'when an ActiveRecord::QueryCanceled is not raised' do + it 'does not set :search_timeout_occurred' do + get :merge_requests, params: { author_id: user.id } + + expect(response).to have_gitlab_http_status(:ok) + expect(assigns(:search_timeout_occurred)).to eq(nil) + end + + context 'rendering views' do + render_views + + it 'displays MR counts in nav' do + get :merge_requests, params: { author_id: user.id } + + expect(response.body).to have_content('Open 0 Merged 0 Closed 0 All 0') + expect(response.body).not_to have_content('Open Merged Closed All') + end + end + end end end @@ -53,9 +111,9 @@ RSpec.describe DashboardController do include DesignManagementTestHelpers render_views - let(:user) { create(:user) } - let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) } - let(:other_project) { create(:project, :public) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) } + let_it_be(:other_project) { create(:project, :public) } before do enable_design_management @@ -76,22 +134,53 @@ RSpec.describe DashboardController do other_project.add_developer(user) end - it 'returns count' do - get :activity, params: { format: :json } + context 'without filter param' do + it 'returns only events of the user' do + get :activity, params: { format: :json } + + expect(json_response['count']).to eq(3) + end + end + + context 'with "projects" filter' do + it 'returns events of the user\'s projects' do + get :activity, params: { format: :json, filter: :projects } - expect(json_response['count']).to eq(6) + expect(json_response['count']).to eq(6) + end + end + + context 'with "followed" filter' do + let_it_be(:followed_user) { create(:user) } + let_it_be(:followed_user_private_project) { create(:project, :private) } + let_it_be(:followed_user_public_project) { create(:project, :public) } + + before do + followed_user_private_project.add_developer(followed_user) + followed_user_public_project.add_developer(followed_user) + user.follow(followed_user) + create(:event, :created, project: followed_user_private_project, target: create(:issue), + author: followed_user) + create(:event, :created, project: followed_user_public_project, target: create(:issue), author: followed_user) + end + + it 'returns public events of the user\'s followed users' do + get :activity, params: { format: :json, filter: :followed } + + expect(json_response['count']).to eq(1) + end end end context 'when user has no permission to see the event' do it 'filters out invisible event' do - get :activity, params: { format: :json } + get :activity, params: { format: :json, filter: :projects } expect(json_response['html']).to include(_('No activities found')) end it 'filters out invisible event when calculating the count' do - get :activity, params: { format: :json } + get :activity, params: { format: :json, filter: :projects } expect(json_response['count']).to eq(0) end diff --git a/spec/controllers/explore/projects_controller_spec.rb b/spec/controllers/explore/projects_controller_spec.rb index a79d9fa1276..c4f0feb21e2 100644 --- a/spec/controllers/explore/projects_controller_spec.rb +++ b/spec/controllers/explore/projects_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Explore::ProjectsController do +RSpec.describe Explore::ProjectsController, feature_category: :projects do shared_examples 'explore projects' do let(:expected_default_sort) { 'latest_activity_desc' } diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index 75f281caa90..7aad67b01e8 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe GraphqlController do +RSpec.describe GraphqlController, feature_category: :integrations do include GraphqlHelpers # two days is enough to make timezones irrelevant @@ -329,11 +329,24 @@ RSpec.describe GraphqlController do expect(assigns(:context)[:request]).to eq request end + + it 'sets `context[:remove_deprecated]` to false by default' do + post :execute + + expect(assigns(:context)[:remove_deprecated]).to be false + end + + it 'sets `context[:remove_deprecated]` to true when `remove_deprecated` param is truthy' do + post :execute, params: { remove_deprecated: '1' } + + expect(assigns(:context)[:remove_deprecated]).to be true + end end describe 'Admin Mode' do - let(:admin) { create(:admin) } - let(:project) { create(:project) } + let_it_be(:admin) { create(:admin) } + let_it_be(:project) { create(:project) } + let(:graphql_query) { graphql_query_for('project', { 'fullPath' => project.full_path }, %w(id name)) } before do @@ -431,114 +444,4 @@ RSpec.describe GraphqlController do expect(log_payload.dig(:exception_object)).to eq(exception) end end - - describe 'removal of deprecated items' do - let(:mock_schema) do - Class.new(GraphQL::Schema) do - lazy_resolve ::Gitlab::Graphql::Lazy, :force - - query(Class.new(::Types::BaseObject) do - graphql_name 'Query' - - field :foo, GraphQL::Types::Boolean, - deprecated: { milestone: '0.1', reason: :renamed } - - field :bar, (Class.new(::Types::BaseEnum) do - graphql_name 'BarEnum' - - value 'FOOBAR', value: 'foobar', deprecated: { milestone: '0.1', reason: :renamed } - end) - - field :baz, GraphQL::Types::Boolean do - argument :arg, String, required: false, deprecated: { milestone: '0.1', reason: :renamed } - end - - def foo - false - end - - def bar - 'foobar' - end - - def baz(arg:) - false - end - end) - end - end - - before do - allow(GitlabSchema).to receive(:execute).and_wrap_original do |method, *args| - mock_schema.execute(*args) - end - end - - context 'without `remove_deprecated` param' do - let(:params) { { query: '{ foo bar baz(arg: "test") }' } } - - subject { post :execute, params: params } - - it "sets context's `remove_deprecated` value to false" do - subject - - expect(assigns(:context)[:remove_deprecated]).to be false - end - - it 'returns deprecated items in response' do - subject - - expect(json_response).to include('data' => { 'foo' => false, 'bar' => 'FOOBAR', 'baz' => false }) - end - end - - context 'with `remove_deprecated` param' do - let(:params) { { remove_deprecated: 'true' } } - - subject { post :execute, params: params } - - it "sets context's `remove_deprecated` value to true" do - subject - - expect(assigns(:context)[:remove_deprecated]).to be true - end - - it 'does not allow deprecated field' do - params[:query] = '{ foo }' - - subject - - expect(json_response).not_to include('data' => { 'foo' => false }) - expect(json_response).to include( - 'errors' => include(a_hash_including('message' => /Field 'foo' doesn't exist on type 'Query'/)) - ) - end - - it 'does not allow deprecated enum value' do - params[:query] = '{ bar }' - - subject - - expect(json_response).not_to include('data' => { 'bar' => 'FOOBAR' }) - expect(json_response).to include( - 'errors' => include( - a_hash_including( - 'message' => /`Query.bar` returned `"foobar"` at `bar`, but this isn't a valid value for `BarEnum`/ - ) - ) - ) - end - - it 'does not allow deprecated argument' do - params[:query] = '{ baz(arg: "test") }' - - subject - - expect(json_response).not_to include('data' => { 'bar' => 'FOOBAR' }) - expect(json_response).to include( - 'errors' => include(a_hash_including('message' => /Field 'baz' doesn't accept argument 'arg'/)) - ) - end - end - end end diff --git a/spec/controllers/groups/children_controller_spec.rb b/spec/controllers/groups/children_controller_spec.rb index f05551432fa..d0656ee47ce 100644 --- a/spec/controllers/groups/children_controller_spec.rb +++ b/spec/controllers/groups/children_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Groups::ChildrenController do +RSpec.describe Groups::ChildrenController, feature_category: :subgroups do include ExternalAuthorizationServiceHelpers let(:group) { create(:group, :public) } diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index 46f507c34ba..01ea7101f2e 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Groups::ClustersController do +RSpec.describe Groups::ClustersController, feature_category: :kubernetes_management do include AccessMatchersForController include GoogleApi::CloudPlatformHelpers diff --git a/spec/controllers/groups/labels_controller_spec.rb b/spec/controllers/groups/labels_controller_spec.rb index 0521c5e02a8..916b2cf10dd 100644 --- a/spec/controllers/groups/labels_controller_spec.rb +++ b/spec/controllers/groups/labels_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Groups::LabelsController do +RSpec.describe Groups::LabelsController, feature_category: :team_planning do let_it_be(:group) { create(:group) } let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, namespace: group) } diff --git a/spec/controllers/groups/runners_controller_spec.rb b/spec/controllers/groups/runners_controller_spec.rb index 93c1571bb6c..1a60f7d824e 100644 --- a/spec/controllers/groups/runners_controller_spec.rb +++ b/spec/controllers/groups/runners_controller_spec.rb @@ -30,7 +30,6 @@ RSpec.describe Groups::RunnersController, feature_category: :runner_fleet do expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:index) - expect(assigns(:group_runners_limited_count)).to be(2) end it 'tracks the event' do diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 22a406b3197..9184cd2263e 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe GroupsController, factory_default: :keep do +RSpec.describe GroupsController, factory_default: :keep, feature_category: :code_review_workflow do include ExternalAuthorizationServiceHelpers include AdminModeHelper @@ -552,6 +552,68 @@ RSpec.describe GroupsController, factory_default: :keep do expect(assigns(:merge_requests)).to eq [merge_request_2, merge_request_1] end end + + context 'rendering views' do + render_views + + it 'displays MR counts in nav' do + get :merge_requests, params: { id: group.to_param } + + expect(response.body).to have_content('Open 2 Merged 0 Closed 0 All 2') + expect(response.body).not_to have_content('Open Merged Closed All') + end + + context 'when MergeRequestsFinder raises an exception' do + before do + allow_next_instance_of(MergeRequestsFinder) do |instance| + allow(instance).to receive(:count_by_state).and_raise(ActiveRecord::QueryCanceled) + end + end + + it 'does not display MR counts in nav' do + get :merge_requests, params: { id: group.to_param } + + expect(response.body).to have_content('Open Merged Closed All') + expect(response.body).not_to have_content('Open 0 Merged 0 Closed 0 All 0') + end + end + end + + context 'when an ActiveRecord::QueryCanceled is raised' do + before do + allow_next_instance_of(Gitlab::IssuableMetadata) do |instance| + allow(instance).to receive(:data).and_raise(ActiveRecord::QueryCanceled) + end + end + + it 'sets :search_timeout_occurred' do + get :merge_requests, params: { id: group.to_param } + + expect(response).to have_gitlab_http_status(:ok) + expect(assigns(:search_timeout_occurred)).to eq(true) + end + + it 'logs the exception' do + get :merge_requests, params: { id: group.to_param } + end + + context 'rendering views' do + render_views + + it 'shows error message' do + get :merge_requests, params: { id: group.to_param } + + expect(response.body).to have_content('Too many results to display. Edit your search or add a filter.') + end + + it 'does not display MR counts in nav' do + get :merge_requests, params: { id: group.to_param } + + expect(response.body).to have_content('Open Merged Closed All') + expect(response.body).not_to have_content('Open 0 Merged 0 Closed 0 All 0') + end + end + end end describe 'DELETE #destroy' do diff --git a/spec/controllers/import/bulk_imports_controller_spec.rb b/spec/controllers/import/bulk_imports_controller_spec.rb index a0d5b576e74..a3992ae850e 100644 --- a/spec/controllers/import/bulk_imports_controller_spec.rb +++ b/spec/controllers/import/bulk_imports_controller_spec.rb @@ -9,14 +9,12 @@ RSpec.describe Import::BulkImportsController, feature_category: :importers do stub_application_setting(bulk_import_enabled: true) sign_in(user) + + allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(false) end context 'when user is signed in' do context 'when bulk_import feature flag is enabled' do - before do - stub_feature_flags(bulk_import: true) - end - describe 'POST configure' do before do allow_next_instance_of(BulkImports::Clients::HTTP) do |instance| @@ -400,6 +398,18 @@ RSpec.describe Import::BulkImportsController, feature_category: :importers do expect(response).to have_gitlab_http_status(:unprocessable_entity) end end + + context 'when request exceeds rate limits' do + it 'prevents user from starting a new migration' do + allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true) + + post :create, params: { bulk_import: {} } + + request + + expect(response).to have_gitlab_http_status(:too_many_requests) + end + end end end diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index c1a61a78d80..406a3604b23 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Import::GithubController, feature_category: :import do +RSpec.describe Import::GithubController, feature_category: :importers do include ImportSpecHelper let(:provider) { :github } @@ -375,7 +375,9 @@ RSpec.describe Import::GithubController, feature_category: :import do it_behaves_like 'a GitHub-ish import controller: GET realtime_changes' it 'includes stats in response' do - create(:project, import_type: provider, namespace: user.namespace, import_status: :finished, import_source: 'example/repo') + project = create(:project, import_type: provider, namespace: user.namespace, import_status: :finished, import_source: 'example/repo') + + ::Gitlab::GithubImport::ObjectCounter.increment(project, :issue, :imported, value: 8) get :realtime_changes @@ -417,4 +419,56 @@ RSpec.describe Import::GithubController, feature_category: :import do end end end + + describe 'POST cancel_all' do + context 'when import is in progress' do + it 'returns success' do + project = create(:project, :import_scheduled, namespace: user.namespace, import_type: 'github', import_url: 'https://fake.url') + project2 = create(:project, :import_started, namespace: user.namespace, import_type: 'github', import_url: 'https://fake2.url') + + expect(Import::Github::CancelProjectImportService) + .to receive(:new).with(project, user) + .and_return(double(execute: { status: :success, project: project })) + + expect(Import::Github::CancelProjectImportService) + .to receive(:new).with(project2, user) + .and_return(double(execute: { status: :bad_request, message: 'The import cannot be canceled because it is finished' })) + + post :cancel_all + + expect(json_response).to eq([ + { + 'id' => project.id, + 'status' => 'success' + }, + { + 'id' => project2.id, + 'status' => 'bad_request', + 'error' => 'The import cannot be canceled because it is finished' + } + ]) + end + end + + context 'when there is no imports in progress' do + it 'returns an empty array' do + create(:project, :import_finished, namespace: user.namespace, import_type: 'github', import_url: 'https://fake.url') + + post :cancel_all + + expect(json_response).to eq([]) + end + end + + context 'when there is no projects created by user' do + it 'returns an empty array' do + other_user_project = create(:project, :import_started, import_type: 'github', import_url: 'https://fake.url') + + post :cancel_all + + expect(json_response).to eq([]) + expect(other_user_project.import_status).to eq('started') + end + end + end end diff --git a/spec/controllers/profiles/accounts_controller_spec.rb b/spec/controllers/profiles/accounts_controller_spec.rb index 1b4b67eeaff..ba349768b0f 100644 --- a/spec/controllers/profiles/accounts_controller_spec.rb +++ b/spec/controllers/profiles/accounts_controller_spec.rb @@ -31,7 +31,7 @@ RSpec.describe Profiles::AccountsController do end end - [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :authentiq, :dingtalk, :alicloud].each do |provider| + [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :dingtalk, :alicloud].each do |provider| describe "#{provider} provider" do let(:user) { create(:omniauth_user, provider: provider.to_s) } diff --git a/spec/controllers/profiles/two_factor_auths_controller_spec.rb b/spec/controllers/profiles/two_factor_auths_controller_spec.rb index 1dd564427d3..7d7cdededdb 100644 --- a/spec/controllers/profiles/two_factor_auths_controller_spec.rb +++ b/spec/controllers/profiles/two_factor_auths_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Profiles::TwoFactorAuthsController do +RSpec.describe Profiles::TwoFactorAuthsController, feature_category: :authentication_and_authorization do before do # `user` should be defined within the action-specific describe blocks sign_in(user) @@ -31,7 +31,7 @@ RSpec.describe Profiles::TwoFactorAuthsController do shared_examples 'user must enter a valid current password' do let(:current_password) { '123' } - let(:error_message) { { message: _('You must provide a valid current password') } } + let(:error_message) { { message: _('You must provide a valid current password.') } } it 'requires the current password', :aggregate_failures do go @@ -154,7 +154,7 @@ RSpec.describe Profiles::TwoFactorAuthsController do context 'with valid pin' do before do - expect(user).to receive(:validate_and_consume_otp!).with(pin).and_return(true) + allow(user).to receive(:validate_and_consume_otp!).with(pin).and_return(true) end it 'enables 2fa for the user' do @@ -187,6 +187,64 @@ RSpec.describe Profiles::TwoFactorAuthsController do it 'renders create' do go expect(response).to render_template(:create) + expect(user.otp_backup_codes?).to be_eql(true) + end + + it 'do not create new backup codes if exists' do + expect(user).to receive(:otp_backup_codes?).and_return(true) + go + expect(response).to redirect_to(profile_two_factor_auth_path) + end + + it 'calls to delete other sessions when backup codes already exist' do + expect(user).to receive(:otp_backup_codes?).and_return(true) + expect(ActiveSession).to receive(:destroy_all_but_current) + go + end + + context 'when webauthn_without_totp flag is disabled' do + before do + stub_feature_flags(webauthn_without_totp: false) + expect(user).to receive(:validate_and_consume_otp!).with(pin).and_return(true) + end + + it 'enables 2fa for the user' do + go + + user.reload + expect(user).to be_two_factor_enabled + end + + it 'presents plaintext codes for the user to save' do + expect(user).to receive(:generate_otp_backup_codes!).and_return(%w(a b c)) + + go + + expect(assigns[:codes]).to match_array %w(a b c) + end + + it 'calls to delete other sessions' do + expect(ActiveSession).to receive(:destroy_all_but_current) + + go + end + + it 'dismisses the `TWO_FACTOR_AUTH_RECOVERY_SETTINGS_CHECK` callout' do + expect(controller.helpers).to receive(:dismiss_two_factor_auth_recovery_settings_check) + + go + end + + it 'renders create' do + go + expect(response).to render_template(:create) + end + + it 'renders create even if backup code already exists' do + expect(user).to receive(:otp_backup_codes?).and_return(true) + go + expect(response).to render_template(:create) + end end end @@ -254,6 +312,75 @@ RSpec.describe Profiles::TwoFactorAuthsController do end end + describe 'POST create_webauthn' do + let_it_be_with_reload(:user) { create(:user) } + let(:client) { WebAuthn::FakeClient.new('http://localhost', encoding: :base64) } + let(:credential) { create_credential(client: client, rp_id: request.host) } + + let(:params) { { device_registration: { name: 'touch id', device_response: device_response } } } # rubocop:disable Rails/SaveBang + + let(:params_with_password) { { device_registration: { name: 'touch id', device_response: device_response }, current_password: user.password } } # rubocop:disable Rails/SaveBang + + before do + session[:challenge] = challenge + end + + def go + post :create_webauthn, params: params + end + + def challenge + @_challenge ||= begin + options_for_create = WebAuthn::Credential.options_for_create( + user: { id: user.webauthn_xid, name: user.username }, + authenticator_selection: { user_verification: 'discouraged' }, + rp: { name: 'GitLab' } + ) + options_for_create.challenge + end + end + + def device_response + client.create(challenge: challenge).to_json # rubocop:disable Rails/SaveBang + end + + it 'update failed_attempts when proper password is not given' do + go + expect(user.failed_attempts).to be_eql(1) + end + + context "when valid password is given" do + it "registers and render OTP backup codes" do + post :create_webauthn, params: params_with_password + expect(user.otp_backup_codes).not_to be_empty + expect(flash[:notice]).to match(/Your WebAuthn device was registered!/) + end + + it 'registers and redirects back if user is already having backup codes' do + expect(user).to receive(:otp_backup_codes?).and_return(true) + post :create_webauthn, params: params_with_password + expect(response).to redirect_to(profile_two_factor_auth_path) + expect(flash[:notice]).to match(/Your WebAuthn device was registered!/) + end + end + + context "when the feature flag 'webauthn_without_totp' is disabled" do + before do + stub_feature_flags(webauthn_without_totp: false) + session[:challenge] = challenge + end + + let(:params) { { device_registration: { name: 'touch id', device_response: device_response } } } # rubocop:disable Rails/SaveBang + + it "does not validate the current_password" do + go + + expect(flash[:notice]).to match(/Your WebAuthn device was registered!/) + expect(response).to redirect_to(profile_two_factor_auth_path) + end + end + end + describe 'DELETE destroy' do def go delete :destroy, params: { current_password: current_password } diff --git a/spec/controllers/profiles_controller_spec.rb b/spec/controllers/profiles_controller_spec.rb index aa92ff6be33..daf0f36c28b 100644 --- a/spec/controllers/profiles_controller_spec.rb +++ b/spec/controllers/profiles_controller_spec.rb @@ -126,6 +126,16 @@ RSpec.describe ProfilesController, :request_store do expect(user.reload.pronunciation).to eq(pronunciation) expect(response).to have_gitlab_http_status(:found) end + + it 'allows updating user specified Discord User ID', :aggregate_failures do + discord_user_id = '1234567890123456789' + sign_in(user) + + put :update, params: { user: { discord: discord_user_id } } + + expect(user.reload.discord).to eq(discord_user_id) + expect(response).to have_gitlab_http_status(:found) + end end describe 'GET audit_log' do diff --git a/spec/controllers/projects/analytics/cycle_analytics/value_streams_controller_spec.rb b/spec/controllers/projects/analytics/cycle_analytics/value_streams_controller_spec.rb index 5b434eb2011..a3a86138f18 100644 --- a/spec/controllers/projects/analytics/cycle_analytics/value_streams_controller_spec.rb +++ b/spec/controllers/projects/analytics/cycle_analytics/value_streams_controller_spec.rb @@ -30,6 +30,20 @@ RSpec.describe Projects::Analytics::CycleAnalytics::ValueStreamsController do expect(json_response.first['name']).to eq('default') end + + # testing the authorize method within ValueStreamActions + context 'when issues and merge requests are disabled' do + it 'renders 404' do + project.project_feature.update!( + issues_access_level: ProjectFeature::DISABLED, + merge_requests_access_level: ProjectFeature::DISABLED + ) + + get :index, params: params + + expect(response).to have_gitlab_http_status(:not_found) + end + end end context 'when user is not member of the project' do diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb index 3d12926c07a..32cd10d9805 100644 --- a/spec/controllers/projects/artifacts_controller_spec.rb +++ b/spec/controllers/projects/artifacts_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::ArtifactsController do +RSpec.describe Projects::ArtifactsController, feature_category: :build_artifacts do include RepoHelpers let(:user) { project.first_owner } @@ -37,23 +37,6 @@ RSpec.describe Projects::ArtifactsController do expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template('projects/artifacts/index') - - app = Nokogiri::HTML.parse(response.body).at_css('div#js-artifact-management') - - expect(app.attributes['data-project-path'].value).to eq(project.full_path) - expect(app.attributes['data-can-destroy-artifacts'].value).to eq('true') - end - - describe 'when user does not have permission to delete artifacts' do - let(:user) { create(:user) } - - it 'passes false to the artifacts app' do - subject - - app = Nokogiri::HTML.parse(response.body).at_css('div#js-artifact-management') - - expect(app.attributes['data-can-destroy-artifacts'].value).to eq('false') - end end end @@ -127,7 +110,7 @@ RSpec.describe Projects::ArtifactsController do end context 'when no file type is supplied' do - let(:filename) { job.artifacts_file.filename } + let(:filename) { job.job_artifacts_archive.filename } it 'sends the artifacts file' do expect(controller).to receive(:send_file) @@ -141,6 +124,38 @@ RSpec.describe Projects::ArtifactsController do end end + context 'when artifact is set as private' do + let(:filename) { job.artifacts_file.filename } + + before do + job.job_artifacts.update_all(accessibility: 'private') + end + + context 'and user is not authoirized' do + let(:user) { create(:user) } + + it 'returns forbidden' do + download_artifact(file_type: 'archive') + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'and user has access to project' do + it 'downloads' do + expect(controller).to receive(:send_file) + .with( + job.artifacts_file.file.path, + hash_including(disposition: 'attachment', filename: filename)).and_call_original + + download_artifact(file_type: 'archive') + + expect(response).to have_gitlab_http_status(:ok) + expect(response.headers['Content-Disposition']).to eq(%Q(attachment; filename="#{filename}"; filename*=UTF-8''#{filename})) + end + end + end + context 'when a file type is supplied' do context 'when an invalid file type is supplied' do let(:file_type) { 'invalid' } @@ -255,10 +270,14 @@ RSpec.describe Projects::ArtifactsController do end context 'when the user has update_build permissions' do + let(:filename) { job.job_artifacts_trace.file.filename } + it 'sends the trace' do download_artifact(file_type: file_type) expect(response).to have_gitlab_http_status(:ok) + expect(response.headers['Content-Disposition']) + .to eq(%Q(attachment; filename="#{filename}"; filename*=UTF-8''#{filename})) end end end diff --git a/spec/controllers/projects/autocomplete_sources_controller_spec.rb b/spec/controllers/projects/autocomplete_sources_controller_spec.rb index 7077aae6b45..70178083e71 100644 --- a/spec/controllers/projects/autocomplete_sources_controller_spec.rb +++ b/spec/controllers/projects/autocomplete_sources_controller_spec.rb @@ -4,10 +4,14 @@ require 'spec_helper' RSpec.describe Projects::AutocompleteSourcesController do let_it_be(:group, reload: true) { create(:group) } + let_it_be(:private_group) { create(:group, :private) } let_it_be(:project) { create(:project, namespace: group) } let_it_be(:public_project) { create(:project, :public, group: group) } let_it_be(:development) { create(:label, project: project, name: 'Development') } - let_it_be(:issue) { create(:labeled_issue, project: project, labels: [development]) } + let_it_be(:private_issue) { create(:labeled_issue, project: project, labels: [development]) } + let_it_be(:private_work_item) { create(:work_item, project: project) } + let_it_be(:issue) { create(:labeled_issue, project: public_project, labels: [development]) } + let_it_be(:work_item) { create(:work_item, project: public_project, id: 1, iid: 100) } let_it_be(:user) { create(:user) } def members_by_username(username) @@ -22,7 +26,7 @@ RSpec.describe Projects::AutocompleteSourcesController do context 'with a public project' do shared_examples 'issuable commands' do it 'returns empty array when no user logged in' do - get :commands, format: :json, params: { namespace_id: group.path, project_id: public_project.path, type: issuable_type, type_id: issuable_iid } + get :commands, format: :json, params: { namespace_id: group.path, project_id: public_project.path, type: issuable_type } expect(response).to have_gitlab_http_status(:ok) expect(json_response).to eq([]) @@ -52,6 +56,13 @@ RSpec.describe Projects::AutocompleteSourcesController do it_behaves_like 'issuable commands' end + context 'with work items' do + let(:issuable_type) { work_item.class.name } + let(:issuable_iid) { work_item.iid } + + it_behaves_like 'issuable commands' + end + context 'with merge request' do let(:merge_request) { create(:merge_request, target_project: public_project, source_project: public_project) } let(:issuable_type) { merge_request.class.name } @@ -68,24 +79,49 @@ RSpec.describe Projects::AutocompleteSourcesController do sign_in(user) end - it 'raises an error when no target type specified' do - expect { get :labels, format: :json, params: { namespace_id: group.path, project_id: project.path } } - .to raise_error(ActionController::ParameterMissing) + shared_examples 'label commands' do + it 'raises an error when no target type specified' do + expect { get :labels, format: :json, params: { namespace_id: group.path, project_id: project.path } } + .to raise_error(ActionController::ParameterMissing) + end + + it 'returns an array of labels' do + get :labels, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issuable_type } + + expect(json_response).to be_a(Array) + expect(json_response.count).to eq(1) + expect(json_response[0]['title']).to eq('Development') + end + end + + context 'with issues' do + let(:issuable_type) { issue.class.name } + let(:issuable_iid) { issue.iid } + + it_behaves_like 'label commands' end - it 'returns an array of labels' do - get :labels, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + context 'with work items' do + let(:issuable_type) { work_item.class.name } + let(:issuable_iid) { work_item.iid } - expect(json_response).to be_a(Array) - expect(json_response.count).to eq(1) - expect(json_response[0]['title']).to eq('Development') + it_behaves_like 'label commands' end end describe 'GET members' do + let_it_be(:invited_private_member) { create(:user) } + let_it_be(:issue) { create(:labeled_issue, project: public_project, labels: [development], author: user) } + let_it_be(:work_item) { create(:work_item, project: public_project, author: user) } + + before_all do + create(:project_group_link, group: private_group, project: public_project) + group.add_owner(user) + private_group.add_developer(invited_private_member) + end + context 'when logged in' do before do - group.add_owner(user) sign_in(user) end @@ -94,42 +130,88 @@ RSpec.describe Projects::AutocompleteSourcesController do .to raise_error(ActionController::ParameterMissing) end - it 'returns an array of member object' do - get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + shared_examples 'all members are returned' do + it 'returns an array of member object' do + get :members, format: :json, params: { namespace_id: group.path, project_id: public_project.path, type: issuable_type } + + expect(members_by_username('all').symbolize_keys).to include( + username: 'all', + name: 'All Project and Group Members', + count: 2) + + expect(members_by_username(group.full_path).symbolize_keys).to include( + type: group.class.name, + name: group.full_name, + avatar_url: group.avatar_url, + count: 1) + + expect(members_by_username(user.username).symbolize_keys).to include( + type: user.class.name, + name: user.name, + avatar_url: user.avatar_url) + + expect(members_by_username(invited_private_member.username).symbolize_keys).to include( + type: invited_private_member.class.name, + name: invited_private_member.name, + avatar_url: invited_private_member.avatar_url) + end + end - expect(members_by_username('all').symbolize_keys).to include( - username: 'all', - name: 'All Project and Group Members', - count: 1) + context 'with issue' do + let(:issuable_type) { issue.class.name } - expect(members_by_username(group.full_path).symbolize_keys).to include( - type: group.class.name, - name: group.full_name, - avatar_url: group.avatar_url, - count: 1) + it_behaves_like 'all members are returned' + end - expect(members_by_username(user.username).symbolize_keys).to include( - type: user.class.name, - name: user.name, - avatar_url: user.avatar_url) + context 'with work item' do + let(:issuable_type) { work_item.class.name } + + it_behaves_like 'all members are returned' end end context 'when anonymous' do - it 'redirects to login page' do - get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + shared_examples 'private project is inaccessible' do + it 'redirects to login page for private project' do + get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issuable_type } - expect(response).to redirect_to new_user_session_path + expect(response).to redirect_to new_user_session_path + end end - context 'with public project' do - it 'returns no members' do - get :members, format: :json, params: { namespace_id: group.path, project_id: public_project.path, type: issue.class.name, type_id: issue.id } + shared_examples 'only public members are returned for public project' do + it 'only returns public members' do + get :members, format: :json, params: { namespace_id: group.path, project_id: public_project.path, type: issuable_type } - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_a(Array) - expect(json_response.count).to eq(1) - expect(json_response.first['count']).to eq(0) + expect(members_by_username('all').symbolize_keys).to include( + username: 'all', + name: 'All Project and Group Members', + count: 1) + + expect(members_by_username(user.username).symbolize_keys).to include( + type: user.class.name, + name: user.name, + avatar_url: user.avatar_url) + end + end + + context 'with issue' do + it_behaves_like 'private project is inaccessible' do + let(:issuable_type) { private_issue.class.name } + end + + it_behaves_like 'only public members are returned for public project' do + let(:issuable_type) { issue.class.name } + end + end + + context 'with work item' do + it_behaves_like 'private project is inaccessible' do + let(:issuable_type) { private_work_item.class.name } + end + + it_behaves_like 'only public members are returned for public project' do + let(:issuable_type) { work_item.class.name } end end end @@ -184,7 +266,7 @@ RSpec.describe Projects::AutocompleteSourcesController do it 'lists contacts' do group.add_developer(user) - get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name } emails = json_response.map { |contact_data| contact_data["email"] } expect(emails).to match_array([contact_1.email, contact_2.email]) @@ -193,7 +275,7 @@ RSpec.describe Projects::AutocompleteSourcesController do context 'when a user can not read contacts' do it 'renders 404' do - get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name } expect(response).to have_gitlab_http_status(:not_found) end @@ -204,7 +286,7 @@ RSpec.describe Projects::AutocompleteSourcesController do it 'renders 404' do group.add_developer(user) - get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name } expect(response).to have_gitlab_http_status(:not_found) end diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index ed11d5936b0..dcde22c1fd6 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::BranchesController do +RSpec.describe Projects::BranchesController, feature_category: :source_code_management do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:developer) { create(:user) } @@ -676,6 +676,18 @@ RSpec.describe Projects::BranchesController do end end + context 'when state is not supported' do + before do + get :index, format: :html, params: { + namespace_id: project.namespace, project_id: project, state: 'unknown' + } + end + + it 'returns 404 page' do + expect(response).to have_gitlab_http_status(:not_found) + end + end + context 'sorting', :aggregate_failures do let(:sort) { 'name_asc' } diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 894f0f8354d..a4f7c92f5cd 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::ClustersController do +RSpec.describe Projects::ClustersController, feature_category: :kubernetes_management do include AccessMatchersForController include GoogleApi::CloudPlatformHelpers include KubernetesHelpers diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb index edb07bbdce6..8d3939d8133 100644 --- a/spec/controllers/projects/commit_controller_spec.rb +++ b/spec/controllers/projects/commit_controller_spec.rb @@ -36,6 +36,7 @@ RSpec.describe Projects::CommitController do go(id: commit.id) expect(response).to be_ok + expect(assigns(:ref)).to eq commit.id end context 'when a pipeline job is running' do @@ -57,6 +58,7 @@ RSpec.describe Projects::CommitController do go(id: commit.id.reverse) expect(response).to be_not_found + expect(assigns(:ref)).to be_nil end end diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index 26d4725656f..67aa82dacbb 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::CommitsController do +RSpec.describe Projects::CommitsController, feature_category: :source_code_management do let(:project) { create(:project, :repository) } let(:user) { create(:user) } diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index dddefbac163..169fed1ab17 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::EnvironmentsController do +RSpec.describe Projects::EnvironmentsController, feature_category: :continuous_delivery do include MetricsDashboardHelpers include KubernetesHelpers @@ -103,6 +103,24 @@ RSpec.describe Projects::EnvironmentsController do expect(json_response['stopped_count']).to eq 1 end + context 'can access stop stale environments feature' do + it 'maintainers can access the feature' do + get :index, params: environment_params(format: :json) + + expect(json_response['can_stop_stale_environments']).to be_truthy + end + + context 'when user is a reporter' do + let(:user) { reporter } + + it 'reporters cannot access the feature' do + get :index, params: environment_params(format: :json) + + expect(json_response['can_stop_stale_environments']).to be_falsey + end + end + end + context 'when enable_environments_search_within_folder FF is disabled' do before do stub_feature_flags(enable_environments_search_within_folder: false) diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb index 962ef93dc72..25c722173c1 100644 --- a/spec/controllers/projects/forks_controller_spec.rb +++ b/spec/controllers/projects/forks_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::ForksController do +RSpec.describe Projects::ForksController, feature_category: :source_code_management do let(:user) { create(:user) } let(:project) { create(:project, :public, :repository) } let(:forked_project) { Projects::ForkService.new(project, user, name: 'Some name').execute } diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 31e297e5773..9c272872a73 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::IssuesController do +RSpec.describe Projects::IssuesController, feature_category: :team_planning do include ProjectForksHelper include_context 'includes Spam constants' @@ -647,9 +647,8 @@ RSpec.describe Projects::IssuesController do end end - context 'when allow_possible_spam feature flag is false' do + context 'when allow_possible_spam application setting is false' do before do - stub_feature_flags(allow_possible_spam: false) expect(controller).to(receive(:spam_action_response_fields).with(issue)) do spam_action_response_fields end @@ -662,7 +661,11 @@ RSpec.describe Projects::IssuesController do end end - context 'when allow_possible_spam feature flag is true' do + context 'when allow_possible_spam application setting is true' do + before do + stub_application_setting(allow_possible_spam: true) + end + it 'updates the issue' do subject @@ -887,11 +890,7 @@ RSpec.describe Projects::IssuesController do end end - context 'when allow_possible_spam feature flag is false' do - before do - stub_feature_flags(allow_possible_spam: false) - end - + context 'when allow_possible_spam application setting is false' do it 'rejects an issue recognized as spam' do expect { update_issue }.not_to change { issue.reload.title } end @@ -925,7 +924,11 @@ RSpec.describe Projects::IssuesController do end end - context 'when allow_possible_spam feature flag is true' do + context 'when allow_possible_spam application setting is true' do + before do + stub_application_setting(allow_possible_spam: true) + end + it 'updates the issue recognized as spam' do expect { update_issue }.to change { issue.reload.title } end @@ -1234,8 +1237,6 @@ RSpec.describe Projects::IssuesController do context 'when SpamVerdictService allows the issue' do before do - stub_feature_flags(allow_possible_spam: false) - expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service| expect(verdict_service).to receive(:execute).and_return(ALLOW) end @@ -1258,11 +1259,7 @@ RSpec.describe Projects::IssuesController do post_new_issue(title: 'Spam Title', description: 'Spam lives here') end - context 'when allow_possible_spam feature flag is false' do - before do - stub_feature_flags(allow_possible_spam: false) - end - + context 'when allow_possible_spam application setting is false' do it 'rejects an issue recognized as spam' do expect { post_spam_issue }.not_to change(Issue, :count) end @@ -1283,7 +1280,11 @@ RSpec.describe Projects::IssuesController do end end - context 'when allow_possible_spam feature flag is true' do + context 'when allow_possible_spam application setting is true' do + before do + stub_application_setting(allow_possible_spam: true) + end + it 'creates an issue recognized as spam' do expect { post_spam_issue }.to change(Issue, :count) end @@ -1377,13 +1378,13 @@ RSpec.describe Projects::IssuesController do end context 'when issue creation limits imposed' do - it 'prevents from creating more issues', :request_store do - post_new_issue - - expect { post_new_issue } - .to change { Gitlab::GitalyClient.get_request_count }.by(1) # creates 1 projects and 0 issues + before do + project.add_developer(user) + sign_in(user) + end - post_new_issue + it 'prevents from creating more issues', :request_store do + 2.times { post_new_issue_in_project } 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) @@ -1402,16 +1403,15 @@ RSpec.describe Projects::IssuesController do expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once - project.add_developer(user) - sign_in(user) + 2.times { post_new_issue_in_project } + end - 2.times do - post :create, params: { - namespace_id: project.namespace.to_param, - project_id: project, - issue: { title: 'Title', description: 'Description' } - } - end + def post_new_issue_in_project + post :create, params: { + namespace_id: project.namespace.to_param, + project_id: project, + issue: { title: 'Title', description: 'Description' } + } end end end diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 3dc89365530..8fb9623c21a 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do +RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state, feature_category: :continuous_integration do include ApiHelpers include HttpIOHelpers @@ -809,14 +809,48 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do sign_in(user) end + context 'when job is not retryable' do + context 'and the job is a bridge' do + let(:job) { create(:ci_bridge, :failed, :reached_max_descendant_pipelines_depth, pipeline: pipeline) } + + it 'renders unprocessable_entity' do + post_retry + + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end + end + + context 'and the job is a build' do + let(:job) { create(:ci_build, :deployment_rejected, pipeline: pipeline) } + + it 'renders unprocessable_entity' do + post_retry + + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end + end + end + context 'when job is retryable' do - let(:job) { create(:ci_build, :retryable, pipeline: pipeline) } + context 'and the job is a bridge' do + let(:job) { create(:ci_bridge, :retryable, pipeline: pipeline) } - it 'redirects to the retried job page' do - post_retry + it 'responds :ok' do + post_retry - expect(response).to have_gitlab_http_status(:found) - expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id)) + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'and the job is a build' do + let(:job) { create(:ci_build, :retryable, pipeline: pipeline) } + + it 'redirects to the retried job page' do + post_retry + + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id)) + end end shared_examples_for 'retried job has the same attributes' do @@ -847,16 +881,6 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do end end - context 'when job is not retryable' do - let(:job) { create(:ci_build, pipeline: pipeline) } - - it 'renders unprocessable_entity' do - post_retry - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - def post_retry post :retry, params: { namespace_id: project.namespace, diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index dfa6ed639b6..98982856d6c 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::LabelsController do +RSpec.describe Projects::LabelsController, feature_category: :team_planning do let_it_be(:group) { create(:group) } let_it_be(:project, reload: true) { create(:project, namespace: group) } let_it_be(:user) { create(:user) } diff --git a/spec/controllers/projects/learn_gitlab_controller_spec.rb b/spec/controllers/projects/learn_gitlab_controller_spec.rb deleted file mode 100644 index a93da82d948..00000000000 --- a/spec/controllers/projects/learn_gitlab_controller_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::LearnGitlabController do - describe 'GET #index' do - let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project, namespace: create(:group)) } - - let(:learn_gitlab_enabled) { true } - let(:params) { { namespace_id: project.namespace.to_param, project_id: project } } - - subject(:action) { get :index, params: params } - - before do - project.namespace.add_owner(user) - allow(controller.helpers).to receive(:learn_gitlab_enabled?).and_return(learn_gitlab_enabled) - end - - context 'unauthenticated user' do - it { is_expected.to have_gitlab_http_status(:redirect) } - end - - context 'authenticated user' do - before do - sign_in(user) - end - - it { is_expected.to render_template(:index) } - - context 'learn_gitlab experiment not enabled' do - let(:learn_gitlab_enabled) { false } - - it { is_expected.to have_gitlab_http_status(:not_found) } - end - - context 'with invite_for_help_continuous_onboarding experiment' do - it 'tracks the assignment', :experiment do - stub_experiments(invite_for_help_continuous_onboarding: true) - - expect(experiment(:invite_for_help_continuous_onboarding)) - .to track(:assignment).with_context(namespace: project.namespace).on_next_instance - - action - end - end - end - end -end diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb index 7db708e0e78..3d4a884587f 100644 --- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::MergeRequests::CreationsController do +RSpec.describe Projects::MergeRequests::CreationsController, feature_category: :code_review_workflow do let(:project) { create(:project, :repository) } let(:user) { project.first_owner } let(:fork_project) { create(:forked_project_with_submodules) } diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb index 4de724fd6d6..23a33d7e0b1 100644 --- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::MergeRequests::DiffsController do +RSpec.describe Projects::MergeRequests::DiffsController, feature_category: :code_review_workflow do include ProjectForksHelper include TrackingHelpers @@ -610,5 +610,39 @@ RSpec.describe Projects::MergeRequests::DiffsController do go end end + + context 'when ck param is present' do + let(:cache_key) { merge_request.merge_head_diff.id } + + before do + create(:merge_request_diff, :merge_head, merge_request: merge_request) + end + + it 'sets Cache-Control with max-age' do + go(ck: cache_key, diff_head: true) + + expect(response.headers['Cache-Control']).to eq('max-age=86400, private') + end + + context 'when diffs_batch_cache_with_max_age feature flag is disabled' do + before do + stub_feature_flags(diffs_batch_cache_with_max_age: false) + end + + it 'does not set Cache-Control with max-age' do + go(ck: cache_key, diff_head: true) + + expect(response.headers['Cache-Control']).not_to eq('max-age=86400, private') + end + end + + context 'when not rendering merge head diff' do + it 'does not set Cache-Control with max-age' do + go(ck: cache_key, diff_head: false) + + expect(response.headers['Cache-Control']).not_to eq('max-age=86400, private') + end + end + 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 182d654aaa8..39482938a8b 100644 --- a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb @@ -388,104 +388,72 @@ RSpec.describe Projects::MergeRequests::DraftsController do context 'publish with note' do before do + allow(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) + .to receive(:track_submit_review_comment) + 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') + it 'creates note' do + post :publish, params: params.merge!(note: 'Hello world') - expect(merge_request.notes.reload.size).to be(1) - end + expect(merge_request.notes.reload.size).to be(2) end - context 'when feature flag is enabled' do - before do - allow(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) - .to receive(:track_submit_review_comment) - end - - 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: '') - 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 + expect(merge_request.notes.reload.size).to be(1) + end - it 'tracks merge request activity' do - post :publish, params: params.merge!(note: 'Hello world') + it 'tracks merge request activity' do + post :publish, params: params.merge!(note: 'Hello world') - expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) - .to have_received(:track_submit_review_comment).with(user: user) - end + expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) + .to have_received(:track_submit_review_comment).with(user: user) end end context 'approve merge request' do before do + allow(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) + .to receive(:track_submit_review_approve) + 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 'approves merge request' do + post :publish, params: params.merge!(approve: true) - it 'does not approve' do - post :publish, params: params.merge!(approve: true) - - expect(merge_request.approvals.reload.size).to be(0) - end + expect(merge_request.approvals.reload.size).to be(1) end - context 'when feature flag is enabled' do - before do - allow(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) - .to receive(:track_submit_review_approve) - end + it 'does not approve merge request' do + post :publish, params: params.merge!(approve: false) - it 'approves merge request' do - post :publish, params: params.merge!(approve: true) + expect(merge_request.approvals.reload.size).to be(0) + end - expect(merge_request.approvals.reload.size).to be(1) - end + it 'tracks merge request activity' do + post :publish, params: params.merge!(approve: true) - it 'does not approve merge request' do - post :publish, params: params.merge!(approve: false) + expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) + .to have_received(:track_submit_review_approve).with(user: user) + end - expect(merge_request.approvals.reload.size).to be(0) + context 'when merge request is already approved by user' do + before do + create(:approval, merge_request: merge_request, user: user) end - it 'tracks merge request activity' do + it 'does return 200' do post :publish, params: params.merge!(approve: true) + expect(response).to have_gitlab_http_status(:ok) + expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) .to have_received(:track_submit_review_approve).with(user: user) end - - context 'when merge request is already approved by user' do - before do - create(:approval, merge_request: merge_request, user: user) - end - - it 'does return 200' do - post :publish, params: params.merge!(approve: true) - - expect(response).to have_gitlab_http_status(:ok) - - expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter) - .to have_received(:track_submit_review_approve).with(user: user) - end - end end end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 095775b0ddd..ceb3f803db5 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -68,6 +68,72 @@ RSpec.describe Projects::MergeRequestsController, feature_category: :code_review end end + context 'when add_prepared_state_to_mr feature flag on' do + before do + stub_feature_flags(add_prepared_state_to_mr: true) + end + + context 'when the merge request is not prepared' do + before do + merge_request.update!(prepared_at: nil, created_at: 10.minutes.ago) + end + + it 'prepares the merge request' do + expect(NewMergeRequestWorker).to receive(:perform_async) + + go + end + + context 'when the merge request was created less than 5 minutes ago' do + it 'does not prepare the merge request again' do + travel_to(4.minutes.from_now) do + merge_request.update!(created_at: Time.current - 4.minutes) + + expect(NewMergeRequestWorker).not_to receive(:perform_async) + + go + end + end + end + + context 'when the merge request was created 5 minutes ago' do + it 'prepares the merge request' do + travel_to(6.minutes.from_now) do + merge_request.update!(created_at: Time.current - 6.minutes) + + expect(NewMergeRequestWorker).to receive(:perform_async) + + go + end + end + end + end + + context 'when the merge request is prepared' do + before do + merge_request.update!(prepared_at: Time.current, created_at: 10.minutes.ago) + end + + it 'prepares the merge request' do + expect(NewMergeRequestWorker).not_to receive(:perform_async) + + go + end + end + end + + context 'when add_prepared_state_to_mr feature flag is off' do + before do + stub_feature_flags(add_prepared_state_to_mr: false) + end + + it 'does not prepare the merge request again' do + expect(NewMergeRequestWorker).not_to receive(:perform_async) + + go + end + end + describe 'as html' do it 'sets the endpoint_metadata_url' do go @@ -82,6 +148,61 @@ RSpec.describe Projects::MergeRequestsController, feature_category: :code_review w: '0')) end + context 'when merge_head diff is present' do + before do + create(:merge_request_diff, :merge_head, merge_request: merge_request) + end + + it 'sets the endpoint_diff_batch_url with ck' do + go + + expect(assigns["endpoint_diff_batch_url"]).to eq( + diffs_batch_project_json_merge_request_path( + project, + merge_request, + 'json', + diff_head: true, + view: 'inline', + w: '0', + page: '0', + per_page: '5', + ck: merge_request.merge_head_diff.id)) + end + + it 'sets diffs_batch_cache_key' do + go + + expect(assigns['diffs_batch_cache_key']).to eq(merge_request.merge_head_diff.id) + end + + context 'when diffs_batch_cache_with_max_age feature flag is disabled' do + before do + stub_feature_flags(diffs_batch_cache_with_max_age: false) + end + + it 'sets the endpoint_diff_batch_url without ck param' do + go + + expect(assigns['endpoint_diff_batch_url']).to eq( + diffs_batch_project_json_merge_request_path( + project, + merge_request, + 'json', + diff_head: true, + view: 'inline', + w: '0', + page: '0', + per_page: '5')) + end + + it 'does not set diffs_batch_cache_key' do + go + + expect(assigns['diffs_batch_cache_key']).to be_nil + end + end + end + context 'when diff files were cleaned' do render_views @@ -335,15 +456,6 @@ RSpec.describe Projects::MergeRequestsController, feature_category: :code_review end end end - - it_behaves_like 'issuable list with anonymous search disabled' do - let(:params) { { namespace_id: project.namespace, project_id: project } } - - before do - sign_out(user) - project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) - end - end end describe 'PUT update' do @@ -1856,18 +1968,26 @@ RSpec.describe Projects::MergeRequestsController, feature_category: :code_review } end - it 'shows a flash message on success' do + it 'displays an flash error message on fail' do + allow(MergeRequests::AssignIssuesService).to receive(:new).and_return(double(execute: { count: 0 })) + post_assign_issues - expect(flash[:notice]).to eq '2 issues have been assigned to you' + expect(flash[:alert]).to eq _('Failed to assign you issues related to the merge request.') end - it 'correctly pluralizes flash message on success' do + it 'shows a flash message on success' do issue2.assignees = [user] post_assign_issues - expect(flash[:notice]).to eq '1 issue has been assigned to you' + expect(flash[:notice]).to eq n_("An issue has been assigned to you.", "%d issues have been assigned to you.", 1) + end + + it 'correctly pluralizes flash message on success' do + post_assign_issues + + expect(flash[:notice]).to eq n_("An issue has been assigned to you.", "%d issues have been assigned to you.", 2) end it 'calls MergeRequests::AssignIssuesService' do diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 0afd2e10ea2..23b0b58158f 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::NotesController do +RSpec.describe Projects::NotesController, type: :controller, feature_category: :team_planning do include ProjectForksHelper let(:user) { create(:user) } diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb index 358b98621a5..a628c1ab230 100644 --- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb +++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::PipelineSchedulesController do +RSpec.describe Projects::PipelineSchedulesController, feature_category: :continuous_integration do include AccessMatchersForController let_it_be(:user) { create(:user) } diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 3d1d28945f7..4e0c098ad81 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::PipelinesController do +RSpec.describe Projects::PipelinesController, feature_category: :continuous_integration do include ApiHelpers let_it_be(:user) { create(:user) } @@ -52,21 +52,6 @@ RSpec.describe Projects::PipelinesController do expect(stages.count).to eq 3 end end - - it 'does not execute N+1 queries', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/345470' do - get_pipelines_index_json - - control_count = ActiveRecord::QueryRecorder.new do - get_pipelines_index_json - end.count - - create_all_pipeline_types - - # There appears to be one extra query for Pipelines#has_warnings? for some reason - expect { get_pipelines_index_json }.not_to exceed_query_limit(control_count + 1) - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['pipelines'].count).to eq 12 - end end it 'does not include coverage data for the pipelines' do @@ -83,14 +68,7 @@ RSpec.describe Projects::PipelinesController do check_pipeline_response(returned: 2, all: 6) end - context 'when performing gitaly calls', :request_store do - before do - # To prevent double writes / fallback read due to MultiStore which is failing the `Gitlab::GitalyClient - # .get_request_count` expectation. - stub_feature_flags(use_primary_store_as_default_for_repository_cache: false) - stub_feature_flags(use_primary_and_secondary_stores_for_repository_cache: false) - end - + context 'when performing gitaly calls', :request_store, :use_null_store_as_repository_cache do it 'limits the Gitaly requests' do # Isolate from test preparation (Repository#exists? is also cached in RequestStore) RequestStore.end! @@ -1355,8 +1333,8 @@ RSpec.describe Projects::PipelinesController do .and_return(service) end - context 'when sending a valid sha' do - let(:sha) { 'master' } + context 'when sending a valid ref' do + let(:ref) { 'master' } let(:ci_config) do { variables: { @@ -1381,8 +1359,8 @@ RSpec.describe Projects::PipelinesController do end end - context 'when sending an invalid sha' do - let(:sha) { 'invalid-sha' } + context 'when sending an invalid ref' do + let(:ref) { 'invalid-ref' } before do synchronous_reactive_cache(service) @@ -1397,7 +1375,7 @@ RSpec.describe Projects::PipelinesController do end context 'when sending an invalid config' do - let(:sha) { 'master' } + let(:ref) { 'master' } let(:ci_config) do { variables: { @@ -1423,7 +1401,7 @@ RSpec.describe Projects::PipelinesController do end context 'when the cache is empty' do - let(:sha) { 'master' } + let(:ref) { 'master' } let(:ci_config) do { variables: { @@ -1446,7 +1424,7 @@ RSpec.describe Projects::PipelinesController do context 'when project uses external project ci config' do let(:other_project) { create(:project, :custom_repo, files: other_project_files) } let(:other_project_files) { { '.gitlab-ci.yml' => YAML.dump(other_project_ci_config) } } - let(:sha) { 'master' } + let(:ref) { 'master' } let(:other_project_ci_config) do { @@ -1479,7 +1457,7 @@ RSpec.describe Projects::PipelinesController do def get_config_variables get :config_variables, params: { namespace_id: project.namespace, project_id: project, - sha: sha }, + sha: ref }, format: :json end end diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index fb27fe58cd9..ab33195eb83 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -8,183 +8,168 @@ RSpec.describe Projects::ProjectMembersController do let_it_be(:sub_group) { create(:group, parent: group) } let_it_be(:project, reload: true) { create(:project, :public) } - before do - travel_to DateTime.new(2019, 4, 1) - end + shared_examples_for 'controller actions' do + before do + travel_to DateTime.new(2019, 4, 1) + end - after do - travel_back - end + after do + travel_back + end - describe 'GET index' do - it 'has the project_members address with a 200 status code' do - get :index, params: { namespace_id: project.namespace, project_id: project } + describe 'GET index' do + it 'has the project_members address with a 200 status code' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(response).to have_gitlab_http_status(:ok) - end + expect(response).to have_gitlab_http_status(:ok) + end - context 'project members' do - context 'when project belongs to group' do - let_it_be(:user_in_group) { create(:user) } - let_it_be(:project_in_group) { create(:project, :public, group: group) } + context 'project members' do + context 'when project belongs to group' do + let_it_be(:user_in_group) { create(:user) } + let_it_be(:project_in_group) { create(:project, :public, group: group) } - before do - group.add_owner(user_in_group) - project_in_group.add_maintainer(user) - sign_in(user) - end + before do + group.add_owner(user_in_group) + project_in_group.add_maintainer(user) + sign_in(user) + end - it 'lists inherited project members by default' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group } + it 'lists inherited project members by default' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id) - end + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id) + end - it 'lists direct project members only' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' } + it 'lists direct project members only' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id) - end + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id) + end - it 'lists inherited project members only' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' } + it 'lists inherited project members only' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id) + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id) + end end - end - context 'when project belongs to a sub-group' do - let_it_be(:user_in_group) { create(:user) } - let_it_be(:project_in_group) { create(:project, :public, group: sub_group) } + context 'when project belongs to a sub-group' do + let_it_be(:user_in_group) { create(:user) } + let_it_be(:project_in_group) { create(:project, :public, group: sub_group) } - before do - group.add_owner(user_in_group) - project_in_group.add_maintainer(user) - sign_in(user) - end + before do + group.add_owner(user_in_group) + project_in_group.add_maintainer(user) + sign_in(user) + end - it 'lists inherited project members by default' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group } + it 'lists inherited project members by default' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id) - end + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id) + end - it 'lists direct project members only' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' } + it 'lists direct project members only' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id) - end + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id) + end - it 'lists inherited project members only' do - get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' } + it 'lists inherited project members only' do + get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' } - expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id) + expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id) + end end - end - context 'when invited project members are present' do - let!(:invited_member) { create(:project_member, :invited, project: project) } + context 'when invited project members are present' do + let!(:invited_member) { create(:project_member, :invited, project: project) } - before do - project.add_maintainer(user) - sign_in(user) - end + before do + project.add_maintainer(user) + sign_in(user) + end - it 'excludes the invited members from project members list' do - get :index, params: { namespace_id: project.namespace, project_id: project } + it 'excludes the invited members from project members list' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:project_members).map(&:invite_email)).not_to contain_exactly(invited_member.invite_email) + expect(assigns(:project_members).map(&:invite_email)).not_to contain_exactly(invited_member.invite_email) + end end end - end - - context 'invited members' do - let_it_be(:invited_member) { create(:project_member, :invited, project: project) } - before do - sign_in(user) - end + context 'invited members' do + let_it_be(:invited_member) { create(:project_member, :invited, project: project) } - context 'when user has `admin_project_member` permissions' do before do - project.add_maintainer(user) + sign_in(user) end - it 'lists invited members' do - get :index, params: { namespace_id: project.namespace, project_id: project } + context 'when user has `admin_project_member` permissions' do + before do + project.add_maintainer(user) + end + + it 'lists invited members' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:invited_members).map(&:invite_email)).to contain_exactly(invited_member.invite_email) + expect(assigns(:invited_members).map(&:invite_email)).to contain_exactly(invited_member.invite_email) + end end - end - context 'when user does not have `admin_project_member` permissions' do - it 'does not list invited members' do - get :index, params: { namespace_id: project.namespace, project_id: project } + context 'when user does not have `admin_project_member` permissions' do + it 'does not list invited members' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:invited_members)).to be_nil + expect(assigns(:invited_members)).to be_nil + end end end - end - context 'access requests' do - let_it_be(:access_requester_user) { create(:user) } - - before do - project.request_access(access_requester_user) - sign_in(user) - end + context 'access requests' do + let_it_be(:access_requester_user) { create(:user) } - context 'when user has `admin_project_member` permissions' do before do - project.add_maintainer(user) + project.request_access(access_requester_user) + sign_in(user) end - it 'lists access requests' do - get :index, params: { namespace_id: project.namespace, project_id: project } + context 'when user has `admin_project_member` permissions' do + before do + project.add_maintainer(user) + end + + it 'lists access requests' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:requesters).map(&:user_id)).to contain_exactly(access_requester_user.id) + expect(assigns(:requesters).map(&:user_id)).to contain_exactly(access_requester_user.id) + end end - end - context 'when user does not have `admin_project_member` permissions' do - it 'does not list access requests' do - get :index, params: { namespace_id: project.namespace, project_id: project } + context 'when user does not have `admin_project_member` permissions' do + it 'does not list access requests' do + get :index, params: { namespace_id: project.namespace, project_id: project } - expect(assigns(:requesters)).to be_nil + expect(assigns(:requesters)).to be_nil + end end end end - end - - describe 'PUT update' do - let_it_be(:requester) { create(:project_member, :access_request, project: project) } - - before do - project.add_maintainer(user) - sign_in(user) - end - context 'access level' do - Gitlab::Access.options.each do |label, value| - it "can change the access level to #{label}" do - params = { - project_member: { access_level: value }, - namespace_id: project.namespace, - project_id: project, - id: requester - } + describe 'PUT update' do + let_it_be(:requester) { create(:project_member, :access_request, project: project) } - put :update, params: params, xhr: true - - expect(requester.reload.human_access).to eq(label) - end + before do + project.add_maintainer(user) + sign_in(user) 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 + context 'access level' do + Gitlab::Access.options.each do |label, value| + it "can change the access level to #{label}" do params = { - project_member: { access_level: Gitlab::Access::OWNER }, + project_member: { access_level: value }, namespace_id: project.namespace, project_id: project, id: requester @@ -192,368 +177,395 @@ RSpec.describe Projects::ProjectMembersController do put :update, params: params, xhr: true - expect(response).to have_gitlab_http_status(:forbidden) + expect(requester.reload.human_access).to eq(label) 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 } + 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 + } - before do - sign_in(user) + put :update, params: params, xhr: true + + expect(response).to have_gitlab_http_status(:forbidden) + end end - it 'returns success' do - params = { - project_member: { access_level: Gitlab::Access::OWNER }, - namespace_id: project.namespace, - project_id: project, - id: requester - } + 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 } - put :update, params: params, xhr: true + 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) + expect(response).to have_gitlab_http_status(:ok) + expect(requester.reload.access_level).to eq(Gitlab::Access::OWNER) + end end end end - end - context 'access expiry date' do - subject do - put :update, xhr: true, params: { - project_member: { - expires_at: expires_at - }, - namespace_id: project.namespace, - project_id: project, - id: requester - } - end + context 'access expiry date' do + subject do + put :update, xhr: true, params: { + project_member: { + expires_at: expires_at + }, + namespace_id: project.namespace, + project_id: project, + id: requester + } + end - context 'when set to a date in the past' do - let(:expires_at) { 2.days.ago } + context 'when set to a date in the past' do + let(:expires_at) { 2.days.ago } - it 'does not update the member' do - subject + it 'does not update the member' do + subject - expect(requester.reload.expires_at).not_to eq(expires_at.to_date) - end + expect(requester.reload.expires_at).not_to eq(expires_at.to_date) + end - it 'returns error status' do - subject + it 'returns error status' do + subject - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end - it 'returns error message' do - subject + it 'returns error message' do + subject - expect(json_response).to eq({ 'message' => 'Expires at cannot be a date in the past' }) + expect(json_response).to eq({ 'message' => 'Expires at cannot be a date in the past' }) + end end - end - context 'when set to a date in the future' do - let(:expires_at) { 5.days.from_now } + context 'when set to a date in the future' do + let(:expires_at) { 5.days.from_now } - it 'updates the member' do - subject + it 'updates the member' do + subject - expect(requester.reload.expires_at).to eq(expires_at.to_date) + expect(requester.reload.expires_at).to eq(expires_at.to_date) + end end end - end - context 'expiration date' do - let(:expiry_date) { 1.month.from_now.to_date } + context 'expiration date' do + let(:expiry_date) { 1.month.from_now.to_date } - before do - travel_to Time.now.utc.beginning_of_day - - put( - :update, - params: { - project_member: { expires_at: expiry_date }, - namespace_id: project.namespace, - project_id: project, - id: requester - }, - format: :json - ) - end + before do + travel_to Time.now.utc.beginning_of_day + + put( + :update, + params: { + project_member: { expires_at: expiry_date }, + namespace_id: project.namespace, + project_id: project, + id: requester + }, + format: :json + ) + end - context 'when `expires_at` is set' do - it 'returns correct json response' do - expect(json_response).to eq({ - "expires_soon" => false, - "expires_at_formatted" => expiry_date.to_time.in_time_zone.to_s(:medium) - }) + context 'when `expires_at` is set' do + it 'returns correct json response' do + expect(json_response).to eq({ + "expires_soon" => false, + "expires_at_formatted" => expiry_date.to_time.in_time_zone.to_s(:medium) + }) + end end - end - context 'when `expires_at` is not set' do - let(:expiry_date) { nil } + context 'when `expires_at` is not set' do + let(:expiry_date) { nil } - it 'returns empty json response' do - expect(json_response).to be_empty + it 'returns empty json response' do + expect(json_response).to be_empty + end end end end - end - describe 'DELETE destroy' do - let_it_be(:member) { create(:project_member, :developer, project: project) } + describe 'DELETE destroy' do + let_it_be(:member) { create(:project_member, :developer, project: project) } - before do - sign_in(user) - end + before do + sign_in(user) + end - context 'when member is not found' do - it 'returns 404' do - delete :destroy, params: { - namespace_id: project.namespace, - project_id: project, - id: 42 - } + context 'when member is not found' do + it 'returns 404' do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: 42 + } - expect(response).to have_gitlab_http_status(:not_found) + expect(response).to have_gitlab_http_status(:not_found) + end end - 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) + 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 + + 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 - 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) } - + context 'when user has enough rights' do before do project.add_maintainer(user) end - it 'returns 403', :aggregate_failures do + it '[HTML] removes user from members', :aggregate_failures do + delete :destroy, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to redirect_to( + project_project_members_path(project) + ) + expect(project.members).not_to include member + end + + it '[JS] removes user from members', :aggregate_failures do delete :destroy, params: { namespace_id: project.namespace, project_id: project, id: member - } + }, xhr: true - expect(response).to have_gitlab_http_status(:forbidden) - expect(project.members).to include member + expect(response).to be_successful + expect(project.members).not_to include member end end end - - context 'when user has enough rights' do - before do - project.add_maintainer(user) - end - - it '[HTML] removes user from members', :aggregate_failures do - delete :destroy, params: { - namespace_id: project.namespace, - project_id: project, - id: member - } - - expect(response).to redirect_to( - project_project_members_path(project) - ) - expect(project.members).not_to include member - end - - it '[JS] removes user from members', :aggregate_failures do - delete :destroy, params: { - namespace_id: project.namespace, - project_id: project, - id: member - }, xhr: true - - expect(response).to be_successful - expect(project.members).not_to include member - end - end - end - end - - describe 'DELETE leave' do - before do - sign_in(user) end - context 'when member is not found' do - it 'returns 404' do - delete :leave, params: { - namespace_id: project.namespace, - project_id: project - } - - expect(response).to have_gitlab_http_status(:not_found) + describe 'DELETE leave' do + before do + sign_in(user) end - end - - context 'when member is found' do - context 'and is not an owner' do - before do - project.add_developer(user) - end - it 'removes user from members', :aggregate_failures do + context 'when member is not found' do + it 'returns 404' do delete :leave, params: { namespace_id: project.namespace, project_id: project } - expect(controller).to set_flash.to "You left the \"#{project.human_name}\" project." - expect(response).to redirect_to(dashboard_projects_path) - expect(project.users).not_to include user + expect(response).to have_gitlab_http_status(:not_found) end end - context 'and is an owner' do - let(:project) { create(:project, namespace: user.namespace) } + context 'when member is found' do + context 'and is not an owner' do + before do + project.add_developer(user) + end + + it 'removes user from members', :aggregate_failures do + delete :leave, params: { + namespace_id: project.namespace, + project_id: project + } - before do - project.add_maintainer(user) + expect(controller).to set_flash.to "You left the \"#{project.human_name}\" project." + expect(response).to redirect_to(dashboard_projects_path) + expect(project.users).not_to include user + end end - it 'cannot remove themselves from the project' do - delete :leave, params: { - namespace_id: project.namespace, - project_id: project - } + context 'and is an owner' do + let(:project) { create(:project, namespace: user.namespace) } - expect(response).to have_gitlab_http_status(:forbidden) - end - end + before do + project.add_maintainer(user) + end - context 'and is a requester' do - before do - project.request_access(user) + it 'cannot remove themselves from the project' do + delete :leave, params: { + namespace_id: project.namespace, + project_id: project + } + + expect(response).to have_gitlab_http_status(:forbidden) + end end - it 'removes user from members', :aggregate_failures do - delete :leave, params: { - namespace_id: project.namespace, - project_id: project - } + context 'and is a requester' do + before do + project.request_access(user) + end + + it 'removes user from members', :aggregate_failures do + delete :leave, params: { + namespace_id: project.namespace, + project_id: project + } - expect(controller).to set_flash.to 'Your access request to the project has been withdrawn.' - expect(response).to redirect_to(project_path(project)) - expect(project.requesters).to be_empty - expect(project.users).not_to include user + expect(controller).to set_flash.to 'Your access request to the project has been withdrawn.' + expect(response).to redirect_to(project_path(project)) + expect(project.requesters).to be_empty + expect(project.users).not_to include user + end end end end - end - - describe 'POST request_access' do - before do - sign_in(user) - end - it 'creates a new ProjectMember that is not a team member', :aggregate_failures do - post :request_access, params: { - namespace_id: project.namespace, - project_id: project - } - - expect(controller).to set_flash.to 'Your request for access has been queued for review.' - expect(response).to redirect_to( - project_path(project) - ) - expect(project.requesters.exists?(user_id: user)).to be_truthy - expect(project.users).not_to include user - end - end + describe 'POST request_access' do + before do + sign_in(user) + end - describe 'POST approve' do - let_it_be(:member) { create(:project_member, :access_request, project: project) } + it 'creates a new ProjectMember that is not a team member', :aggregate_failures do + post :request_access, params: { + namespace_id: project.namespace, + project_id: project + } - before do - sign_in(user) + expect(controller).to set_flash.to 'Your request for access has been queued for review.' + expect(response).to redirect_to( + project_path(project) + ) + expect(project.requesters.exists?(user_id: user)).to be_truthy + expect(project.users).not_to include user + end end - context 'when member is not found' do - it 'returns 404' do - post :approve_access_request, params: { - namespace_id: project.namespace, - project_id: project, - id: 42 - } + describe 'POST approve' do + let_it_be(:member) { create(:project_member, :access_request, project: project) } - expect(response).to have_gitlab_http_status(:not_found) + before do + sign_in(user) end - end - - context 'when member is found' do - 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 + context 'when member is not found' do + it 'returns 404' do post :approve_access_request, params: { namespace_id: project.namespace, project_id: project, - id: member + id: 42 } expect(response).to have_gitlab_http_status(:not_found) - expect(project.members).not_to include member end end - context 'when user has enough rights' do - before do - project.add_maintainer(user) + context 'when member is found' do + 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 + post :approve_access_request, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to have_gitlab_http_status(:not_found) + expect(project.members).not_to include member + end end - it 'adds user to members', :aggregate_failures do - post :approve_access_request, params: { - namespace_id: project.namespace, - project_id: project, - id: member - } + context 'when user has enough rights' do + before do + project.add_maintainer(user) + end - expect(response).to redirect_to( - project_project_members_path(project) - ) - expect(project.members).to include member + it 'adds user to members', :aggregate_failures do + post :approve_access_request, params: { + namespace_id: project.namespace, + project_id: project, + id: member + } + + expect(response).to redirect_to( + project_project_members_path(project) + ) + expect(project.members).to include member + end end end end - end - describe 'POST resend_invite' do - let_it_be(:member) { create(:project_member, project: project) } + describe 'POST resend_invite' do + let_it_be(:member) { create(:project_member, project: project) } - before do - project.add_maintainer(user) - sign_in(user) + before do + project.add_maintainer(user) + sign_in(user) + end + + it 'is successful' do + post :resend_invite, params: { namespace_id: project.namespace, project_id: project, id: member } + + expect(response).to have_gitlab_http_status(:found) + end end + end - it 'is successful' do - post :resend_invite, params: { namespace_id: project.namespace, project_id: project, id: member } + it_behaves_like 'controller actions' - expect(response).to have_gitlab_http_status(:found) + context 'when project_members_index_by_project_namespace feature flag is disabled' do + before do + stub_feature_flags(project_members_index_by_project_namespace: false) end + + it_behaves_like 'controller actions' end end diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 1c9aafacbd9..40252cf65cd 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Projects::RawController do +RSpec.describe Projects::RawController, feature_category: :source_code_management do include RepoHelpers let_it_be(:project) { create(:project, :public, :repository) } @@ -23,13 +23,13 @@ RSpec.describe Projects::RawController do subject { get_show } - shared_examples 'single Gitaly request' do - it 'makes a single Gitaly request', :request_store, :clean_gitlab_redis_cache do + shared_examples 'limited number of Gitaly request' do + it 'makes a limited number of Gitaly request', :request_store, :clean_gitlab_redis_cache do # Warm up to populate repository cache get_show RequestStore.clear! - expect { get_show }.to change { Gitlab::GitalyClient.get_request_count }.by(1) + expect { get_show }.to change { Gitlab::GitalyClient.get_request_count }.by(2) end end @@ -57,7 +57,7 @@ RSpec.describe Projects::RawController do it_behaves_like 'project cache control headers' it_behaves_like 'content disposition headers' - include_examples 'single Gitaly request' + include_examples 'limited number of Gitaly request' end context 'image header' do @@ -73,7 +73,7 @@ RSpec.describe Projects::RawController do it_behaves_like 'project cache control headers' it_behaves_like 'content disposition headers' - include_examples 'single Gitaly request' + include_examples 'limited number of Gitaly request' end context 'with LFS files' do @@ -82,7 +82,7 @@ RSpec.describe Projects::RawController do it_behaves_like 'a controller that can serve LFS files' it_behaves_like 'project cache control headers' - include_examples 'single Gitaly request' + include_examples 'limited number of Gitaly request' end context 'when the endpoint receives requests above the limit' do @@ -239,8 +239,10 @@ RSpec.describe Projects::RawController do end describe 'caching' do + let(:ref) { project.default_branch } + def request_file - get(:show, params: { namespace_id: project.namespace, project_id: project, id: 'master/README.md' }) + get(:show, params: { namespace_id: project.namespace, project_id: project, id: "#{ref}/README.md" }) end it 'sets appropriate caching headers' do @@ -254,6 +256,21 @@ RSpec.describe Projects::RawController do ) end + context 'when a blob access by permalink' do + let(:ref) { project.commit.id } + + it 'sets appropriate caching headers with longer max-age' do + sign_in create(:user) + request_file + + expect(response.headers['ETag']).to eq("\"bdd5aa537c1e1f6d1b66de4bac8a6132\"") + expect(response.cache_control[:no_store]).to be_nil + expect(response.header['Cache-Control']).to eq( + 'max-age=3600, public, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) + end + end + context 'when a public project has private repo' do let(:project) { create(:project, :public, :repository, :repository_private) } let(:user) { create(:user, maintainer_projects: [project]) } @@ -278,21 +295,6 @@ RSpec.describe Projects::RawController do expect(response).to have_gitlab_http_status(:not_modified) end end - - context 'when improve_blobs_cache_headers disabled' do - before do - stub_feature_flags(improve_blobs_cache_headers: false) - end - - it 'uses weak etags with a restricted set of headers' do - sign_in create(:user) - request_file - - expect(response.headers['ETag']).to eq("W/\"bdd5aa537c1e1f6d1b66de4bac8a6132\"") - expect(response.cache_control[:no_store]).to be_nil - expect(response.header['Cache-Control']).to eq('max-age=60, public') - end - end end end end diff --git a/spec/controllers/projects/refs_controller_spec.rb b/spec/controllers/projects/refs_controller_spec.rb index a7a8361ae20..a0d119baf16 100644 --- a/spec/controllers/projects/refs_controller_spec.rb +++ b/spec/controllers/projects/refs_controller_spec.rb @@ -22,65 +22,30 @@ RSpec.describe Projects::RefsController, feature_category: :source_code_manageme subject { get :switch, params: params } - context 'when the use_ref_type_parameter feature flag is not enabled' do - before do - stub_feature_flags(use_ref_type_parameter: false) - end - - where(:destination, :ref_type, :redirected_to) do - 'tree' | nil | lazy { project_tree_path(project, id) } - 'tree' | 'heads' | lazy { project_tree_path(project, id) } - 'blob' | nil | lazy { project_blob_path(project, id) } - 'blob' | 'heads' | lazy { project_blob_path(project, id) } - 'graph' | nil | lazy { project_network_path(project, id) } - 'graph' | 'heads' | lazy { project_network_path(project, id) } - 'graphs' | nil | lazy { project_graph_path(project, id) } - 'graphs' | 'heads' | lazy { project_graph_path(project, id) } - 'find_file' | nil | lazy { project_find_file_path(project, id) } - 'find_file' | 'heads' | lazy { project_find_file_path(project, id) } - 'graphs_commits' | nil | lazy { commits_project_graph_path(project, id) } - 'graphs_commits' | 'heads' | lazy { commits_project_graph_path(project, id) } - 'badges' | nil | lazy { project_settings_ci_cd_path(project, ref: id) } - 'badges' | 'heads' | lazy { project_settings_ci_cd_path(project, ref: id) } - 'commits' | nil | lazy { project_commits_path(project, id) } - 'commits' | 'heads' | lazy { project_commits_path(project, id) } - 'somethingelse' | nil | lazy { project_commits_path(project, id) } - 'somethingelse' | 'heads' | lazy { project_commits_path(project, id) } - end - - with_them do - it 'redirects to destination' do - expect(subject).to redirect_to(redirected_to) - end - end + where(:destination, :ref_type, :redirected_to) do + 'tree' | nil | lazy { project_tree_path(project, id) } + 'tree' | 'heads' | lazy { project_tree_path(project, id) } + 'blob' | nil | lazy { project_blob_path(project, id) } + 'blob' | 'heads' | lazy { project_blob_path(project, id) } + 'graph' | nil | lazy { project_network_path(project, id) } + 'graph' | 'heads' | lazy { project_network_path(project, id, ref_type: 'heads') } + 'graphs' | nil | lazy { project_graph_path(project, id) } + 'graphs' | 'heads' | lazy { project_graph_path(project, id, ref_type: 'heads') } + 'find_file' | nil | lazy { project_find_file_path(project, id) } + 'find_file' | 'heads' | lazy { project_find_file_path(project, id) } + 'graphs_commits' | nil | lazy { commits_project_graph_path(project, id) } + 'graphs_commits' | 'heads' | lazy { commits_project_graph_path(project, id) } + 'badges' | nil | lazy { project_settings_ci_cd_path(project, ref: id) } + 'badges' | 'heads' | lazy { project_settings_ci_cd_path(project, ref: id) } + 'commits' | nil | lazy { project_commits_path(project, id) } + 'commits' | 'heads' | lazy { project_commits_path(project, id, ref_type: 'heads') } + nil | nil | lazy { project_commits_path(project, id) } + nil | 'heads' | lazy { project_commits_path(project, id, ref_type: 'heads') } end - context 'when the use_ref_type_parameter feature flag is enabled' do - where(:destination, :ref_type, :redirected_to) do - 'tree' | nil | lazy { project_tree_path(project, id) } - 'tree' | 'heads' | lazy { project_tree_path(project, id) } - 'blob' | nil | lazy { project_blob_path(project, id) } - 'blob' | 'heads' | lazy { project_blob_path(project, id) } - 'graph' | nil | lazy { project_network_path(project, id) } - 'graph' | 'heads' | lazy { project_network_path(project, id, ref_type: 'heads') } - 'graphs' | nil | lazy { project_graph_path(project, id) } - 'graphs' | 'heads' | lazy { project_graph_path(project, id, ref_type: 'heads') } - 'find_file' | nil | lazy { project_find_file_path(project, id) } - 'find_file' | 'heads' | lazy { project_find_file_path(project, id) } - 'graphs_commits' | nil | lazy { commits_project_graph_path(project, id) } - 'graphs_commits' | 'heads' | lazy { commits_project_graph_path(project, id) } - 'badges' | nil | lazy { project_settings_ci_cd_path(project, ref: id) } - 'badges' | 'heads' | lazy { project_settings_ci_cd_path(project, ref: id) } - 'commits' | nil | lazy { project_commits_path(project, id) } - 'commits' | 'heads' | lazy { project_commits_path(project, id, ref_type: 'heads') } - nil | nil | lazy { project_commits_path(project, id) } - nil | 'heads' | lazy { project_commits_path(project, id, ref_type: 'heads') } - end - - with_them do - it 'redirects to destination' do - expect(subject).to redirect_to(redirected_to) - end + with_them do + it 'redirects to destination' do + expect(subject).to redirect_to(redirected_to) end end end diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index 928428b5caf..8186176a46b 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Projects::RepositoriesController do +RSpec.describe Projects::RepositoriesController, feature_category: :source_code_management do let_it_be(:project) { create(:project, :repository) } describe 'POST create' do @@ -143,7 +143,9 @@ RSpec.describe Projects::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) expect(response.header['ETag']).to be_present - expect(response.header['Cache-Control']).to include('max-age=60, public') + expect(response.header['Cache-Control']).to eq( + 'max-age=60, public, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) end context 'and repo is private' do @@ -154,7 +156,9 @@ RSpec.describe Projects::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) expect(response.header['ETag']).to be_present - expect(response.header['Cache-Control']).to include('max-age=60, private') + expect(response.header['Cache-Control']).to eq( + 'max-age=60, private, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) end end end @@ -164,7 +168,9 @@ RSpec.describe Projects::RepositoriesController do get_archive('ddd0f15ae83993f5cb66a927a28673882e99100b') expect(response).to have_gitlab_http_status(:ok) - expect(response.header['Cache-Control']).to include('max-age=3600') + expect(response.header['Cache-Control']).to eq( + 'max-age=3600, private, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) end end diff --git a/spec/controllers/projects/service_ping_controller_spec.rb b/spec/controllers/projects/service_ping_controller_spec.rb index 10d4b897564..601dfd9b011 100644 --- a/spec/controllers/projects/service_ping_controller_spec.rb +++ b/spec/controllers/projects/service_ping_controller_spec.rb @@ -42,83 +42,6 @@ RSpec.describe Projects::ServicePingController do end end - describe 'POST #web_ide_clientside_preview' do - subject { post :web_ide_clientside_preview, params: { namespace_id: project.namespace, project_id: project } } - - context 'when web ide clientside preview is enabled' do - before do - stub_application_setting(web_ide_clientside_preview_enabled: true) - end - - it_behaves_like 'counter is not increased' - it_behaves_like 'counter is increased', 'WEB_IDE_PREVIEWS_COUNT' - end - - context 'when web ide clientside preview is not enabled' do - let(:user) { project.first_owner } - - before do - stub_application_setting(web_ide_clientside_preview_enabled: false) - end - - it 'returns 404' do - subject - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - - describe 'POST #web_ide_clientside_preview_success' do - subject { post :web_ide_clientside_preview_success, params: { namespace_id: project.namespace, project_id: project } } - - context 'when web ide clientside preview is enabled' do - before do - stub_application_setting(web_ide_clientside_preview_enabled: true) - end - - 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', :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, project: project) - - subject - - expect(response).to have_gitlab_http_status(:ok) - end - - it_behaves_like 'Snowplow event tracking with RedisHLL context' do - let(:project) { create(:project) } - let(:namespace) { project.namespace } - let(:category) { 'Gitlab::UsageDataCounters::EditorUniqueCounter' } - let(:action) { 'ide_edit' } - let(:property) { 'g_edit_by_live_preview' } - let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_ide_edit' } - let(:context) { [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context] } - let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } - end - end - end - - context 'when web ide clientside preview is not enabled' do - let(:user) { project.owner } - - before do - stub_application_setting(web_ide_clientside_preview_enabled: false) - end - - it 'returns 404' do - subject - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - describe 'POST #web_ide_pipelines_count' do subject { post :web_ide_pipelines_count, params: { namespace_id: project.namespace, project_id: project } } diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb index dcd1072612a..ba917fa3a31 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -2,7 +2,7 @@ require('spec_helper') -RSpec.describe Projects::Settings::CiCdController do +RSpec.describe Projects::Settings::CiCdController, feature_category: :continuous_integration do let_it_be(:user) { create(:user) } let_it_be(:project_auto_devops) { create(:project_auto_devops) } diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb index 51ea2e5d7c6..781e4ff7b00 100644 --- a/spec/controllers/projects/settings/repository_controller_spec.rb +++ b/spec/controllers/projects/settings/repository_controller_spec.rb @@ -19,40 +19,6 @@ RSpec.describe Projects::Settings::RepositoryController, feature_category: :sour expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:show) end - - context 'when feature flag `group_protected_branches` disabled' do - before do - stub_feature_flags(group_protected_branches: false) - end - - it 'does not assign instance variable `protected_group_branches`' do - get :show, params: base_params - - expect(assigns).not_to include(:protected_group_branches) - end - end - - context 'when feature flag `group_protected_branches` enabled' do - context 'when the root namespace is a user' do - it 'assigns empty instance variable `protected_group_branches`' do - get :show, params: base_params - - expect(assigns[:protected_group_branches]).to eq([]) - end - end - - context 'when the root namespace is a group' do - let_it_be(:project) { create(:project_empty_repo, :public, :in_group) } - - let(:protected_group_branch) { create(:protected_branch, group: project.root_namespace, project: nil) } - - it 'assigns instance variable `protected_group_branches`' do - get :show, params: base_params - - expect(assigns[:protected_group_branches]).to include(protected_group_branch) - end - end - end end describe 'PUT cleanup' do diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index bc58eaa1d6f..51f8a3b1197 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -2,7 +2,7 @@ require('spec_helper') -RSpec.describe ProjectsController do +RSpec.describe ProjectsController, feature_category: :projects do include ExternalAuthorizationServiceHelpers include ProjectForksHelper using RSpec::Parameterized::TableSyntax @@ -629,29 +629,66 @@ RSpec.describe ProjectsController do describe '#housekeeping' do let_it_be(:group) { create(:group) } - let_it_be(:project) { create(:project, group: group) } + let(:housekeeping_service_dbl) { instance_double(Repositories::HousekeepingService) } + let(:params) do + { + namespace_id: project.namespace.path, + id: project.path, + prune: prune + } + end + let(:prune) { nil } + let_it_be(:project) { create(:project, group: group) } let(:housekeeping) { Repositories::HousekeepingService.new(project) } + subject { post :housekeeping, params: params } + context 'when authenticated as owner' do before do group.add_owner(user) sign_in(user) - allow(Repositories::HousekeepingService).to receive(:new).with(project, :gc).and_return(housekeeping) + allow(Repositories::HousekeepingService).to receive(:new).with(project, :eager).and_return(housekeeping) end it 'forces a full garbage collection' do expect(housekeeping).to receive(:execute).once post :housekeeping, - params: { - namespace_id: project.namespace.path, - id: project.path - } + params: { + namespace_id: project.namespace.path, + id: project.path + } expect(response).to have_gitlab_http_status(:found) end + + it 'logs an audit event' do + expect(housekeeping).to receive(:execute).once.and_yield + + expect(::Gitlab::Audit::Auditor).to receive(:audit).with(a_hash_including( + name: 'manually_trigger_housekeeping', + author: user, + scope: project, + target: project, + message: "Housekeeping task: eager" + )) + + subject + end + + context 'and requesting prune' do + let(:prune) { true } + + it 'enqueues pruning' do + allow(Repositories::HousekeepingService).to receive(:new).with(project, :prune).and_return(housekeeping_service_dbl) + expect(housekeeping_service_dbl).to receive(:execute) + + subject + expect(response).to have_gitlab_http_status(:found) + end + end end context 'when authenticated as developer' do @@ -665,10 +702,10 @@ RSpec.describe ProjectsController do expect(housekeeping).not_to receive(:execute) post :housekeeping, - params: { - namespace_id: project.namespace.path, - id: project.path - } + params: { + namespace_id: project.namespace.path, + id: project.path + } expect(response).to have_gitlab_http_status(:found) end diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index d0439a18158..b217b100349 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe RegistrationsController do +RSpec.describe RegistrationsController, feature_category: :user_profile do include TermsHelper include FullNameHelper @@ -215,11 +215,18 @@ RSpec.describe RegistrationsController do property: member.id.to_s, user: member.reload.user ) + + expect_snowplow_event( + category: 'RegistrationsController', + action: 'create_user', + label: 'invited', + user: member.reload.user + ) end end context 'when member does not exist from the session key value' do - let(:originating_member_id) { -1 } + let(:originating_member_id) { nil } it 'does not track invite acceptance' do subject @@ -229,6 +236,13 @@ RSpec.describe RegistrationsController do action: 'accepted', label: 'invite_email' ) + + expect_snowplow_event( + category: 'RegistrationsController', + action: 'create_user', + label: 'signup', + user: member.reload.user + ) end end end diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb index c6a8cee2f70..6fa1d93265d 100644 --- a/spec/controllers/root_controller_spec.rb +++ b/spec/controllers/root_controller_spec.rb @@ -37,16 +37,16 @@ RSpec.describe RootController do user.dashboard = 'stars' end - it 'redirects to their specified dashboard' do + it 'redirects to their starred projects list' do get :index expect(response).to redirect_to starred_dashboard_projects_path end end - context 'who has customized their dashboard setting for project activities' do + context 'who has customized their dashboard setting for their own activities' do before do - user.dashboard = 'project_activity' + user.dashboard = 'your_activity' end it 'redirects to the activity list' do @@ -56,12 +56,24 @@ RSpec.describe RootController do end end + context 'who has customized their dashboard setting for project activities' do + before do + user.dashboard = 'project_activity' + end + + it 'redirects to the projects activity list' do + get :index + + expect(response).to redirect_to activity_dashboard_path(filter: 'projects') + end + end + context 'who has customized their dashboard setting for starred project activities' do before do user.dashboard = 'starred_project_activity' end - it 'redirects to the activity list' do + it 'redirects to their starred projects activity list' do get :index expect(response).to redirect_to activity_dashboard_path(filter: 'starred') @@ -73,7 +85,7 @@ RSpec.describe RootController do user.dashboard = 'followed_user_activity' end - it 'redirects to the activity list' do + it 'redirects to the followed users activity list' do get :index expect(response).to redirect_to activity_dashboard_path(filter: 'followed') diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 37fc5a033ba..0f7f4a1910b 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe SearchController do +RSpec.describe SearchController, feature_category: :global_search do include ExternalAuthorizationServiceHelpers context 'authorized user' do @@ -359,12 +359,13 @@ RSpec.describe SearchController do end.to raise_error(ActionController::ParameterMissing) end - it 'sets private cache control headers' do + it 'sets correct cache control headers' do get :count, params: { search: 'hello', scope: 'projects' } expect(response).to have_gitlab_http_status(:ok) expect(response.headers['Cache-Control']).to eq('max-age=60, private') + expect(response.headers['Pragma']).to be_nil end it 'does NOT blow up if search param is NOT a string' do diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 78b3cc63b08..1f7d169bae5 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -221,7 +221,7 @@ RSpec.describe SessionsController do expect(Gitlab::Metrics).to receive(:counter) .with(:successful_login_captcha_total, anything) .and_return(counter) - expect(Gitlab::Metrics).to receive(:counter).and_call_original + expect(Gitlab::Metrics).to receive(:counter).at_least(1).time.and_call_original post(:create, params: { user: user_params }, session: sesion_params) end |