turniere-backend/spec/config/scaling_env_spec.rb

149 lines
3.9 KiB
Ruby

# frozen_string_literal: true
require 'erb'
require 'yaml'
RSpec.describe 'runtime scaling configuration' do
around do |example|
with_env(
'DATABASE_URL' => nil,
'POSTGRES_URL' => nil,
'PORT' => nil,
'RAILS_DB_POOL' => nil,
'RAILS_ENV' => nil,
'RAILS_MAX_THREADS' => nil,
'RAILS_MIN_THREADS' => nil,
'SIDEKIQ_CONCURRENCY' => nil,
'WEB_CONCURRENCY' => nil
) { example.run }
end
it 'configures Puma workers and threads from environment variables' do
with_env(
'PORT' => '4001',
'RAILS_ENV' => 'production',
'RAILS_MAX_THREADS' => '7',
'RAILS_MIN_THREADS' => '2',
'WEB_CONCURRENCY' => '4'
) do
config = evaluate_puma_config
expect(config.fetch(:threads)).to eq(%w[2 7])
expect(config.fetch(:workers)).to eq('4')
expect(config.fetch(:preload_app)).to be(true)
expect(config.fetch(:port)).to eq('4001')
expect(config.fetch(:environment)).to eq('production')
expect(config.fetch(:plugin)).to eq(:tmp_restart)
end
end
it 'uses safe Puma defaults' do
config = evaluate_puma_config
expect(config.fetch(:threads)).to eq([5, 5])
expect(config.fetch(:workers)).to eq(2)
expect(config.fetch(:port)).to eq(3000)
expect(config.fetch(:environment)).to eq('production')
end
it 'configures the production database pool from RAILS_DB_POOL' do
with_env(
'DATABASE_URL' => 'postgres://turniere:secret@postgres/turniere',
'RAILS_DB_POOL' => '9',
'RAILS_MAX_THREADS' => '7'
) do
config = database_config.fetch('production')
expect(config.fetch('adapter')).to eq('postgresql')
expect(config.fetch('encoding')).to eq('unicode')
expect(config.fetch('url')).to eq('postgres://turniere:secret@postgres/turniere')
expect(config.fetch('pool')).to eq(9)
end
end
it 'falls back to RAILS_MAX_THREADS for the database pool and POSTGRES_URL for legacy deployments' do
with_env(
'POSTGRES_URL' => 'postgres://turniere:secret@legacy-postgres/turniere',
'RAILS_MAX_THREADS' => '6'
) do
config = database_config.fetch('production')
expect(config.fetch('url')).to eq('postgres://turniere:secret@legacy-postgres/turniere')
expect(config.fetch('pool')).to eq(6)
end
end
it 'configures Sidekiq concurrency from SIDEKIQ_CONCURRENCY' do
with_env('SIDEKIQ_CONCURRENCY' => '10') do
expect(sidekiq_config).to include(':concurrency: 10')
end
expect(sidekiq_config).to include(':concurrency: 5')
end
def evaluate_puma_config
probe = puma_config_probe_class.new
probe.instance_eval(File.read(File.join(root, 'config/puma.rb')), File.join(root, 'config/puma.rb'))
probe.config
end
def database_config
YAML.safe_load(ERB.new(File.read(File.join(root, 'config/database.yml'))).result, aliases: true)
end
def sidekiq_config
ERB.new(File.read(File.join(root, 'config/sidekiq.yml'))).result
end
def with_env(values)
previous = values.to_h { |key, _value| [key, ENV.fetch(key, nil)] }
values.each do |key, value|
value.nil? ? ENV.delete(key) : ENV[key] = value
end
yield
ensure
previous.each do |key, value|
value.nil? ? ENV.delete(key) : ENV[key] = value
end
end
def root
File.expand_path('../..', __dir__)
end
def puma_config_probe_class
Class.new do
attr_reader :config
def initialize
@config = {}
end
def threads(min_threads, max_threads)
@config[:threads] = [min_threads, max_threads]
end
def workers(count)
@config[:workers] = count
end
def preload_app!
@config[:preload_app] = true
end
def port(value)
@config[:port] = value
end
def environment(value)
@config[:environment] = value
end
def plugin(name)
@config[:plugin] = name
end
end
end
end