216 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			216 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| require 'spec_helper'
 | |
| require 'sidekiq/testing'
 | |
| 
 | |
| RSpec.describe Gitlab::SidekiqMiddleware do
 | |
|   before do
 | |
|     stub_const('TestWorker', Class.new)
 | |
| 
 | |
|     TestWorker.class_eval do
 | |
|       include Sidekiq::Worker
 | |
|       include ApplicationWorker
 | |
| 
 | |
|       def perform(_arg)
 | |
|         Gitlab::SafeRequestStore['gitaly_call_actual'] = 1
 | |
|         Gitlab::SafeRequestStore[:gitaly_query_time] = 5
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   around do |example|
 | |
|     Sidekiq::Testing.inline! { example.run }
 | |
|   end
 | |
| 
 | |
|   let(:worker_class) { TestWorker }
 | |
|   let(:job_args) { [0.01] }
 | |
| 
 | |
|   # The test sets up a new server middleware stack, ensuring that the
 | |
|   # appropriate middlewares, as passed into server_configurator,
 | |
|   # are invoked.
 | |
|   # Additionally the test ensure that each middleware is
 | |
|   # 1) not failing
 | |
|   # 2) yielding exactly once
 | |
|   describe '.server_configurator' do
 | |
|     around do |example|
 | |
|       with_sidekiq_server_middleware do |chain|
 | |
|         described_class.server_configurator(
 | |
|           metrics: metrics,
 | |
|           arguments_logger: arguments_logger,
 | |
|           memory_killer: memory_killer
 | |
|         ).call(chain)
 | |
| 
 | |
|         example.run
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     let(:middleware_expected_args) { [a_kind_of(worker_class), hash_including({ 'args' => job_args }), anything] }
 | |
|     let(:all_sidekiq_middlewares) do
 | |
|       [
 | |
|        Gitlab::SidekiqMiddleware::Monitor,
 | |
|        Gitlab::SidekiqMiddleware::BatchLoader,
 | |
|        Labkit::Middleware::Sidekiq::Server,
 | |
|        Gitlab::SidekiqMiddleware::InstrumentationLogger,
 | |
|        Gitlab::SidekiqVersioning::Middleware,
 | |
|        Gitlab::SidekiqStatus::ServerMiddleware,
 | |
|        Gitlab::SidekiqMiddleware::ServerMetrics,
 | |
|        Gitlab::SidekiqMiddleware::ArgumentsLogger,
 | |
|        Gitlab::SidekiqMiddleware::MemoryKiller,
 | |
|        Gitlab::SidekiqMiddleware::RequestStoreMiddleware,
 | |
|        Gitlab::SidekiqMiddleware::ExtraDoneLogMetadata,
 | |
|        Gitlab::SidekiqMiddleware::WorkerContext::Server,
 | |
|        Gitlab::SidekiqMiddleware::AdminMode::Server,
 | |
|        Gitlab::SidekiqMiddleware::DuplicateJobs::Server
 | |
|       ]
 | |
|     end
 | |
| 
 | |
|     let(:enabled_sidekiq_middlewares) { all_sidekiq_middlewares - disabled_sidekiq_middlewares }
 | |
| 
 | |
|     shared_examples "a server middleware chain" do
 | |
|       it "passes through the right server middlewares" do
 | |
|         enabled_sidekiq_middlewares.each do |middleware|
 | |
|           expect_any_instance_of(middleware).to receive(:call).with(*middleware_expected_args).once.and_call_original
 | |
|         end
 | |
| 
 | |
|         disabled_sidekiq_middlewares.each do |middleware|
 | |
|           expect_any_instance_of(middleware).not_to receive(:call)
 | |
|         end
 | |
| 
 | |
|         worker_class.perform_async(*job_args)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     shared_examples "a server middleware chain for mailer" do
 | |
|       let(:worker_class) { ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper }
 | |
|       let(:job_args) do
 | |
|         [
 | |
|           {
 | |
|             "job_class" => "ActionMailer::MailDeliveryJob",
 | |
|             "job_id" => "a180b47c-3fd6-41b8-81e9-34da61c3400e",
 | |
|             "provider_job_id" => nil,
 | |
|             "queue_name" => "mailers",
 | |
|             "priority" => nil,
 | |
|             "arguments" => [
 | |
|               "Notify",
 | |
|               "test_email",
 | |
|               "deliver_now",
 | |
|               {
 | |
|                 "args" => [
 | |
|                   "test@example.com",
 | |
|                   "subject",
 | |
|                   "body"
 | |
|                 ],
 | |
|                 ActiveJob::Arguments.const_get('RUBY2_KEYWORDS_KEY', false) => ["args"]
 | |
|               }
 | |
|             ],
 | |
|             "executions" => 0,
 | |
|             "exception_executions" => {},
 | |
|             "locale" => "en",
 | |
|             "timezone" => "UTC",
 | |
|             "enqueued_at" => "2020-07-27T07:43:31Z"
 | |
|           }
 | |
|         ]
 | |
|       end
 | |
| 
 | |
|       it_behaves_like "a server middleware chain"
 | |
|     end
 | |
| 
 | |
|     context "all optional middlewares off" do
 | |
|       let(:metrics) { false }
 | |
|       let(:arguments_logger) { false }
 | |
|       let(:memory_killer) { false }
 | |
|       let(:disabled_sidekiq_middlewares) do
 | |
|         [
 | |
|           Gitlab::SidekiqMiddleware::ServerMetrics,
 | |
|           Gitlab::SidekiqMiddleware::ArgumentsLogger,
 | |
|           Gitlab::SidekiqMiddleware::MemoryKiller
 | |
|         ]
 | |
|       end
 | |
| 
 | |
|       it_behaves_like "a server middleware chain"
 | |
|       it_behaves_like "a server middleware chain for mailer"
 | |
|     end
 | |
| 
 | |
|     context "all optional middlewares on" do
 | |
|       let(:metrics) { true }
 | |
|       let(:arguments_logger) { true }
 | |
|       let(:memory_killer) { true }
 | |
|       let(:disabled_sidekiq_middlewares) { [] }
 | |
| 
 | |
|       it_behaves_like "a server middleware chain"
 | |
|       it_behaves_like "a server middleware chain for mailer"
 | |
| 
 | |
|       context "server metrics" do
 | |
|         let(:gitaly_histogram) { double(:gitaly_histogram) }
 | |
| 
 | |
|         before do
 | |
|           allow(Gitlab::Metrics).to receive(:histogram).and_call_original
 | |
| 
 | |
|           allow(Gitlab::Metrics).to receive(:histogram)
 | |
|                                       .with(:sidekiq_jobs_gitaly_seconds, anything, anything, anything)
 | |
|                                       .and_return(gitaly_histogram)
 | |
|         end
 | |
| 
 | |
|         it "records correct Gitaly duration" do
 | |
|           expect(gitaly_histogram).to receive(:observe).with(anything, 5.0)
 | |
| 
 | |
|           worker_class.perform_async(*job_args)
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   # The test sets up a new client middleware stack. The test ensures
 | |
|   # that each middleware is:
 | |
|   # 1) not failing
 | |
|   # 2) yielding exactly once
 | |
|   describe '.client_configurator' do
 | |
|     let(:chain) { Sidekiq::Middleware::Chain.new }
 | |
|     let(:job) { { 'args' => job_args } }
 | |
|     let(:queue) { 'default' }
 | |
|     let(:redis_pool) { Sidekiq.redis_pool }
 | |
|     let(:middleware_expected_args) { [worker_class_arg, job, queue, redis_pool] }
 | |
|     let(:expected_middlewares) do
 | |
|       [
 | |
|          ::Gitlab::SidekiqMiddleware::WorkerContext::Client,
 | |
|          ::Labkit::Middleware::Sidekiq::Client,
 | |
|          ::Gitlab::SidekiqMiddleware::DuplicateJobs::Client,
 | |
|          ::Gitlab::SidekiqStatus::ClientMiddleware,
 | |
|          ::Gitlab::SidekiqMiddleware::AdminMode::Client,
 | |
|          ::Gitlab::SidekiqMiddleware::ClientMetrics
 | |
|       ]
 | |
|     end
 | |
| 
 | |
|     before do
 | |
|       described_class.client_configurator.call(chain)
 | |
|     end
 | |
| 
 | |
|     shared_examples "a client middleware chain" do
 | |
|       # Its possible that a middleware could accidentally omit a yield call
 | |
|       # this will prevent the full middleware chain from being executed.
 | |
|       # This test ensures that this does not happen
 | |
|       it "invokes the chain" do
 | |
|         expected_middlewares do |middleware|
 | |
|           expect_any_instance_of(middleware).to receive(:call).with(*middleware_expected_args).once.ordered.and_call_original
 | |
|         end
 | |
| 
 | |
|         expect { |b| chain.invoke(worker_class_arg, job, queue, redis_pool, &b) }.to yield_control.once
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     # Sidekiq documentation states that the worker class could be a string
 | |
|     # or a class reference. We should test for both
 | |
|     context "handles string worker_class values" do
 | |
|       let(:worker_class_arg) { worker_class.to_s }
 | |
| 
 | |
|       it_behaves_like "a client middleware chain"
 | |
|     end
 | |
| 
 | |
|     context "handles string worker_class values" do
 | |
|       let(:worker_class_arg) { worker_class }
 | |
| 
 | |
|       it_behaves_like "a client middleware chain"
 | |
|     end
 | |
|   end
 | |
| end
 |