# frozen_string_literal: true require 'spec_helper' RSpec.describe Gitlab::Database::LoadBalancing::RackMiddleware, :redis do let(:app) { double(:app) } let(:middleware) { described_class.new(app) } let(:warden_user) { double(:warden, user: double(:user, id: 42)) } let(:single_sticking_object) { Set.new([[:user, 42]]) } let(:multiple_sticking_objects) do Set.new([ [:user, 42], [:runner, '123456789'], [:runner, '1234'] ]) end after do Gitlab::Database::LoadBalancing::Session.clear_session end describe '.stick_or_unstick' do before do allow(Gitlab::Database::LoadBalancing).to receive(:enable?) .and_return(true) end it 'sticks or unsticks a single object and updates the Rack environment' do expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:unstick_or_continue_sticking) .with(:user, 42) env = {} described_class.stick_or_unstick(env, :user, 42) expect(env[described_class::STICK_OBJECT].to_a).to eq([[:user, 42]]) end it 'sticks or unsticks multiple objects and updates the Rack environment' do expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:unstick_or_continue_sticking) .with(:user, 42) .ordered expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:unstick_or_continue_sticking) .with(:runner, '123456789') .ordered env = {} described_class.stick_or_unstick(env, :user, 42) described_class.stick_or_unstick(env, :runner, '123456789') expect(env[described_class::STICK_OBJECT].to_a).to eq([ [:user, 42], [:runner, '123456789'] ]) end end describe '#call' do it 'handles a request' do env = {} expect(middleware).to receive(:clear).twice expect(middleware).to receive(:unstick_or_continue_sticking).with(env) expect(middleware).to receive(:stick_if_necessary).with(env) expect(app).to receive(:call).with(env).and_return(10) expect(middleware.call(env)).to eq(10) end end describe '#unstick_or_continue_sticking' do it 'does not stick if no namespace and identifier could be found' do expect(Gitlab::Database::LoadBalancing::Sticking) .not_to receive(:unstick_or_continue_sticking) middleware.unstick_or_continue_sticking({}) end it 'sticks to the primary if a warden user is found' do env = { 'warden' => warden_user } expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:unstick_or_continue_sticking) .with(:user, 42) middleware.unstick_or_continue_sticking(env) end it 'sticks to the primary if a sticking namespace and identifier is found' do env = { described_class::STICK_OBJECT => single_sticking_object } expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:unstick_or_continue_sticking) .with(:user, 42) middleware.unstick_or_continue_sticking(env) end it 'sticks to the primary if multiple sticking namespaces and identifiers were found' do env = { described_class::STICK_OBJECT => multiple_sticking_objects } expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:unstick_or_continue_sticking) .with(:user, 42) .ordered expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:unstick_or_continue_sticking) .with(:runner, '123456789') .ordered expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:unstick_or_continue_sticking) .with(:runner, '1234') .ordered middleware.unstick_or_continue_sticking(env) end end describe '#stick_if_necessary' do it 'does not stick to the primary if not necessary' do expect(Gitlab::Database::LoadBalancing::Sticking) .not_to receive(:stick_if_necessary) middleware.stick_if_necessary({}) end it 'sticks to the primary if a warden user is found' do env = { 'warden' => warden_user } expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:stick_if_necessary) .with(:user, 42) middleware.stick_if_necessary(env) end it 'sticks to the primary if a a single sticking object is found' do env = { described_class::STICK_OBJECT => single_sticking_object } expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:stick_if_necessary) .with(:user, 42) middleware.stick_if_necessary(env) end it 'sticks to the primary if multiple sticking namespaces and identifiers were found' do env = { described_class::STICK_OBJECT => multiple_sticking_objects } expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:stick_if_necessary) .with(:user, 42) .ordered expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:stick_if_necessary) .with(:runner, '123456789') .ordered expect(Gitlab::Database::LoadBalancing::Sticking) .to receive(:stick_if_necessary) .with(:runner, '1234') .ordered middleware.stick_if_necessary(env) end end describe '#clear' do it 'clears the currently used host and session' do lb = double(:lb) session = double(:session) allow(middleware).to receive(:load_balancer).and_return(lb) expect(lb).to receive(:release_host) stub_const('Gitlab::Database::LoadBalancing::RackMiddleware::Session', session) expect(session).to receive(:clear_session) middleware.clear end end describe '.load_balancer' do it 'returns a the load balancer' do proxy = double(:proxy) expect(Gitlab::Database::LoadBalancing).to receive(:proxy) .and_return(proxy) expect(proxy).to receive(:load_balancer) middleware.load_balancer end end describe '#sticking_namespaces_and_ids' do context 'using a Warden request' do it 'returns the warden user if present' do env = { 'warden' => warden_user } expect(middleware.sticking_namespaces_and_ids(env)).to eq([[:user, 42]]) end it 'returns an empty Array if no user was present' do warden = double(:warden, user: nil) env = { 'warden' => warden } expect(middleware.sticking_namespaces_and_ids(env)).to eq([]) end end context 'using a request with a manually set sticking object' do it 'returns the sticking object' do env = { described_class::STICK_OBJECT => multiple_sticking_objects } expect(middleware.sticking_namespaces_and_ids(env)).to eq([ [:user, 42], [:runner, '123456789'], [:runner, '1234'] ]) end end context 'using a regular request' do it 'returns an empty Array' do expect(middleware.sticking_namespaces_and_ids({})).to eq([]) end end end end