diff options
Diffstat (limited to 'spec/controllers')
35 files changed, 964 insertions, 677 deletions
diff --git a/spec/controllers/admin/instance_review_controller_spec.rb b/spec/controllers/admin/instance_review_controller_spec.rb index d15894eeb5d..898cd30cdca 100644 --- a/spec/controllers/admin/instance_review_controller_spec.rb +++ b/spec/controllers/admin/instance_review_controller_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Admin::InstanceReviewController do include UsageDataHelpers let(:admin) { create(:admin) } - let(:subscriptions_url) { ::Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL } + let(:subscriptions_instance_review_url) { Gitlab::SubscriptionPortal.subscriptions_instance_review_url } before do sign_in(admin) @@ -44,7 +44,7 @@ RSpec.describe Admin::InstanceReviewController do notes_count: 0 } }.to_query - expect(response).to redirect_to("#{subscriptions_url}/instance_review?#{params}") + expect(response).to redirect_to("#{subscriptions_instance_review_url}?#{params}") end end @@ -61,7 +61,7 @@ RSpec.describe Admin::InstanceReviewController do version: ::Gitlab::VERSION } }.to_query - expect(response).to redirect_to("#{subscriptions_url}/instance_review?#{params}") + expect(response).to redirect_to("#{subscriptions_instance_review_url}?#{params}") end end end diff --git a/spec/controllers/admin/serverless/domains_controller_spec.rb b/spec/controllers/admin/serverless/domains_controller_spec.rb deleted file mode 100644 index e7503fb37fa..00000000000 --- a/spec/controllers/admin/serverless/domains_controller_spec.rb +++ /dev/null @@ -1,370 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Admin::Serverless::DomainsController do - let(:admin) { create(:admin) } - let(:user) { create(:user) } - - describe '#index' do - context 'non-admin user' do - before do - sign_in(user) - end - - it 'responds with 404' do - get :index - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'admin user' do - before do - create(:pages_domain) - sign_in(admin) - end - - context 'with serverless_domain feature disabled' do - before do - stub_feature_flags(serverless_domain: false) - end - - it 'responds with 404' do - get :index - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when instance-level serverless domain exists' do - let!(:serverless_domain) { create(:pages_domain, :instance_serverless) } - - it 'loads the instance serverless domain' do - get :index - - expect(assigns(:domain).id).to eq(serverless_domain.id) - end - end - - context 'when domain does not exist' do - it 'initializes an instance serverless domain' do - get :index - - domain = assigns(:domain) - - expect(domain.persisted?).to eq(false) - expect(domain.wildcard).to eq(true) - expect(domain.scope).to eq('instance') - expect(domain.usage).to eq('serverless') - end - end - end - end - - describe '#create' do - let(:create_params) do - sample_domain = build(:pages_domain) - - { - domain: 'serverless.gitlab.io', - user_provided_certificate: sample_domain.certificate, - user_provided_key: sample_domain.key - } - end - - context 'non-admin user' do - before do - sign_in(user) - end - - it 'responds with 404' do - post :create, params: { pages_domain: create_params } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'admin user' do - before do - sign_in(admin) - end - - context 'with serverless_domain feature disabled' do - before do - stub_feature_flags(serverless_domain: false) - end - - it 'responds with 404' do - post :create, params: { pages_domain: create_params } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when an instance-level serverless domain exists' do - let!(:serverless_domain) { create(:pages_domain, :instance_serverless) } - - it 'does not create a new domain' do - expect { post :create, params: { pages_domain: create_params } }.not_to change { PagesDomain.instance_serverless.count } - end - - it 'redirects to index' do - post :create, params: { pages_domain: create_params } - - expect(response).to redirect_to admin_serverless_domains_path - expect(flash[:notice]).to include('An instance-level serverless domain already exists.') - end - end - - context 'when an instance-level serverless domain does not exist' do - it 'creates an instance serverless domain with the provided attributes' do - expect { post :create, params: { pages_domain: create_params } }.to change { PagesDomain.instance_serverless.count }.by(1) - - domain = PagesDomain.instance_serverless.first - expect(domain.domain).to eq(create_params[:domain]) - expect(domain.certificate).to eq(create_params[:user_provided_certificate]) - expect(domain.key).to eq(create_params[:user_provided_key]) - expect(domain.wildcard).to eq(true) - expect(domain.scope).to eq('instance') - expect(domain.usage).to eq('serverless') - end - - it 'redirects to index' do - post :create, params: { pages_domain: create_params } - - expect(response).to redirect_to admin_serverless_domains_path - expect(flash[:notice]).to include('Domain was successfully created.') - end - end - - context 'when there are errors' do - it 'renders index view' do - post :create, params: { pages_domain: { foo: 'bar' } } - - expect(assigns(:domain).errors.size).to be > 0 - expect(response).to render_template('index') - end - end - end - end - - describe '#update' do - let(:domain) { create(:pages_domain, :instance_serverless) } - - let(:update_params) do - sample_domain = build(:pages_domain) - - { - user_provided_certificate: sample_domain.certificate, - user_provided_key: sample_domain.key - } - end - - context 'non-admin user' do - before do - sign_in(user) - end - - it 'responds with 404' do - put :update, params: { id: domain.id, pages_domain: update_params } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'admin user' do - before do - sign_in(admin) - end - - context 'with serverless_domain feature disabled' do - before do - stub_feature_flags(serverless_domain: false) - end - - it 'responds with 404' do - put :update, params: { id: domain.id, pages_domain: update_params } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when domain exists' do - it 'updates the domain with the provided attributes' do - new_certificate = build(:pages_domain, :ecdsa).certificate - new_key = build(:pages_domain, :ecdsa).key - - put :update, params: { id: domain.id, pages_domain: { user_provided_certificate: new_certificate, user_provided_key: new_key } } - - domain.reload - - expect(domain.certificate).to eq(new_certificate) - expect(domain.key).to eq(new_key) - end - - it 'does not update the domain name' do - put :update, params: { id: domain.id, pages_domain: { domain: 'new.com' } } - - expect(domain.reload.domain).not_to eq('new.com') - end - - it 'redirects to index' do - put :update, params: { id: domain.id, pages_domain: update_params } - - expect(response).to redirect_to admin_serverless_domains_path - expect(flash[:notice]).to include('Domain was successfully updated.') - end - end - - context 'when domain does not exist' do - it 'returns 404' do - put :update, params: { id: 0, pages_domain: update_params } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when there are errors' do - it 'renders index view' do - put :update, params: { id: domain.id, pages_domain: { user_provided_certificate: 'bad certificate' } } - - expect(assigns(:domain).errors.size).to be > 0 - expect(response).to render_template('index') - end - end - end - end - - describe '#verify' do - let(:domain) { create(:pages_domain, :instance_serverless) } - - context 'non-admin user' do - before do - sign_in(user) - end - - it 'responds with 404' do - post :verify, params: { id: domain.id } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'admin user' do - before do - sign_in(admin) - end - - def stub_service - service = double(:service) - - expect(VerifyPagesDomainService).to receive(:new).with(domain).and_return(service) - - service - end - - context 'with serverless_domain feature disabled' do - before do - stub_feature_flags(serverless_domain: false) - end - - it 'responds with 404' do - post :verify, params: { id: domain.id } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - it 'handles verification success' do - expect(stub_service).to receive(:execute).and_return(status: :success) - - post :verify, params: { id: domain.id } - - expect(response).to redirect_to admin_serverless_domains_path - expect(flash[:notice]).to eq('Successfully verified domain ownership') - end - - it 'handles verification failure' do - expect(stub_service).to receive(:execute).and_return(status: :failed) - - post :verify, params: { id: domain.id } - - expect(response).to redirect_to admin_serverless_domains_path - expect(flash[:alert]).to eq('Failed to verify domain ownership') - end - end - end - - describe '#destroy' do - let!(:domain) { create(:pages_domain, :instance_serverless) } - - context 'non-admin user' do - before do - sign_in(user) - end - - it 'responds with 404' do - delete :destroy, params: { id: domain.id } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'admin user' do - before do - sign_in(admin) - end - - context 'with serverless_domain feature disabled' do - before do - stub_feature_flags(serverless_domain: false) - end - - it 'responds with 404' do - delete :destroy, params: { id: domain.id } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when domain exists' do - context 'and is not associated to any clusters' do - it 'deletes the domain' do - expect { delete :destroy, params: { id: domain.id } } - .to change { PagesDomain.count }.from(1).to(0) - - expect(response).to have_gitlab_http_status(:found) - expect(flash[:notice]).to include('Domain was successfully deleted.') - end - end - - context 'and is associated to any clusters' do - before do - create(:serverless_domain_cluster, pages_domain: domain) - end - - it 'does not delete the domain' do - expect { delete :destroy, params: { id: domain.id } } - .not_to change { PagesDomain.count } - - expect(response).to have_gitlab_http_status(:conflict) - expect(flash[:notice]).to include('Domain cannot be deleted while associated to one or more clusters.') - end - end - end - - context 'when domain does not exist' do - before do - domain.destroy! - end - - it 'responds with 404' do - delete :destroy, params: { id: domain.id } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - end -end diff --git a/spec/controllers/admin/topics/avatars_controller_spec.rb b/spec/controllers/admin/topics/avatars_controller_spec.rb new file mode 100644 index 00000000000..7edc0e0c497 --- /dev/null +++ b/spec/controllers/admin/topics/avatars_controller_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Admin::Topics::AvatarsController do + let(:user) { create(:admin) } + let(:topic) { create(:topic, avatar: fixture_file_upload("spec/fixtures/dk.png")) } + + before do + sign_in(user) + controller.instance_variable_set(:@topic, topic) + end + + it 'removes avatar from DB by calling destroy' do + delete :destroy, params: { topic_id: topic.id } + @topic = assigns(:topic) + expect(@topic.avatar.present?).to be_falsey + expect(@topic).to be_valid + end +end diff --git a/spec/controllers/admin/topics_controller_spec.rb b/spec/controllers/admin/topics_controller_spec.rb new file mode 100644 index 00000000000..6d66cb43338 --- /dev/null +++ b/spec/controllers/admin/topics_controller_spec.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Admin::TopicsController do + let_it_be(:topic) { create(:topic, name: 'topic') } + let_it_be(:admin) { create(:admin) } + let_it_be(:user) { create(:user) } + + before do + sign_in(admin) + end + + describe 'GET #index' do + it 'renders the template' do + get :index + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template('index') + end + + context 'as a normal user' do + before do + sign_in(user) + end + + it 'renders a 404 error' do + get :index + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + + describe 'GET #new' do + it 'renders the template' do + get :new + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template('new') + end + + context 'as a normal user' do + before do + sign_in(user) + end + + it 'renders a 404 error' do + get :new + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + + describe 'GET #edit' do + it 'renders the template' do + get :edit, params: { id: topic.id } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template('edit') + end + + context 'as a normal user' do + before do + sign_in(user) + end + + it 'renders a 404 error' do + get :edit, params: { id: topic.id } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + + describe 'POST #create' do + it 'creates topic' do + expect do + post :create, params: { projects_topic: { name: 'test' } } + end.to change { Projects::Topic.count }.by(1) + end + + it 'shows error message for invalid topic' do + post :create, params: { projects_topic: { name: nil } } + + errors = assigns[:topic].errors + expect(errors).to contain_exactly(errors.full_message(:name, I18n.t('errors.messages.blank'))) + end + + context 'as a normal user' do + before do + sign_in(user) + end + + it 'renders a 404 error' do + post :create, params: { projects_topic: { name: 'test' } } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + + describe 'PUT #update' do + it 'updates topic' do + put :update, params: { id: topic.id, projects_topic: { name: 'test' } } + + expect(response).to redirect_to(edit_admin_topic_path(topic)) + expect(topic.reload.name).to eq('test') + end + + it 'shows error message for invalid topic' do + put :update, params: { id: topic.id, projects_topic: { name: nil } } + + errors = assigns[:topic].errors + expect(errors).to contain_exactly(errors.full_message(:name, I18n.t('errors.messages.blank'))) + end + + context 'as a normal user' do + before do + sign_in(user) + end + + it 'renders a 404 error' do + put :update, params: { id: topic.id, projects_topic: { name: 'test' } } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end +end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 218aa04dd3f..e9a49319f21 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -704,7 +704,7 @@ RSpec.describe ApplicationController do get :index - expect(response.headers['Cache-Control']).to eq 'no-store' + expect(response.headers['Cache-Control']).to eq 'private, no-store' expect(response.headers['Pragma']).to eq 'no-cache' end @@ -740,7 +740,7 @@ RSpec.describe ApplicationController do it 'sets no-cache headers', :aggregate_failures do subject - expect(response.headers['Cache-Control']).to eq 'no-store' + expect(response.headers['Cache-Control']).to eq 'private, no-store' expect(response.headers['Pragma']).to eq 'no-cache' expect(response.headers['Expires']).to eq 'Fri, 01 Jan 1990 00:00:00 GMT' end @@ -967,6 +967,14 @@ RSpec.describe ApplicationController do end end + describe '.endpoint_id_for_action' do + controller(described_class) { } + + it 'returns an expected endpoint id' do + expect(controller.class.endpoint_id_for_action('hello')).to eq('AnonymousController#hello') + end + end + describe '#current_user' do controller(described_class) do def index; end diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb index cc60ab16d2e..b2200050e41 100644 --- a/spec/controllers/boards/issues_controller_spec.rb +++ b/spec/controllers/boards/issues_controller_spec.rb @@ -116,7 +116,7 @@ RSpec.describe Boards::IssuesController do it 'does not query issues table more than once' do recorder = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: board, list: list1) } - query_count = recorder.occurrences.select { |query,| query.start_with?('SELECT issues.*') }.each_value.first + query_count = recorder.occurrences.select { |query,| query.match?(/FROM "?issues"?/) }.each_value.first expect(query_count).to eq(1) end diff --git a/spec/controllers/concerns/group_tree_spec.rb b/spec/controllers/concerns/group_tree_spec.rb index a0707688e54..e808f1caa6e 100644 --- a/spec/controllers/concerns/group_tree_spec.rb +++ b/spec/controllers/concerns/group_tree_spec.rb @@ -21,82 +21,94 @@ RSpec.describe GroupTree do end describe 'GET #index' do - it 'filters groups' do - other_group = create(:group, name: 'filter') - other_group.add_owner(user) + shared_examples 'returns filtered groups' do + it 'filters groups' do + other_group = create(:group, name: 'filter') + other_group.add_owner(user) - get :index, params: { filter: 'filt' }, format: :json + get :index, params: { filter: 'filt' }, format: :json - expect(assigns(:groups)).to contain_exactly(other_group) - end + expect(assigns(:groups)).to contain_exactly(other_group) + end - context 'for subgroups' do - it 'only renders root groups when no parent was given' do - create(:group, :public, parent: group) + context 'for subgroups' do + it 'only renders root groups when no parent was given' do + create(:group, :public, parent: group) - get :index, format: :json + get :index, format: :json - expect(assigns(:groups)).to contain_exactly(group) - end + expect(assigns(:groups)).to contain_exactly(group) + end - it 'contains only the subgroup when a parent was given' do - subgroup = create(:group, :public, parent: group) + it 'contains only the subgroup when a parent was given' do + subgroup = create(:group, :public, parent: group) - get :index, params: { parent_id: group.id }, format: :json + get :index, params: { parent_id: group.id }, format: :json - expect(assigns(:groups)).to contain_exactly(subgroup) - end + expect(assigns(:groups)).to contain_exactly(subgroup) + end - it 'allows filtering for subgroups and includes the parents for rendering' do - subgroup = create(:group, :public, parent: group, name: 'filter') + it 'allows filtering for subgroups and includes the parents for rendering' do + subgroup = create(:group, :public, parent: group, name: 'filter') - get :index, params: { filter: 'filt' }, format: :json + get :index, params: { filter: 'filt' }, format: :json - expect(assigns(:groups)).to contain_exactly(group, subgroup) - end + expect(assigns(:groups)).to contain_exactly(group, subgroup) + end - it 'does not include groups the user does not have access to' do - parent = create(:group, :private) - subgroup = create(:group, :private, parent: parent, name: 'filter') - subgroup.add_developer(user) - _other_subgroup = create(:group, :private, parent: parent, name: 'filte') + it 'does not include groups the user does not have access to' do + parent = create(:group, :private) + subgroup = create(:group, :private, parent: parent, name: 'filter') + subgroup.add_developer(user) + _other_subgroup = create(:group, :private, parent: parent, name: 'filte') - get :index, params: { filter: 'filt' }, format: :json + get :index, params: { filter: 'filt' }, format: :json - expect(assigns(:groups)).to contain_exactly(parent, subgroup) - end + expect(assigns(:groups)).to contain_exactly(parent, subgroup) + end - it 'preloads parents regardless of pagination' do - allow(Kaminari.config).to receive(:default_per_page).and_return(1) - group = create(:group, :public) - subgroup = create(:group, :public, parent: group) - search_result = create(:group, :public, name: 'result', parent: subgroup) + it 'preloads parents regardless of pagination' do + allow(Kaminari.config).to receive(:default_per_page).and_return(1) + group = create(:group, :public) + subgroup = create(:group, :public, parent: group) + search_result = create(:group, :public, name: 'result', parent: subgroup) - get :index, params: { filter: 'resu' }, format: :json + get :index, params: { filter: 'resu' }, format: :json - expect(assigns(:groups)).to contain_exactly(group, subgroup, search_result) + expect(assigns(:groups)).to contain_exactly(group, subgroup, search_result) + end end - end - context 'json content' do - it 'shows groups as json' do - get :index, format: :json + context 'json content' do + it 'shows groups as json' do + get :index, format: :json - expect(json_response.first['id']).to eq(group.id) - end + expect(json_response.first['id']).to eq(group.id) + end - context 'nested groups' do - it 'expands the tree when filtering' do - subgroup = create(:group, :public, parent: group, name: 'filter') + context 'nested groups' do + it 'expands the tree when filtering' do + subgroup = create(:group, :public, parent: group, name: 'filter') - get :index, params: { filter: 'filt' }, format: :json + get :index, params: { filter: 'filt' }, format: :json - children_response = json_response.first['children'] + children_response = json_response.first['children'] - expect(json_response.first['id']).to eq(group.id) - expect(children_response.first['id']).to eq(subgroup.id) + expect(json_response.first['id']).to eq(group.id) + expect(children_response.first['id']).to eq(subgroup.id) + end end end end + + it_behaves_like 'returns filtered groups' + + context 'when feature flag :linear_group_tree_ancestor_scopes is disabled' do + before do + stub_feature_flags(linear_group_tree_ancestor_scopes: false) + end + + it_behaves_like 'returns filtered groups' + end end end diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb index 899aa7a41c1..2d41bc431ec 100644 --- a/spec/controllers/dashboard/milestones_controller_spec.rb +++ b/spec/controllers/dashboard/milestones_controller_spec.rb @@ -65,11 +65,12 @@ RSpec.describe Dashboard::MilestonesController do expect(response.body).not_to include(project_milestone.title) end - it 'shows counts of open and closed group and project milestones to which the user belongs to' do + it 'shows counts of open/closed/all group and project milestones to which the user belongs to' do get :index - expect(response.body).to include("Open\n<span class=\"badge badge-pill\">2</span>") - expect(response.body).to include("Closed\n<span class=\"badge badge-pill\">2</span>") + expect(response.body).to have_content('Open 2') + expect(response.body).to have_content('Closed 2') + expect(response.body).to have_content('All 4') end context 'external authorization' do diff --git a/spec/controllers/every_controller_spec.rb b/spec/controllers/every_controller_spec.rb index a1c377eff76..902872b6e92 100644 --- a/spec/controllers/every_controller_spec.rb +++ b/spec/controllers/every_controller_spec.rb @@ -1,24 +1,14 @@ # frozen_string_literal: true require 'spec_helper' - RSpec.describe "Every controller" do context "feature categories" do let_it_be(:feature_categories) do - YAML.load_file(Rails.root.join('config', 'feature_categories.yml')).map(&:to_sym).to_set + Gitlab::FeatureCategories.default.categories.map(&:to_sym).to_set end let_it_be(:controller_actions) do - # This will return tuples of all controller actions defined in the routes - # Only for controllers inheriting ApplicationController - # Excluding controllers from gems (OAuth, Sidekiq) - Rails.application.routes.routes - .map { |route| route.required_defaults.presence } - .compact - .select { |route| route[:controller].present? && route[:action].present? } - .map { |route| [constantize_controller(route[:controller]), route[:action]] } - .select { |(controller, action)| controller&.include?(::Gitlab::WithFeatureCategory) } - .reject { |(controller, action)| controller == ApplicationController || controller == Devise::UnlocksController } + Gitlab::RequestEndpoints.all_controller_actions end let_it_be(:routes_without_category) do @@ -74,9 +64,6 @@ RSpec.describe "Every controller" do end def actions_defined_in_feature_category_config(controller) - controller.send(:class_attributes)[:feature_category_config] - .values - .flatten - .map(&:to_s) + controller.send(:class_attributes)[:endpoint_attributes_config].defined_actions end end diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index aed97a01a72..6e7bcfdaa08 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -38,6 +38,14 @@ RSpec.describe GraphqlController do sign_in(user) end + it 'sets feature category in ApplicationContext from request' do + request.headers["HTTP_X_GITLAB_FEATURE_CATEGORY"] = "web_ide" + + post :execute + + expect(::Gitlab::ApplicationContext.current_context_attribute(:feature_category)).to eq('web_ide') + end + it 'returns 200 when user can access API' do post :execute diff --git a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb index 7415c2860c8..fa402d556c7 100644 --- a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb +++ b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Groups::DependencyProxyForContainersController do include HttpBasicAuthHelpers include DependencyProxyHelpers + include WorkhorseHelpers let_it_be(:user) { create(:user) } let_it_be_with_reload(:group) { create(:group, :private) } @@ -242,16 +243,9 @@ RSpec.describe Groups::DependencyProxyForContainersController do end describe 'GET #blob' do - let_it_be(:blob) { create(:dependency_proxy_blob) } + let(:blob) { create(:dependency_proxy_blob, group: group) } let(:blob_sha) { blob.file_name.sub('.gz', '') } - let(:blob_response) { { status: :success, blob: blob, from_cache: false } } - - before do - allow_next_instance_of(DependencyProxy::FindOrCreateBlobService) do |instance| - allow(instance).to receive(:execute).and_return(blob_response) - end - end subject { get_blob } @@ -264,40 +258,31 @@ RSpec.describe Groups::DependencyProxyForContainersController do it_behaves_like 'without permission' it_behaves_like 'feature flag disabled with private group' - context 'remote blob request fails' do - let(:blob_response) do - { - status: :error, - http_status: 400, - message: '' - } - end - - before do - group.add_guest(user) - end - - it 'proxies status from the remote blob request', :aggregate_failures do - subject - - expect(response).to have_gitlab_http_status(:bad_request) - expect(response.body).to be_empty - end - end - context 'a valid user' do before do group.add_guest(user) end it_behaves_like 'a successful blob pull' - it_behaves_like 'a package tracking event', described_class.name, 'pull_blob' + it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache' - context 'with a cache entry' do - let(:blob_response) { { status: :success, blob: blob, from_cache: true } } + context 'when cache entry does not exist' do + let(:blob_sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' } - it_behaves_like 'returning response status', :success - it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache' + it 'returns Workhorse send-dependency instructions' do + subject + + send_data_type, send_data = workhorse_send_data + header, url = send_data.values_at('Header', 'Url') + + expect(send_data_type).to eq('send-dependency') + expect(header).to eq("Authorization" => ["Bearer abcd1234"]) + expect(url).to eq(DependencyProxy::Registry.blob_url('alpine', blob_sha)) + expect(response.headers['Content-Type']).to eq('application/gzip') + expect(response.headers['Content-Disposition']).to eq( + ActionDispatch::Http::ContentDisposition.format(disposition: 'attachment', filename: blob.file_name) + ) + end end end @@ -319,6 +304,74 @@ RSpec.describe Groups::DependencyProxyForContainersController do it_behaves_like 'a successful blob pull' end end + + context 'when dependency_proxy_workhorse disabled' do + let(:blob_response) { { status: :success, blob: blob, from_cache: false } } + + before do + stub_feature_flags(dependency_proxy_workhorse: false) + + allow_next_instance_of(DependencyProxy::FindOrCreateBlobService) do |instance| + allow(instance).to receive(:execute).and_return(blob_response) + end + end + + context 'remote blob request fails' do + let(:blob_response) do + { + status: :error, + http_status: 400, + message: '' + } + end + + before do + group.add_guest(user) + end + + it 'proxies status from the remote blob request', :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(:bad_request) + expect(response.body).to be_empty + end + end + + context 'a valid user' do + before do + group.add_guest(user) + end + + it_behaves_like 'a successful blob pull' + it_behaves_like 'a package tracking event', described_class.name, 'pull_blob' + + context 'with a cache entry' do + let(:blob_response) { { status: :success, blob: blob, from_cache: true } } + + it_behaves_like 'returning response status', :success + it_behaves_like 'a package tracking event', described_class.name, 'pull_blob_from_cache' + end + end + + context 'a valid deploy token' do + let_it_be(:user) { create(:deploy_token, :group, :dependency_proxy_scopes) } + let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) } + + it_behaves_like 'a successful blob pull' + + context 'pulling from a subgroup' do + let_it_be_with_reload(:parent_group) { create(:group) } + let_it_be_with_reload(:group) { create(:group, parent: parent_group) } + + before do + parent_group.create_dependency_proxy_setting!(enabled: true) + group_deploy_token.update_column(:group_id, parent_group.id) + end + + it_behaves_like 'a successful blob pull' + end + end + end end it_behaves_like 'not found when disabled' @@ -328,6 +381,61 @@ RSpec.describe Groups::DependencyProxyForContainersController do end end + describe 'GET #authorize_upload_blob' do + let(:blob_sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' } + + subject(:authorize_upload_blob) do + request.headers.merge!(workhorse_internal_api_request_header) + + get :authorize_upload_blob, params: { group_id: group.to_param, image: 'alpine', sha: blob_sha } + end + + it_behaves_like 'without permission' + + context 'with a valid user' do + before do + group.add_guest(user) + end + + it 'sends Workhorse file upload instructions', :aggregate_failures do + authorize_upload_blob + + expect(response.headers['Content-Type']).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) + expect(json_response['TempPath']).to eq(DependencyProxy::FileUploader.workhorse_local_upload_path) + end + end + end + + describe 'GET #upload_blob' do + let(:blob_sha) { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4' } + let(:file) { fixture_file_upload("spec/fixtures/dependency_proxy/#{blob_sha}.gz", 'application/gzip') } + + subject do + request.headers.merge!(workhorse_internal_api_request_header) + + get :upload_blob, params: { + group_id: group.to_param, + image: 'alpine', + sha: blob_sha, + file: file + } + end + + it_behaves_like 'without permission' + + context 'with a valid user' do + before do + group.add_guest(user) + + expect_next_found_instance_of(Group) do |instance| + expect(instance).to receive_message_chain(:dependency_proxy_blobs, :create!) + end + end + + it_behaves_like 'a package tracking event', described_class.name, 'pull_blob' + end + end + def enable_dependency_proxy group.create_dependency_proxy_setting!(enabled: true) end diff --git a/spec/controllers/groups/registry/repositories_controller_spec.rb b/spec/controllers/groups/registry/repositories_controller_spec.rb index f4541eda293..9ac19b06718 100644 --- a/spec/controllers/groups/registry/repositories_controller_spec.rb +++ b/spec/controllers/groups/registry/repositories_controller_spec.rb @@ -19,6 +19,7 @@ RSpec.describe Groups::Registry::RepositoriesController do before do stub_container_registry_config(enabled: true) stub_container_registry_tags(repository: :any, tags: []) + stub_container_registry_info group.add_owner(user) group.add_guest(guest) sign_in(user) @@ -37,6 +38,18 @@ RSpec.describe Groups::Registry::RepositoriesController do 'name' => repo.name ) end + + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is a #{error_class}" do + it 'displays a connection error message' do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + + subject + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end shared_examples 'with name parameter' do @@ -71,6 +84,18 @@ RSpec.describe Groups::Registry::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) expect_no_snowplow_event end + + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is an invalid path error #{error_class}" do + it 'displays a connection error message' do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + + subject + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'json format' do diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb index 599e82afe9b..4e2123c8cc4 100644 --- a/spec/controllers/help_controller_spec.rb +++ b/spec/controllers/help_controller_spec.rb @@ -34,14 +34,6 @@ RSpec.describe HelpController do is_expected.to redirect_to("#{documentation_base_url}/ee/#{path}.html") end end - - context 'when feature flag is disabled' do - before do - stub_feature_flags(help_page_documentation_redirect: false) - end - - it_behaves_like 'documentation pages local render' - end end before do diff --git a/spec/controllers/import/bulk_imports_controller_spec.rb b/spec/controllers/import/bulk_imports_controller_spec.rb index 3b2ed2c63ed..3adba32c74a 100644 --- a/spec/controllers/import/bulk_imports_controller_spec.rb +++ b/spec/controllers/import/bulk_imports_controller_spec.rb @@ -51,62 +51,87 @@ RSpec.describe Import::BulkImportsController do end describe 'GET status' do + def get_status(params_override = {}) + params = { page: 1, per_page: 20, filter: '' }.merge(params_override) + + get :status, + params: params, + format: :json, + session: { + bulk_import_gitlab_url: 'https://gitlab.example.com', + bulk_import_gitlab_access_token: 'demo-pat' + } + end + + include_context 'bulk imports requests context', 'https://gitlab.example.com' + let(:client) { BulkImports::Clients::HTTP.new(url: 'http://gitlab.example', token: 'token') } + let(:version) { "#{BulkImport::MIN_MAJOR_VERSION}.#{BulkImport::MIN_MINOR_VERSION_FOR_PROJECT}.0" } + let(:version_response) { double(code: 200, success?: true, parsed_response: { 'version' => version }) } describe 'serialized group data' do - let(:client_response) do + let(:expected_response) do double( parsed_response: [ - { 'id' => 1, 'full_name' => 'group1', 'full_path' => 'full/path/group1', 'web_url' => 'http://demo.host/full/path/group1' }, - { 'id' => 2, 'full_name' => 'group2', 'full_path' => 'full/path/group2', 'web_url' => 'http://demo.host/full/path/group1' } + { + "full_name" => "Stub", + "full_path" => "stub-group", + "id" => 2595438, + "web_url" => "https://gitlab.com/groups/auto-breakfast" + } ], headers: { 'x-next-page' => '2', 'x-page' => '1', 'x-per-page' => '20', - 'x-total' => '37', + 'x-total' => '42', 'x-total-pages' => '2' } ) end - let(:client_params) do - { - top_level_only: true, - min_access_level: Gitlab::Access::OWNER - } - end - - before do - allow(controller).to receive(:client).and_return(client) - allow(client).to receive(:get).with('groups', client_params).and_return(client_response) - end - it 'returns serialized group data' do - get :status, format: :json + get_status + + version_validation = { + "features" => { + "project_migration" => { + "available" => true, + "min_version" => BulkImport.min_gl_version_for_project_migration.to_s + }, + "source_instance_version" => version + } + } - expect(json_response).to eq({ importable_data: client_response.parsed_response }.as_json) + expect(json_response).to include("importable_data" => expected_response.parsed_response, "version_validation" => hash_including(version_validation)) end it 'forwards pagination headers' do - get :status, format: :json - - expect(response.headers['x-per-page']).to eq client_response.headers['x-per-page'] - expect(response.headers['x-page']).to eq client_response.headers['x-page'] - expect(response.headers['x-next-page']).to eq client_response.headers['x-next-page'] - expect(response.headers['x-prev-page']).to eq client_response.headers['x-prev-page'] - expect(response.headers['x-total']).to eq client_response.headers['x-total'] - expect(response.headers['x-total-pages']).to eq client_response.headers['x-total-pages'] + get_status + + expect(response.headers['x-per-page']).to eq expected_response.headers['x-per-page'] + expect(response.headers['x-page']).to eq expected_response.headers['x-page'] + expect(response.headers['x-next-page']).to eq expected_response.headers['x-next-page'] + expect(response.headers['x-prev-page']).to eq expected_response.headers['x-prev-page'] + expect(response.headers['x-total']).to eq expected_response.headers['x-total'] + expect(response.headers['x-total-pages']).to eq expected_response.headers['x-total-pages'] end context 'when filtering' do - it 'returns filtered result' do - filter = 'test' - search_params = client_params.merge(search: filter) + let_it_be(:filter) { 'test' } - expect(client).to receive(:get).with('groups', search_params).and_return(client_response) + let(:client_params) do + { + top_level_only: true, + min_access_level: Gitlab::Access::OWNER, + search: filter + } + end + + it 'returns filtered result' do + get_status(filter: filter) - get :status, format: :json, params: { filter: filter } + expect(json_response['importable_data'].first['full_name']).to eq('Test') end end end @@ -148,18 +173,19 @@ RSpec.describe Import::BulkImportsController do context 'when connection error occurs' do before do - allow(controller).to receive(:client).and_return(client) - allow(client).to receive(:get).and_raise(BulkImports::Error) + allow_next_instance_of(BulkImports::Clients::HTTP) do |instance| + allow(instance).to receive(:get).and_raise(BulkImports::Error) + end end it 'returns 422' do - get :status, format: :json + get_status expect(response).to have_gitlab_http_status(:unprocessable_entity) end it 'clears session' do - get :status, format: :json + get_status expect(session[:gitlab_url]).to be_nil expect(session[:gitlab_access_token]).to be_nil @@ -199,9 +225,9 @@ RSpec.describe Import::BulkImportsController do session[:bulk_import_gitlab_url] = instance_url end - it 'executes BulkImportService' do + it 'executes BulkImpors::CreatetService' do expect_next_instance_of( - BulkImportService, user, bulk_import_params, { url: instance_url, access_token: pat }) do |service| + ::BulkImports::CreateService, user, bulk_import_params, { url: instance_url, access_token: pat }) do |service| allow(service).to receive(:execute).and_return(ServiceResponse.success(payload: bulk_import)) end @@ -214,7 +240,7 @@ RSpec.describe Import::BulkImportsController do it 'returns error when validation fails' do error_response = ServiceResponse.error(message: 'Record invalid', http_status: :unprocessable_entity) expect_next_instance_of( - BulkImportService, user, bulk_import_params, { url: instance_url, access_token: pat }) do |service| + ::BulkImports::CreateService, user, bulk_import_params, { url: instance_url, access_token: pat }) do |service| allow(service).to receive(:execute).and_return(error_response) end diff --git a/spec/controllers/jira_connect/app_descriptor_controller_spec.rb b/spec/controllers/jira_connect/app_descriptor_controller_spec.rb index 25c11d92b4e..9d890efdd33 100644 --- a/spec/controllers/jira_connect/app_descriptor_controller_spec.rb +++ b/spec/controllers/jira_connect/app_descriptor_controller_spec.rb @@ -46,7 +46,8 @@ RSpec.describe JiraConnect::AppDescriptorController do apiVersion: 1, apiMigrations: { 'context-qsh': true, - gdpr: true + gdpr: true, + 'signed-install': true } ) @@ -89,5 +90,17 @@ RSpec.describe JiraConnect::AppDescriptorController do ) ) end + + context 'when jira_connect_asymmetric_jwt is disabled' do + before do + stub_feature_flags(jira_connect_asymmetric_jwt: false) + end + + specify do + get :show + + expect(json_response).to include('apiMigrations' => include('signed-install' => false)) + end + end end end diff --git a/spec/controllers/jira_connect/events_controller_spec.rb b/spec/controllers/jira_connect/events_controller_spec.rb index e9fecb594a7..78bd0dc8318 100644 --- a/spec/controllers/jira_connect/events_controller_spec.rb +++ b/spec/controllers/jira_connect/events_controller_spec.rb @@ -3,9 +3,49 @@ require 'spec_helper' RSpec.describe JiraConnect::EventsController do + shared_examples 'verifies asymmetric JWT token' do + context 'when token is valid' do + include_context 'valid JWT token' + + it 'renders successful' do + send_request + + expect(response).to have_gitlab_http_status(:success) + end + end + + context 'when token is invalid' do + include_context 'invalid JWT token' + + it 'renders unauthorized' do + send_request + + expect(response).to have_gitlab_http_status(:unauthorized) + end + end + end + + shared_context 'valid JWT token' do + before do + allow_next_instance_of(Atlassian::JiraConnect::AsymmetricJwt) do |asymmetric_jwt| + allow(asymmetric_jwt).to receive(:valid?).and_return(true) + allow(asymmetric_jwt).to receive(:iss_claim).and_return(client_key) + end + end + end + + shared_context 'invalid JWT token' do + before do + allow_next_instance_of(Atlassian::JiraConnect::AsymmetricJwt) do |asymmetric_jwt| + allow(asymmetric_jwt).to receive(:valid?).and_return(false) + end + end + end + describe '#installed' do let(:client_key) { '1234' } let(:shared_secret) { 'secret' } + let(:params) do { clientKey: client_key, @@ -14,10 +54,16 @@ RSpec.describe JiraConnect::EventsController do } end + include_context 'valid JWT token' + subject do post :installed, params: params end + it_behaves_like 'verifies asymmetric JWT token' do + let(:send_request) { subject } + end + it 'saves the jira installation data' do expect { subject }.to change { JiraConnectInstallation.count }.by(1) end @@ -31,13 +77,15 @@ RSpec.describe JiraConnect::EventsController do expect(installation.base_url).to eq('https://test.atlassian.net') end - context 'client key already exists' do - it 'returns 422' do - create(:jira_connect_installation, client_key: client_key) + context 'when jira_connect_asymmetric_jwt is disabled' do + before do + stub_feature_flags(jira_connect_asymmetric_jwt: false) + end - subject + it 'saves the jira installation data without JWT validation' do + expect(Atlassian::JiraConnect::AsymmetricJwt).not_to receive(:new) - expect(response).to have_gitlab_http_status(:unprocessable_entity) + expect { subject }.to change { JiraConnectInstallation.count }.by(1) end end @@ -49,27 +97,68 @@ RSpec.describe JiraConnect::EventsController do } end - it 'validates the JWT token in authorization header and returns 200 without creating a new installation' do - create(:jira_connect_installation, client_key: client_key, shared_secret: shared_secret) - request.headers["Authorization"] = "Bearer #{Atlassian::Jwt.encode({ iss: client_key }, shared_secret)}" + it 'returns 422' do + subject - expect { subject }.not_to change { JiraConnectInstallation.count } - expect(response).to have_gitlab_http_status(:ok) + expect(response).to have_gitlab_http_status(:unprocessable_entity) end - end - describe '#uninstalled' do - let!(:installation) { create(:jira_connect_installation) } - let(:qsh) { Atlassian::Jwt.create_query_string_hash('https://gitlab.test/events/uninstalled', 'POST', 'https://gitlab.test') } + context 'and an installation exists' do + let!(:installation) { create(:jira_connect_installation, client_key: client_key, shared_secret: shared_secret) } - before do - request.headers['Authorization'] = "JWT #{auth_token}" + it 'validates the JWT token in authorization header and returns 200 without creating a new installation' do + expect { subject }.not_to change { JiraConnectInstallation.count } + expect(response).to have_gitlab_http_status(:ok) + end + + context 'when jira_connect_asymmetric_jwt is disabled' do + before do + stub_feature_flags(jira_connect_asymmetric_jwt: false) + end + + it 'decodes the JWT token in authorization header and returns 200 without creating a new installation' do + request.headers["Authorization"] = "Bearer #{Atlassian::Jwt.encode({ iss: client_key }, shared_secret)}" + + expect(Atlassian::JiraConnect::AsymmetricJwt).not_to receive(:new) + + expect { subject }.not_to change { JiraConnectInstallation.count } + + expect(response).to have_gitlab_http_status(:ok) + end + end end + end + end + + describe '#uninstalled' do + let_it_be(:installation) { create(:jira_connect_installation) } + + let(:client_key) { installation.client_key } + let(:params) do + { + clientKey: client_key, + baseUrl: 'https://test.atlassian.net' + } + end + + it_behaves_like 'verifies asymmetric JWT token' do + let(:send_request) { post :uninstalled, params: params } + end + + subject(:post_uninstalled) { post :uninstalled, params: params } - subject(:post_uninstalled) { post :uninstalled } + context 'when JWT is invalid' do + include_context 'invalid JWT token' - context 'when JWT is invalid' do - let(:auth_token) { 'invalid_token' } + it 'does not delete the installation' do + expect { post_uninstalled }.not_to change { JiraConnectInstallation.count } + end + + context 'when jira_connect_asymmetric_jwt is disabled' do + before do + stub_feature_flags(jira_connect_asymmetric_jwt: false) + request.headers['Authorization'] = 'JWT invalid token' + end it 'returns 403' do post_uninstalled @@ -81,14 +170,42 @@ RSpec.describe JiraConnect::EventsController do expect { post_uninstalled }.not_to change { JiraConnectInstallation.count } end end + end + + context 'when JWT is valid' do + include_context 'valid JWT token' + + let(:jira_base_path) { '/-/jira_connect' } + let(:jira_event_path) { '/-/jira_connect/events/uninstalled' } + + it 'calls the DestroyService and returns ok in case of success' do + expect_next_instance_of(JiraConnectInstallations::DestroyService, installation, jira_base_path, jira_event_path) do |destroy_service| + expect(destroy_service).to receive(:execute).and_return(true) + end + + post_uninstalled + + expect(response).to have_gitlab_http_status(:ok) + end + + it 'calls the DestroyService and returns unprocessable_entity in case of failure' do + expect_next_instance_of(JiraConnectInstallations::DestroyService, installation, jira_base_path, jira_event_path) do |destroy_service| + expect(destroy_service).to receive(:execute).and_return(false) + end + + post_uninstalled + + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end + + context 'when jira_connect_asymmetric_jwt is disabled' do + before do + stub_feature_flags(jira_connect_asymmetric_jwt: false) - context 'when JWT is valid' do - let(:auth_token) do - Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) + request.headers['Authorization'] = "JWT #{Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret)}" end - let(:jira_base_path) { '/-/jira_connect' } - let(:jira_event_path) { '/-/jira_connect/events/uninstalled' } + let(:qsh) { Atlassian::Jwt.create_query_string_hash('https://gitlab.test/events/uninstalled', 'POST', 'https://gitlab.test') } it 'calls the DestroyService and returns ok in case of success' do expect_next_instance_of(JiraConnectInstallations::DestroyService, installation, jira_base_path, jira_event_path) do |destroy_service| diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb index 9fa90dde997..4f74af295c6 100644 --- a/spec/controllers/metrics_controller_spec.rb +++ b/spec/controllers/metrics_controller_spec.rb @@ -67,6 +67,12 @@ RSpec.describe MetricsController, :request_store do expect(response.body).to match(/^prometheus_counter 1$/) end + it 'initializes the rails request SLIs' do + expect(Gitlab::Metrics::RailsSlis).to receive(:initialize_request_slis_if_needed!).and_call_original + + get :index + end + context 'prometheus metrics are disabled' do before do allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(false) diff --git a/spec/controllers/profiles/two_factor_auths_controller_spec.rb b/spec/controllers/profiles/two_factor_auths_controller_spec.rb index ca63760d988..e57bd5be937 100644 --- a/spec/controllers/profiles/two_factor_auths_controller_spec.rb +++ b/spec/controllers/profiles/two_factor_auths_controller_spec.rb @@ -27,12 +27,6 @@ RSpec.describe Profiles::TwoFactorAuthsController do expect(flash[:notice]) .to eq _('You need to verify your primary email first before enabling Two-Factor Authentication.') end - - it 'does not redirect when the `ensure_verified_primary_email_for_2fa` feature flag is disabled' do - stub_feature_flags(ensure_verified_primary_email_for_2fa: false) - - expect(response).not_to redirect_to(profile_emails_path) - end end shared_examples 'user must enter a valid current password' do diff --git a/spec/controllers/profiles_controller_spec.rb b/spec/controllers/profiles_controller_spec.rb index b4019643baf..4959003d788 100644 --- a/spec/controllers/profiles_controller_spec.rb +++ b/spec/controllers/profiles_controller_spec.rb @@ -3,7 +3,8 @@ require('spec_helper') RSpec.describe ProfilesController, :request_store do - let(:user) { create(:user) } + let(:password) { 'longsecret987!' } + let(:user) { create(:user, password: password) } describe 'POST update' do it 'does not update password' do @@ -23,7 +24,7 @@ RSpec.describe ProfilesController, :request_store do sign_in(user) put :update, - params: { user: { email: "john@gmail.com", name: "John" } } + params: { user: { email: "john@gmail.com", name: "John", validation_password: password } } user.reload diff --git a/spec/controllers/projects/alerting/notifications_controller_spec.rb b/spec/controllers/projects/alerting/notifications_controller_spec.rb index fe0c4ce00bf..2fff8026b22 100644 --- a/spec/controllers/projects/alerting/notifications_controller_spec.rb +++ b/spec/controllers/projects/alerting/notifications_controller_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Projects::Alerting::NotificationsController do + include HttpBasicAuthHelpers + let_it_be(:project) { create(:project) } let_it_be(:environment) { create(:environment, project: project) } @@ -53,86 +55,96 @@ RSpec.describe Projects::Alerting::NotificationsController do end end - context 'bearer token' do - context 'when set' do - context 'when extractable' do - before do - request.headers['HTTP_AUTHORIZATION'] = 'Bearer some token' - end - - it 'extracts bearer token' do - expect(notify_service).to receive(:execute).with('some token', nil) - - make_request - end - - context 'with a corresponding integration' do - context 'with integration parameters specified' do - let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project) } + shared_examples 'a working token' do + it 'extracts token' do + expect(notify_service).to receive(:execute).with('some token', nil) - let(:params) { project_params(endpoint_identifier: integration.endpoint_identifier, name: integration.name) } - - context 'the integration is active' do - it 'extracts and finds the integration' do - expect(notify_service).to receive(:execute).with('some token', integration) + make_request + end - make_request - end - end + context 'with a corresponding integration' do + context 'with integration parameters specified' do + let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project) } - context 'when the integration is inactive' do - before do - integration.update!(active: false) - end + let(:params) { project_params(endpoint_identifier: integration.endpoint_identifier, name: integration.name) } - it 'does not find an integration' do - expect(notify_service).to receive(:execute).with('some token', nil) + context 'the integration is active' do + it 'extracts and finds the integration' do + expect(notify_service).to receive(:execute).with('some token', integration) - make_request - end - end + make_request end + end - context 'without integration parameters specified' do - let_it_be(:integration) { create(:alert_management_http_integration, :legacy, project: project) } + context 'when the integration is inactive' do + before do + integration.update!(active: false) + end - it 'extracts and finds the legacy integration' do - expect(notify_service).to receive(:execute).with('some token', integration) + it 'does not find an integration' do + expect(notify_service).to receive(:execute).with('some token', nil) - make_request - end + make_request end end end - context 'when inextractable' do - it 'passes nil for a non-bearer token' do - request.headers['HTTP_AUTHORIZATION'] = 'some token' + context 'without integration parameters specified' do + let_it_be(:integration) { create(:alert_management_http_integration, :legacy, project: project) } - expect(notify_service).to receive(:execute).with(nil, nil) + it 'extracts and finds the legacy integration' do + expect(notify_service).to receive(:execute).with('some token', integration) make_request end end end + end - context 'when missing' do - it 'passes nil' do - expect(notify_service).to receive(:execute).with(nil, nil) - - make_request + context 'with bearer token' do + context 'when set' do + before do + request.headers.merge(build_token_auth_header('some token')) end + + it_behaves_like 'a working token' + end + end + + context 'with basic auth token' do + before do + request.headers.merge basic_auth_header(nil, 'some token') + end + + it_behaves_like 'a working token' + end + + context 'when inextractable token' do + it 'passes nil for a non-bearer token' do + request.headers['HTTP_AUTHORIZATION'] = 'some token' + + expect(notify_service).to receive(:execute).with(nil, nil) + + make_request + end + end + + context 'when missing token' do + it 'passes nil' do + expect(notify_service).to receive(:execute).with(nil, nil) + + make_request end end end - context 'generic alert payload' do + context 'with generic alert payload' do it_behaves_like 'process alert payload', Projects::Alerting::NotifyService do let(:payload) { { title: 'Alert title' } } end end - context 'Prometheus alert payload' do + context 'with Prometheus alert payload' do include PrometheusHelpers it_behaves_like 'process alert payload', Projects::Prometheus::Alerts::NotifyService do diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index a00e302a64f..43e8bbd83cf 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -239,7 +239,7 @@ RSpec.describe Projects::BranchesController do end end - context 'without issue feature access' do + context 'without issue feature access', :sidekiq_inline do before do project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE) @@ -656,6 +656,26 @@ RSpec.describe Projects::BranchesController do ) end end + + context 'when gitaly is not available' do + before do + allow_next_instance_of(Gitlab::GitalyClient::RefService) do |ref_service| + allow(ref_service).to receive(:local_branches).and_raise(GRPC::DeadlineExceeded) + end + + get :index, format: :html, params: { + namespace_id: project.namespace, project_id: project + } + end + + it 'returns with a status 200' do + expect(response).to have_gitlab_http_status(:ok) + end + + it 'sets gitaly_unavailable variable' do + expect(assigns[:gitaly_unavailable]).to be_truthy + end + end end describe 'GET diverging_commit_counts' do diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index 2412b970342..48afd42e8ff 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -409,7 +409,7 @@ RSpec.describe Projects::CompareController do end end - context 'when the user does not have access to the project' do + context 'when the user does not have access to the project', :sidekiq_inline do before do project.team.truncate project.update!(visibility: 'private') diff --git a/spec/controllers/projects/design_management/designs/resized_image_controller_spec.rb b/spec/controllers/projects/design_management/designs/resized_image_controller_spec.rb index 56c0ef592ca..cc0f4a426f4 100644 --- a/spec/controllers/projects/design_management/designs/resized_image_controller_spec.rb +++ b/spec/controllers/projects/design_management/designs/resized_image_controller_spec.rb @@ -91,7 +91,7 @@ RSpec.describe Projects::DesignManagement::Designs::ResizedImageController do # (the record that represents the design at a specific version), to # verify that the correct file is being returned. def etag(action) - ActionDispatch::TestResponse.new.send(:generate_weak_etag, [action.cache_key, '']) + ActionDispatch::TestResponse.new.send(:generate_weak_etag, [action.cache_key]) end specify { expect(newest_version.sha).not_to eq(oldest_version.sha) } diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 977879b453c..0b3bd4d78ac 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1411,39 +1411,42 @@ RSpec.describe Projects::IssuesController do stub_application_setting(issues_create_limit: 5) end - it 'prevents from creating more issues', :request_store do - 5.times { post_new_issue } - - expect { post_new_issue } - .to change { Gitlab::GitalyClient.get_request_count }.by(1) # creates 1 projects and 0 issues - - post_new_issue - expect(response.body).to eq(_('This endpoint has been requested too many times. Try again later.')) - expect(response).to have_gitlab_http_status(:too_many_requests) - end - - it 'logs the event on auth.log' do - attributes = { - message: 'Application_Rate_Limiter_Request', - env: :issues_create_request_limit, - remote_ip: '0.0.0.0', - request_method: 'POST', - path: "/#{project.full_path}/-/issues", - user_id: user.id, - username: user.username - } + context 'when issue creation limits imposed' do + it 'prevents from creating more issues', :request_store do + 5.times { post_new_issue } - expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once + expect { post_new_issue } + .to change { Gitlab::GitalyClient.get_request_count }.by(1) # creates 1 projects and 0 issues - project.add_developer(user) - sign_in(user) + post_new_issue - 6.times do - post :create, params: { - namespace_id: project.namespace.to_param, - project_id: project, - issue: { title: 'Title', description: 'Description' } + expect(response.body).to eq(_('This endpoint has been requested too many times. Try again later.')) + expect(response).to have_gitlab_http_status(:too_many_requests) + end + + it 'logs the event on auth.log' do + attributes = { + message: 'Application_Rate_Limiter_Request', + env: :issues_create_request_limit, + remote_ip: '0.0.0.0', + request_method: 'POST', + path: "/#{project.full_path}/-/issues", + user_id: user.id, + username: user.username } + + expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once + + project.add_developer(user) + sign_in(user) + + 6.times do + post :create, params: { + namespace_id: project.namespace.to_param, + project_id: project, + issue: { title: 'Title', description: 'Description' } + } + 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 0da8a30611c..438fc2f2106 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -1876,8 +1876,7 @@ RSpec.describe Projects::MergeRequestsController do let(:sha) { forked.commit.sha } let(:environment) { create(:environment, project: forked) } let(:pipeline) { create(:ci_pipeline, sha: sha, project: forked) } - let(:build) { create(:ci_build, pipeline: pipeline) } - let!(:deployment) { create(:deployment, :succeed, environment: environment, sha: sha, ref: 'master', deployable: build) } + let!(:build) { create(:ci_build, :with_deployment, environment: environment.name, pipeline: pipeline) } let(:merge_request) do create(:merge_request, source_project: forked, target_project: project, target_branch: 'master', head_pipeline: pipeline) @@ -1901,8 +1900,7 @@ RSpec.describe Projects::MergeRequestsController do let(:source_environment) { create(:environment, project: project) } let(:merge_commit_sha) { project.repository.merge(user, forked.commit.id, merge_request, "merged in test") } let(:post_merge_pipeline) { create(:ci_pipeline, sha: merge_commit_sha, project: project) } - let(:post_merge_build) { create(:ci_build, pipeline: post_merge_pipeline) } - let!(:source_deployment) { create(:deployment, :succeed, environment: source_environment, sha: merge_commit_sha, ref: 'master', deployable: post_merge_build) } + let!(:post_merge_build) { create(:ci_build, :with_deployment, environment: source_environment.name, pipeline: post_merge_pipeline) } before do merge_request.update!(merge_commit_sha: merge_commit_sha) @@ -1944,9 +1942,6 @@ RSpec.describe Projects::MergeRequestsController do context 'when a merge request has multiple environments with deployments' do let(:sha) { merge_request.diff_head_sha } - let(:ref) { merge_request.source_branch } - - let!(:build) { create(:ci_build, pipeline: pipeline) } let!(:pipeline) { create(:ci_pipeline, sha: sha, project: project) } let!(:environment) { create(:environment, name: 'env_a', project: project) } let!(:another_environment) { create(:environment, name: 'env_b', project: project) } @@ -1954,8 +1949,8 @@ RSpec.describe Projects::MergeRequestsController do before do merge_request.update_head_pipeline - create(:deployment, :succeed, environment: environment, sha: sha, ref: ref, deployable: build) - create(:deployment, :succeed, environment: another_environment, sha: sha, ref: ref, deployable: build) + create(:ci_build, :with_deployment, environment: environment.name, pipeline: pipeline) + create(:ci_build, :with_deployment, environment: another_environment.name, pipeline: pipeline) end it 'exposes multiple environment statuses' do diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb index 27a3e95896a..d86f38c1f0b 100644 --- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb +++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb @@ -397,7 +397,7 @@ RSpec.describe Projects::PipelineSchedulesController do end end - describe 'POST #play', :clean_gitlab_redis_cache do + describe 'POST #play', :clean_gitlab_redis_rate_limiting do let(:ref) { 'master' } before do diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 2c25c7e20ea..a81173ccaac 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -84,7 +84,7 @@ RSpec.describe Projects::RawController do include_examples 'single Gitaly request' end - context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do + context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_rate_limiting do let(:file_path) { 'master/README.md' } before do diff --git a/spec/controllers/projects/registry/repositories_controller_spec.rb b/spec/controllers/projects/registry/repositories_controller_spec.rb index 0685e5a2055..a5faaaf5969 100644 --- a/spec/controllers/projects/registry/repositories_controller_spec.rb +++ b/spec/controllers/projects/registry/repositories_controller_spec.rb @@ -9,6 +9,7 @@ RSpec.describe Projects::Registry::RepositoriesController do before do sign_in(user) stub_container_registry_config(enabled: true) + stub_container_registry_info end context 'when user has access to registry' do @@ -30,6 +31,18 @@ RSpec.describe Projects::Registry::RepositoriesController do expect(response).to have_gitlab_http_status(:not_found) end + + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is a #{error_class}" do + it 'displays a connection error message' do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + + go_to_index + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end shared_examples 'renders a list of repositories' do diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index efb57494f82..d0719643b7f 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -17,6 +17,25 @@ RSpec.describe Projects::TagsController do expect(assigns(:tags).map(&:name)).to include('v1.1.0', 'v1.0.0') end + context 'when Gitaly is unavailable' do + where(:format) do + [:html, :atom] + end + + with_them do + it 'returns 503 status code' do + expect_next_instance_of(TagsFinder) do |finder| + expect(finder).to receive(:execute).and_return([[], Gitlab::Git::CommandError.new]) + end + + get :index, params: { namespace_id: project.namespace.to_param, project_id: project }, format: format + + expect(assigns(:tags)).to eq([]) + expect(response).to have_gitlab_http_status(:service_unavailable) + end + end + end + it 'returns releases matching those tags' do subject diff --git a/spec/controllers/projects/usage_quotas_controller_spec.rb b/spec/controllers/projects/usage_quotas_controller_spec.rb new file mode 100644 index 00000000000..6125ba13f96 --- /dev/null +++ b/spec/controllers/projects/usage_quotas_controller_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::UsageQuotasController do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, namespace: user.namespace) } + + describe 'GET #index' do + render_views + + it 'does not render search settings partial' do + sign_in(user) + get(:index, params: { namespace_id: user.namespace, project_id: project }) + + expect(response).to render_template('index') + expect(response).not_to render_template('shared/search_settings') + end + end +end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 9d070061850..3d966848c5b 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -312,6 +312,17 @@ RSpec.describe ProjectsController do expect { get_show }.not_to change { Gitlab::GitalyClient.get_request_count } end + + it "renders files even with invalid license" do + controller.instance_variable_set(:@project, public_project) + expect(public_project.repository).to receive(:license_key).and_return('woozle wuzzle').at_least(:once) + + get_show + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template('_files') + expect(response.body).to have_content('LICENSE') # would be 'MIT license' if stub not works + end end context "when the url contains .atom" do @@ -409,42 +420,66 @@ RSpec.describe ProjectsController do end describe 'POST create' do - let!(:params) do - { - path: 'foo', - description: 'bar', - import_url: project.http_url_to_repo, - namespace_id: user.namespace.id - } - end - subject { post :create, params: { project: params } } before do sign_in(user) end - context 'when import by url is disabled' do - before do - stub_application_setting(import_sources: []) + context 'on import' do + let(:params) do + { + path: 'foo', + description: 'bar', + namespace_id: user.namespace.id, + import_url: project.http_url_to_repo + } + end + + context 'when import by url is disabled' do + before do + stub_application_setting(import_sources: []) + end + + it 'does not create project and reports an error' do + expect { subject }.not_to change { Project.count } + + expect(response).to have_gitlab_http_status(:not_found) + end end - it 'does not create project and reports an error' do - expect { subject }.not_to change { Project.count } + context 'when import by url is enabled' do + before do + stub_application_setting(import_sources: ['git']) + end + + it 'creates project' do + expect { subject }.to change { Project.count } - expect(response).to have_gitlab_http_status(:not_found) + expect(response).to have_gitlab_http_status(:redirect) + end end end - context 'when import by url is enabled' do - before do - stub_application_setting(import_sources: ['git']) + context 'with new_project_sast_enabled', :experiment do + let(:params) do + { + path: 'foo', + description: 'bar', + namespace_id: user.namespace.id, + initialize_with_sast: '1' + } end - it 'creates project' do - expect { subject }.to change { Project.count } + it 'tracks an event on project creation' do + expect(experiment(:new_project_sast_enabled)).to track(:created, + property: 'blank', + checked: true, + project: an_instance_of(Project), + namespace: user.namespace + ).on_next_instance.with_context(user: user) - expect(response).to have_gitlab_http_status(:redirect) + post :create, params: { project: params } end end end @@ -1373,12 +1408,12 @@ RSpec.describe ProjectsController do end end - context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do + context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_rate_limiting do include_examples 'rate limits project export endpoint' end end - describe '#download_export', :clean_gitlab_redis_cache do + describe '#download_export', :clean_gitlab_redis_rate_limiting do let(:action) { :download_export } context 'object storage enabled' do @@ -1413,7 +1448,7 @@ RSpec.describe ProjectsController do end end - context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do + context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_rate_limiting do before do allow(Gitlab::ApplicationRateLimiter) .to receive(:increment) @@ -1485,7 +1520,7 @@ RSpec.describe ProjectsController do end end - context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_cache do + context 'when the endpoint receives requests above the limit', :clean_gitlab_redis_rate_limiting do include_examples 'rate limits project export endpoint' end end diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index 5edd60ebc79..a25c597edb2 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -602,6 +602,22 @@ RSpec.describe RegistrationsController do end end + context 'when user did not accept app terms' do + let(:user) { create(:user, accepted_term: nil) } + + before do + stub_application_setting(password_authentication_enabled_for_web: false) + stub_application_setting(password_authentication_enabled_for_git: false) + stub_application_setting(enforce_terms: true) + end + + it 'fails with message' do + post :destroy, params: { username: user.username } + + expect_failure(s_('Profiles|You must accept the Terms of Service in order to perform this action.')) + end + end + it 'sets the username and caller_id in the context' do expect(controller).to receive(:destroy).and_wrap_original do |m, *args| m.call(*args) diff --git a/spec/controllers/repositories/git_http_controller_spec.rb b/spec/controllers/repositories/git_http_controller_spec.rb index 04d5008cb34..b5cd14154a3 100644 --- a/spec/controllers/repositories/git_http_controller_spec.rb +++ b/spec/controllers/repositories/git_http_controller_spec.rb @@ -7,12 +7,33 @@ RSpec.describe Repositories::GitHttpController do let_it_be(:personal_snippet) { create(:personal_snippet, :public, :repository) } let_it_be(:project_snippet) { create(:project_snippet, :public, :repository, project: project) } + shared_examples 'handles unavailable Gitaly' do + let(:params) { super().merge(service: 'git-upload-pack') } + + before do + request.headers.merge! auth_env(user.username, user.password, nil) + end + + context 'when Gitaly is unavailable' do + it 'responds with a 503 message' do + expect(Gitlab::GitalyClient).to receive(:call).and_raise(GRPC::Unavailable) + + get :info_refs, params: params + + expect(response).to have_gitlab_http_status(:service_unavailable) + expect(response.body).to eq('The git server, Gitaly, is not available at this time. Please contact your administrator.') + end + end + end + context 'when repository container is a project' do it_behaves_like Repositories::GitHttpController do let(:container) { project } let(:user) { project.owner } let(:access_checker_class) { Gitlab::GitAccess } + it_behaves_like 'handles unavailable Gitaly' + describe 'POST #git_upload_pack' do before do allow(controller).to receive(:verify_workhorse_api!).and_return(true) @@ -84,6 +105,8 @@ RSpec.describe Repositories::GitHttpController do let(:container) { personal_snippet } let(:user) { personal_snippet.author } let(:access_checker_class) { Gitlab::GitAccessSnippet } + + it_behaves_like 'handles unavailable Gitaly' end end @@ -92,6 +115,8 @@ RSpec.describe Repositories::GitHttpController do let(:container) { project_snippet } let(:user) { project_snippet.author } let(:access_checker_class) { Gitlab::GitAccessSnippet } + + it_behaves_like 'handles unavailable Gitaly' end end end diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 4e87a9fc1ba..73e8e0c7dd4 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -215,6 +215,16 @@ RSpec.describe SearchController do end end + it 'strips surrounding whitespace from search query' do + get :show, params: { scope: 'notes', search: ' foobar ' } + expect(assigns[:search_term]).to eq 'foobar' + end + + it 'strips surrounding whitespace from autocomplete term' do + expect(controller).to receive(:search_autocomplete_opts).with('youcompleteme') + get :autocomplete, params: { term: ' youcompleteme ' } + end + it 'finds issue comments' do project = create(:project, :public) note = create(:note_on_issue, project: project) @@ -305,7 +315,7 @@ RSpec.describe SearchController do expect(response).to have_gitlab_http_status(:ok) - expect(response.headers['Cache-Control']).to eq('no-store') + expect(response.headers['Cache-Control']).to eq('private, no-store') end end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 2aa9b86b20e..8442c214cd3 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -599,6 +599,46 @@ RSpec.describe UploadsController do end end + context "when viewing a topic avatar" do + let!(:topic) { create(:topic, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } + + context "when signed in" do + before do + sign_in(user) + end + + it "responds with status 200" do + get :show, params: { model: "projects/topic", mounted_as: "avatar", id: topic.id, filename: "dk.png" } + + expect(response).to have_gitlab_http_status(:ok) + end + + it_behaves_like 'content publicly cached' do + subject do + get :show, params: { model: "projects/topic", mounted_as: "avatar", id: topic.id, filename: "dk.png" } + + response + end + end + end + + context "when not signed in" do + it "responds with status 200" do + get :show, params: { model: "projects/topic", mounted_as: "avatar", id: topic.id, filename: "dk.png" } + + expect(response).to have_gitlab_http_status(:ok) + end + + it_behaves_like 'content publicly cached' do + subject do + get :show, params: { model: "projects/topic", mounted_as: "avatar", id: topic.id, filename: "dk.png" } + + response + end + end + end + end + context 'Appearance' do context 'when viewing a custom header logo' do let!(:appearance) { create :appearance, header_logo: fixture_file_upload('spec/fixtures/dk.png', 'image/png') } |