# frozen_string_literal: true require 'spec_helper' RSpec.describe ApplicationController, type: :request, feature_category: :shared do let_it_be_with_reload(:user) { create(:user) } it_behaves_like 'Base action controller' do before do sign_in(user) end subject(:request) { get root_path } end it 'does not send Link header', :use_clean_rails_redis_caching do sign_in(user) get root_path expect(response.headers['Link']).to be_nil end describe 'session expiration' do context 'when user is authenticated' do it 'sets the redis_expiry option from session_expire_delay setting' do sign_in(user) get root_path expect(request.env['rack.session.options'][:redis_expiry]).to eq( Settings.gitlab['session_expire_delay'] * 60 ) end end context 'when user is unauthenticated' do it 'sets the redis_expiry option from unauthenticated_session_expire_delay' do get root_path expect(request.env['rack.session.options'][:redis_expiry]).to eq( Settings.gitlab['unauthenticated_session_expire_delay'] ) end end end describe 'unknown route' do # This spec targets CI environment with precompiled assets to trigger # Sprockets' `File.binread` and find encoding issues. # # See https://gitlab.com/gitlab-com/gl-infra/production/-/issues/17627#note_1782396646 it 'returns 404 even when locale contains UTF-8 chars' do user.update!(preferred_language: 'ZH-cn') sign_in(user) get "/some/undefined/path" expect(response).to have_gitlab_http_status(:not_found) expect(response.body.encoding.name).to eq('UTF-8') end end describe 'User-Agent header' do before do sign_in(user) get root_path, headers: { 'User-Agent': user_agent } end context 'when missing' do let(:user_agent) { nil } it { expect(response).to have_gitlab_http_status(:ok) } end context 'when correct' do let(:user_agent) { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)' } it { expect(response).to have_gitlab_http_status(:ok) } end context 'when too long' do let(:user_agent) { 'a' * 3000 } it { expect(response).to have_gitlab_http_status(:forbidden) } end end describe 'HTTP Router headers' do before do sign_in(user) end let(:headers) do { 'X-Gitlab-Http-Router-Rule-Action' => 'classify', 'X-Gitlab-Http-Router-Rule-Type' => 'FIRST_CELL' } end subject(:perform_request) do get root_path, headers: headers end it 'includes the HTTP ROUTER headers in ApplicationContext' do expect_next_instance_of(RootController) do |controller| expect(controller).to receive(:index).and_wrap_original do |m, *args| m.call(*args) expect(Gitlab::ApplicationContext.current).to include( 'meta.http_router_rule_action' => 'classify', 'meta.http_router_rule_type' => 'FIRST_CELL' ) end end perform_request end context 'for counters' do let(:http_router_rule_counter) { Gitlab::Metrics.counter(:gitlab_http_router_rule_total, 'description') } context 'when the headers are present' do context 'for classify action' do it 'increments the counter' do expect { perform_request }.to change { http_router_rule_counter.get(rule_action: 'classify', rule_type: 'FIRST_CELL') }.by(1) end end context 'for classify action by SESSION_PREFIX' do let(:headers) do { 'X-Gitlab-Http-Router-Rule-Action' => 'classify', 'X-Gitlab-Http-Router-Rule-Type' => 'SESSION_PREFIX' } end it 'increments the counter with labels' do expect { perform_request }.to change { http_router_rule_counter.get(rule_action: 'classify', rule_type: 'SESSION_PREFIX') }.by(1) end end context 'for classify action by CELL_ID' do let(:headers) do { 'X-Gitlab-Http-Router-Rule-Action' => 'classify', 'X-Gitlab-Http-Router-Rule-Type' => 'CELL_ID' } end it 'increments the counter with labels' do expect { perform_request }.to change { http_router_rule_counter.get(rule_action: 'classify', rule_type: 'CELL_ID') }.by(1) end end context 'for proxy action' do let(:headers) do { 'X-Gitlab-Http-Router-Rule-Action' => 'proxy' } end it 'increments the counter' do expect { perform_request }.to change { http_router_rule_counter.get(rule_action: 'proxy', rule_type: nil) }.by(1) end end context 'for invalid action and type' do let(:headers) do { 'X-Gitlab-Http-Router-Rule-Action' => 'invalid', 'X-Gitlab-Http-Router-Rule-Type' => 'invalid' } end it 'does not increment the counter' do expect { perform_request }.to change { http_router_rule_counter.get(rule_action: 'invalid', rule_type: 'invalid') }.by(0) end end context 'when action is not present and type is present' do let(:headers) do { 'X-Gitlab-Http-Router-Rule-Type' => 'FIRST_CELL' } end it 'does not increment the counter' do expect { perform_request }.to change { http_router_rule_counter.get(rule_action: nil, rule_type: 'FIRST_CELL') }.by(0) end end end context 'when the headers are absent' do let(:headers) { {} } it 'does not increment the counter' do expect { perform_request }.to change { http_router_rule_counter.get(rule_action: nil, rule_type: nil) }.by(0) end end end end describe 'static context middleware', feature_category: :error_budgets do # to check the middleware tests, go to spec/lib/gitlab/middleware/static_context_middleware_spec.rb let(:expected_context) do { 'meta.feature_category' => 'groups_and_projects', 'meta.caller_id' => 'Dashboard::GroupsController#index' } end context 'when action is successfully called' do it 'pushes static context to current context' do context = {} allow_next_instance_of(Dashboard::GroupsController) do |_controller| context.merge!(Gitlab::ApplicationContext.current.to_h) end get '/dashboard/groups' # randomly picked route expect(response).to have_gitlab_http_status(:found) expect(context).to include expected_context end end context 'when an exception is thrown before action' do it 'pushes static context to current context before controller callbacks' do context = {} unexpected_error = 'boom 💣💥' allow_next_instance_of(Dashboard::GroupsController) do |controller| # picking up a random before_action method to raise an "unexpected" exception allow(controller).to receive(:authenticate_user!).and_raise(unexpected_error) context.merge!(Gitlab::ApplicationContext.current.to_h) end expect { get '/dashboard/groups' }.to raise_error(unexpected_error) expect(context).to include expected_context end end context 'when controller overrides feature_category with nil' do it 'ignores nil feature category override' do context = {} allow_next_instance_of(Projects::NotesController) do |controller| # mimicking a bug overriding feature_category with nil allow(controller).to receive(:feature_category).and_return(nil) context.merge!(Gitlab::ApplicationContext.current.to_h) end project = create(:project, :public) project_snippet = create(:project_snippet, project: project) create(:note_on_project_snippet, project: project, noteable: project_snippet) # picking a route targeting a controller that overrides feature_category get project_noteable_notes_path( project, target_type: 'project_snippet', target_id: project_snippet.id, html: true ) expect(context).to include({ 'meta.feature_category' => 'team_planning' }) end end end end