DEV: Introduce TemporaryRedis and unset DISCOURSE_* env vars in the themes:isolated_test rake task (#13401)

The `themes:isolated_test` rake task will now unset all `DISCOURSE_*` env variables if `UNSET_DISCOURSE_ENV_VARS` env var is set and will also spin up a temporary redis server so the unicorn web server that's spun up for the tests doesn't leak into the "main" redis server.
This commit is contained in:
Osama Sayegh 2021-06-23 07:38:43 +03:00 committed by GitHub
parent 30c7a9b06d
commit d3a3d1b94c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 2 deletions

View File

@ -42,7 +42,10 @@ task "qunit:test", [:timeout, :qunit_path] do |_, args|
"UNICORN_PID_PATH" => "#{Rails.root}/tmp/pids/unicorn_test_#{port}.pid", # So this can run alongside development
"UNICORN_PORT" => port.to_s,
"UNICORN_SIDEKIQS" => "0",
"DISCOURSE_SKIP_CSS_WATCHER" => "1"
"DISCOURSE_SKIP_CSS_WATCHER" => "1",
"UNICORN_LISTENER" => "127.0.0.1:#{port}",
"LOGSTASH_UNICORN_URI" => nil,
"UNICORN_WORKERS" => "3"
},
"#{Rails.root}/bin/unicorn -c config/unicorn.conf.rb",
pgroup: true

View File

@ -120,7 +120,22 @@ task "themes:qunit", :type, :value do |t, args|
end
desc "Install a theme/component on a temporary DB and run QUnit tests"
task "themes:install_and_test" => :environment do |t, args|
task "themes:isolated_test" => :environment do |t, args|
# This task can be called in a production environment that likely has a bunch
# of DISCOURSE_* env vars that we don't want to be picked up by the Unicorn
# server that will be spawned for the tests. So we need to unset them all
# before we proceed.
# Make this behavior opt-in to make it very obvious.
if ENV["UNSET_DISCOURSE_ENV_VARS"] == "1"
ENV.keys.each do |key|
next if !key.start_with?('DISCOURSE_')
ENV[key] = nil
end
end
redis = TemporaryRedis.new
redis.start
$redis = redis.instance # rubocop:disable Style/GlobalVars
db = TemporaryDb.new
db.start
db.migrate
@ -139,6 +154,7 @@ task "themes:install_and_test" => :environment do |t, args|
ENV["PGHOST"] = "localhost"
ENV["QUNIT_RAILS_ENV"] = "development"
ENV["DISCOURSE_DEV_DB"] = "discourse"
ENV["DISCOURSE_REDIS_PORT"] = redis.port.to_s
count = 0
themes.each do |(name, id)|
@ -155,4 +171,5 @@ task "themes:install_and_test" => :environment do |t, args|
ensure
db&.stop
db&.remove
redis&.remove
end

107
lib/temporary_redis.rb Normal file
View File

@ -0,0 +1,107 @@
# frozen_string_literal: true
class TemporaryRedis
REDIS_TEMP_DIR = "/tmp/discourse_temp_redis"
REDIS_LOG_PATH = "#{REDIS_TEMP_DIR}/redis.log"
REDIS_PID_PATH = "#{REDIS_TEMP_DIR}/redis.pid"
attr_reader :instance
def initialize
set_redis_server_bin
end
def port
@port ||= find_free_port(11000..11900)
end
def start
return if @started
FileUtils.rm_rf(REDIS_TEMP_DIR)
Dir.mkdir(REDIS_TEMP_DIR)
FileUtils.touch(REDIS_LOG_PATH)
puts "Starting redis on port: #{port}"
@thread = Thread.new do
system(
@redis_server_bin,
"--port", port.to_s,
"--pidfile", REDIS_PID_PATH,
"--logfile", REDIS_LOG_PATH,
"--databases", "1",
"--save", '""',
"--appendonly", "no",
"--daemonize", "no",
"--maxclients", "100",
"--dir", REDIS_TEMP_DIR
)
end
puts "Waiting for redis server to start..."
success = false
instance = nil
config = {
port: port,
host: "127.0.0.1",
db: 0
}
start = Time.now
while !success
begin
instance = DiscourseRedis.new(config, namespace: true)
success = instance.ping == "PONG"
rescue Redis::CannotConnectError
ensure
if !success && (Time.now - start) >= 5
STDERR.puts "ERROR: Could not connect to redis in 5 seconds."
self.remove
exit(1)
elsif !success
sleep 0.1
end
end
end
puts "Redis is ready"
@instance = instance
@started = true
end
def remove
if @instance
@instance.shutdown
@thread.join
puts "Redis has been shutdown."
end
FileUtils.rm_rf(REDIS_TEMP_DIR)
@started = false
puts "Redis files have been cleaned up."
end
private
def set_redis_server_bin
path = `which redis-server 2> /dev/null`.strip
if path.size < 1
STDERR.puts 'ERROR: redis-server is not installed on this machine. Please install it'
exit(1)
end
@redis_server_bin = path
rescue => ex
STDERR.puts 'ERROR: Failed to find redis-server binary:'
STDERR.puts ex.inspect
exit(1)
end
def find_free_port(range)
range.each do |port|
return port if port_available?(port)
end
end
def port_available?(port)
TCPServer.open(port).close
true
rescue Errno::EADDRINUSE
false
end
end