# frozen_string_literal: true require_relative 'helper' require 'sidekiq/cli' describe Sidekiq::CLI do describe '#parse' do before do Sidekiq.options = Sidekiq::DEFAULTS.dup @logger = Sidekiq.logger @logdev = StringIO.new Sidekiq.logger = Logger.new(@logdev) @cli = Sidekiq::CLI.new end after do Sidekiq.logger = @logger end describe 'options' do describe 'require' do it 'accepts with -r' do @cli.parse(%w[sidekiq -r ./test/fake_env.rb]) assert_equal './test/fake_env.rb', Sidekiq.options[:require] end end describe 'concurrency' do it 'accepts with -c' do @cli.parse(%w[sidekiq -c 60 -r ./test/fake_env.rb]) assert_equal 60, Sidekiq.options[:concurrency] end describe 'when concurrency is empty and RAILS_MAX_THREADS env var is set' do before do ENV['RAILS_MAX_THREADS'] = '9' end after do ENV.delete('RAILS_MAX_THREADS') end it 'sets concurrency from RAILS_MAX_THREADS env var' do @cli.parse(%w[sidekiq -r ./test/fake_env.rb]) assert_equal 9, Sidekiq.options[:concurrency] end it 'option overrides RAILS_MAX_THREADS env var' do @cli.parse(%w[sidekiq -c 60 -r ./test/fake_env.rb]) assert_equal 60, Sidekiq.options[:concurrency] end end end describe 'queues' do it 'accepts with -q' do @cli.parse(%w[sidekiq -q foo -r ./test/fake_env.rb]) assert_equal ['foo'], Sidekiq.options[:queues] end describe 'when weights are not present' do it 'accepts queues without weights' do @cli.parse(%w[sidekiq -q foo -q bar -r ./test/fake_env.rb]) assert_equal ['foo', 'bar'], Sidekiq.options[:queues] end it 'sets strictly ordered queues' do @cli.parse(%w[sidekiq -q foo -q bar -r ./test/fake_env.rb]) assert_equal true, !!Sidekiq.options[:strict] end end describe 'when weights are present' do it 'accepts queues with weights' do @cli.parse(%w[sidekiq -q foo,3 -q bar -r ./test/fake_env.rb]) assert_equal ['foo', 'foo', 'foo', 'bar'], Sidekiq.options[:queues] end it 'does not set strictly ordered queues' do @cli.parse(%w[sidekiq -q foo,3 -q bar -r ./test/fake_env.rb]) assert_equal false, !!Sidekiq.options[:strict] end end it 'accepts queues with multi-word names' do @cli.parse(%w[sidekiq -q queue_one -q queue-two -r ./test/fake_env.rb]) assert_equal ['queue_one', 'queue-two'], Sidekiq.options[:queues] end it 'accepts queues with dots in the name' do @cli.parse(%w[sidekiq -q foo.bar -r ./test/fake_env.rb]) assert_equal ['foo.bar'], Sidekiq.options[:queues] end describe 'when duplicate queue names' do it 'raises an argument error' do assert_raises(ArgumentError) { @cli.parse(%w[sidekiq -q foo -q foo -r ./test/fake_env.rb]) } assert_raises(ArgumentError) { @cli.parse(%w[sidekiq -q foo,3 -q foo,1 -r ./test/fake_env.rb]) } end end describe 'when queues are empty' do it "sets 'default' queue" do @cli.parse(%w[sidekiq -r ./test/fake_env.rb]) assert_equal ['default'], Sidekiq.options[:queues] end end end describe 'process index' do it 'accepts with -i' do @cli.parse(%w[sidekiq -i 7 -r ./test/fake_env.rb]) assert_equal 7, Sidekiq.options[:index] end it 'accepts stringy value' do @cli.parse(%w[sidekiq -i worker.7 -r ./test/fake_env.rb]) assert_equal 7, Sidekiq.options[:index] end end describe 'timeout' do it 'accepts with -t' do @cli.parse(%w[sidekiq -t 30 -r ./test/fake_env.rb]) assert_equal 30, Sidekiq.options[:timeout] end end describe 'logfile' do it 'accepts wiht -L' do @cli.parse(%w[sidekiq -L /tmp/sidekiq.log -r ./test/fake_env.rb]) assert_equal '/tmp/sidekiq.log', Sidekiq.options[:logfile] end end describe 'verbose' do it 'accepts with -v' do @cli.parse(%w[sidekiq -v -r ./test/fake_env.rb]) assert_equal Logger::DEBUG, Sidekiq.logger.level end end describe 'pidfile' do it 'accepts with -P' do @cli.parse(%w[sidekiq -P /tmp/sidekiq.pid -r ./test/fake_env.rb]) assert_equal '/tmp/sidekiq.pid', Sidekiq.options[:pidfile] end end describe 'config file' do it 'accepts with -C' do @cli.parse(%w[sidekiq -C ./test/config.yml]) assert_equal './test/config.yml', Sidekiq.options[:config_file] refute Sidekiq.options[:verbose] assert_equal './test/fake_env.rb', Sidekiq.options[:require] assert_nil Sidekiq.options[:environment] assert_equal 50, Sidekiq.options[:concurrency] assert_equal '/tmp/sidekiq-config-test.pid', Sidekiq.options[:pidfile] assert_equal '/tmp/sidekiq.log', Sidekiq.options[:logfile] assert_equal 2, Sidekiq.options[:queues].count { |q| q == 'very_often' } assert_equal 1, Sidekiq.options[:queues].count { |q| q == 'seldom' } end it 'accepts stringy keys' do @cli.parse(%w[sidekiq -C ./test/config_string.yml]) assert_equal './test/config_string.yml', Sidekiq.options[:config_file] refute Sidekiq.options[:verbose] assert_equal './test/fake_env.rb', Sidekiq.options[:require] assert_nil Sidekiq.options[:environment] assert_equal 50, Sidekiq.options[:concurrency] assert_equal '/tmp/sidekiq-config-test.pid', Sidekiq.options[:pidfile] assert_equal '/tmp/sidekiq.log', Sidekiq.options[:logfile] assert_equal 2, Sidekiq.options[:queues].count { |q| q == 'very_often' } assert_equal 1, Sidekiq.options[:queues].count { |q| q == 'seldom' } end it 'accepts environment specific config' do @cli.parse(%w[sidekiq -e staging -C ./test/config_environment.yml]) assert_equal './test/config_environment.yml', Sidekiq.options[:config_file] refute Sidekiq.options[:verbose] assert_equal './test/fake_env.rb', Sidekiq.options[:require] assert_equal 'staging', Sidekiq.options[:environment] assert_equal 50, Sidekiq.options[:concurrency] assert_equal '/tmp/sidekiq-config-test.pid', Sidekiq.options[:pidfile] assert_equal '/tmp/sidekiq.log', Sidekiq.options[:logfile] assert_equal 2, Sidekiq.options[:queues].count { |q| q == 'very_often' } assert_equal 1, Sidekiq.options[:queues].count { |q| q == 'seldom' } end describe 'when config file is empty' do it 'sets default options' do @cli.parse(%w[sidekiq -C ./test/config_empty.yml -r ./test/fake_env.rb]) assert_equal './test/config_empty.yml', Sidekiq.options[:config_file] refute Sidekiq.options[:verbose] assert_equal './test/fake_env.rb', Sidekiq.options[:require] assert_nil Sidekiq.options[:environment] assert_equal 10, Sidekiq.options[:concurrency] assert_nil Sidekiq.options[:pidfile] assert_nil Sidekiq.options[:logfile] assert_equal ['default'], Sidekiq.options[:queues] end end describe 'when config file and flags' do it 'merges options' do @cli.parse(%w[sidekiq -C ./test/config.yml -e snoop -c 100 -r ./test/fake_env.rb -P /tmp/sidekiq.pid -q often,7 -q seldom,3]) assert_equal './test/config.yml', Sidekiq.options[:config_file] refute Sidekiq.options[:verbose] assert_equal './test/fake_env.rb', Sidekiq.options[:require] assert_equal 'snoop', Sidekiq.options[:environment] assert_equal 100, Sidekiq.options[:concurrency] assert_equal '/tmp/sidekiq.pid', Sidekiq.options[:pidfile] assert_equal '/tmp/sidekiq.log', Sidekiq.options[:logfile] assert_equal 7, Sidekiq.options[:queues].count { |q| q == 'often' } assert_equal 3, Sidekiq.options[:queues].count { |q| q == 'seldom' } end end describe 'default config file' do describe 'when required path is a directory' do it 'tries config/sidekiq.yml from required diretory' do @cli.parse(%w[sidekiq -r ./test/dummy]) assert_equal './test/dummy/config/sidekiq.yml', Sidekiq.options[:config_file] assert_equal 25, Sidekiq.options[:concurrency] end end describe 'when required path is a file' do it 'tries config/sidekiq.yml from current diretory' do Sidekiq.options[:require] = './test/dummy' # stub current dir – ./ @cli.parse(%w[sidekiq -r ./test/fake_env.rb]) assert_equal './test/dummy/config/sidekiq.yml', Sidekiq.options[:config_file] assert_equal 25, Sidekiq.options[:concurrency] end end describe 'without any required path' do it 'tries config/sidekiq.yml from current diretory' do Sidekiq.options[:require] = './test/dummy' # stub current dir – ./ @cli.parse(%w[sidekiq]) assert_equal './test/dummy/config/sidekiq.yml', Sidekiq.options[:config_file] assert_equal 25, Sidekiq.options[:concurrency] end end end end end describe 'validation' do describe 'when required application path does not exist' do it 'exits with status 1' do exit = assert_raises(SystemExit) { @cli.parse(%w[sidekiq -r /non/existent/path]) } assert_equal 1, exit.status end end describe 'when required path is a directory without config/application.rb' do it 'exits with status 1' do exit = assert_raises(SystemExit) { @cli.parse(%w[sidekiq -r ./test/fixtures]) } assert_equal 1, exit.status end describe 'when config file path does not exist' do it 'raises argument error' do assert_raises(ArgumentError) do @cli.parse(%w[sidekiq -r ./test/fake_env.rb -C /non/existent/path]) end end end end end end describe '#run' do before do Sidekiq.options = Sidekiq::DEFAULTS.dup Sidekiq.options[:require] = './test/fake_env.rb' @logger = Sidekiq.logger @logdev = StringIO.new Sidekiq.logger = Logger.new(@logdev) @cli = Sidekiq::CLI.new end after do Sidekiq.logger = @logger end describe 'pidfile' do it 'writes process pid to file' do Sidekiq.options[:pidfile] = '/tmp/sidekiq.pid' @cli.stub(:launch, nil) do @cli.run end assert_equal Process.pid, File.read('/tmp/sidekiq.pid').chop.to_i end end describe 'require workers' do describe 'when path is a rails directory' do before do Sidekiq.options[:require] = './test/dummy' @cli.environment = 'test' end it 'requires sidekiq railtie and rails application with environment' do @cli.stub(:launch, nil) do @cli.run end assert defined?(Sidekiq::Rails) assert defined?(Dummy::Application) end it 'tags with the app directory name' do @cli.stub(:launch, nil) do @cli.run end assert_equal 'dummy', Sidekiq.options[:tag] end end describe 'when path is file' do it 'requires application' do @cli.stub(:launch, nil) do @cli.run end assert $LOADED_FEATURES.any? { |x| x =~ /test\/fake_env/ } end end end describe 'when development environment and stdout tty' do it 'prints banner' do @cli.stub(:environment, 'development') do assert_output(/#{Regexp.escape(Sidekiq::CLI.banner)}/) do $stdout.stub(:tty?, true) do @cli.stub(:launch, nil) do @cli.run end end end end end end end describe 'signal handling' do before do @cli = Sidekiq::CLI.new Sidekiq.options = Sidekiq::DEFAULTS.dup @logger = Sidekiq.logger @logdev = StringIO.new Sidekiq.logger = Logger.new(@logdev) end after do Sidekiq.logger = @logger end %w(INT TERM).each do |sig| describe sig do it 'raises interrupt error' do assert_raises Interrupt do @cli.handle_signal(sig) end end end end %w(TSTP USR1).each do |sig| describe sig do it 'quites with a corresponding event' do quiet = false Sidekiq.on(:quiet) do quiet = true end @cli.parse(%w[sidekiq -r ./test/fake_env.rb]) @cli.launcher = Sidekiq::Launcher.new(Sidekiq.options) @cli.handle_signal(sig) assert_match(/Got #{sig} signal/, @logdev.string) assert_equal true, quiet end end end describe 'TTIN' do it 'prints backtraces for all threads in the process to the logfile' do @cli.parse(%w[sidekiq -r ./test/fake_env.rb]) @cli.handle_signal('TTIN') assert_match(/Got TTIN signal/, @logdev.string) assert_match(/\bbacktrace\b/, @logdev.string) end end describe 'UNKNOWN' do it 'logs about' do @cli.parse(%w[sidekiq -r ./test/fake_env.rb]) @cli.handle_signal('UNKNOWN') assert_match(/Got UNKNOWN signal/, @logdev.string) assert_match(/No signal handler for UNKNOWN/, @logdev.string) end end end end