From 175ba6666453684bba3c24d03b75580a1f8e68bb Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Mon, 18 Aug 2014 10:19:41 +0300 Subject: ActiveJob Integration Tests --- .../support/integration/adapters/backburner.rb | 38 +++++++++ .../support/integration/adapters/delayed_job.rb | 20 +++++ .../test/support/integration/adapters/inline.rb | 15 ++++ activejob/test/support/integration/adapters/qu.rb | 38 +++++++++ activejob/test/support/integration/adapters/que.rb | 37 +++++++++ .../support/integration/adapters/queue_classic.rb | 45 +++++++++++ .../test/support/integration/adapters/resque.rb | 47 +++++++++++ .../test/support/integration/adapters/sidekiq.rb | 58 ++++++++++++++ .../test/support/integration/adapters/sneakers.rb | 90 ++++++++++++++++++++++ .../support/integration/adapters/sucker_punch.rb | 5 ++ .../test/support/integration/dummy_app_template.rb | 21 +++++ activejob/test/support/integration/helper.rb | 32 ++++++++ activejob/test/support/integration/jobs_manager.rb | 27 +++++++ .../test/support/integration/test_case_helpers.rb | 48 ++++++++++++ 14 files changed, 521 insertions(+) create mode 100644 activejob/test/support/integration/adapters/backburner.rb create mode 100644 activejob/test/support/integration/adapters/delayed_job.rb create mode 100644 activejob/test/support/integration/adapters/inline.rb create mode 100644 activejob/test/support/integration/adapters/qu.rb create mode 100644 activejob/test/support/integration/adapters/que.rb create mode 100644 activejob/test/support/integration/adapters/queue_classic.rb create mode 100644 activejob/test/support/integration/adapters/resque.rb create mode 100644 activejob/test/support/integration/adapters/sidekiq.rb create mode 100644 activejob/test/support/integration/adapters/sneakers.rb create mode 100644 activejob/test/support/integration/adapters/sucker_punch.rb create mode 100644 activejob/test/support/integration/dummy_app_template.rb create mode 100644 activejob/test/support/integration/helper.rb create mode 100644 activejob/test/support/integration/jobs_manager.rb create mode 100644 activejob/test/support/integration/test_case_helpers.rb (limited to 'activejob/test/support/integration') diff --git a/activejob/test/support/integration/adapters/backburner.rb b/activejob/test/support/integration/adapters/backburner.rb new file mode 100644 index 0000000000..0cda36a273 --- /dev/null +++ b/activejob/test/support/integration/adapters/backburner.rb @@ -0,0 +1,38 @@ +module BackburnerJobsManager + def setup + ActiveJob::Base.queue_adapter = :backburner + Backburner.configure do |config| + config.logger = Rails.logger + end + unless can_run? + puts "Cannot run integration tests for backburner. To be able to run integration tests for backburner you need to install and start beanstalkd.\n" + exit + end + end + + def clear_jobs + tube.clear + end + + def start_workers + @thread = Thread.new { Backburner.work "integration-tests" } # backburner dasherizes the queue name + end + + def stop_workers + @thread.kill + end + + def tube + @tube ||= Beaneater::Tube.new(Backburner::Worker.connection, "backburner.worker.queue.integration-tests") # backburner dasherizes the queue name + end + + def can_run? + begin + Backburner::Worker.connection.send :connect! + rescue => e + return false + end + true + end +end + diff --git a/activejob/test/support/integration/adapters/delayed_job.rb b/activejob/test/support/integration/adapters/delayed_job.rb new file mode 100644 index 0000000000..dbd0d1a4db --- /dev/null +++ b/activejob/test/support/integration/adapters/delayed_job.rb @@ -0,0 +1,20 @@ +require 'delayed_job' +require 'delayed_job_active_record' + +module DelayedJobJobsManager + def setup + ActiveJob::Base.queue_adapter = :delayed_job + end + def clear_jobs + Delayed::Job.delete_all + end + + def start_workers + @worker = Delayed::Worker.new(quiet: false, sleep_delay: 0.5, queues: %w(integration_tests)) + @thread = Thread.new { @worker.start } + end + + def stop_workers + @worker.stop + end +end diff --git a/activejob/test/support/integration/adapters/inline.rb b/activejob/test/support/integration/adapters/inline.rb new file mode 100644 index 0000000000..83c38f706f --- /dev/null +++ b/activejob/test/support/integration/adapters/inline.rb @@ -0,0 +1,15 @@ +module InlineJobsManager + def setup + ActiveJob::Base.queue_adapter = :inline + end + + def clear_jobs + end + + def start_workers + end + + def stop_workers + end +end + diff --git a/activejob/test/support/integration/adapters/qu.rb b/activejob/test/support/integration/adapters/qu.rb new file mode 100644 index 0000000000..e913f04a24 --- /dev/null +++ b/activejob/test/support/integration/adapters/qu.rb @@ -0,0 +1,38 @@ +module QuJobsManager + def setup + require 'qu-rails' + require 'qu-redis' + ActiveJob::Base.queue_adapter = :qu + ENV['REDISTOGO_URL'] = "tcp://127.0.0.1:6379/12" + backend = Qu::Backend::Redis.new + backend.namespace = "active_jobs_int_test" + Qu.backend = backend + Qu.logger = Rails.logger + Qu.interval = 0.5 + unless can_run? + puts "Cannot run integration tests for qu. To be able to run integration tests for qu you need to install and start redis.\n" + exit + end + end + + def clear_jobs + Qu.clear "integration_tests" + end + + def start_workers + @thread = Thread.new { Qu::Worker.new("integration_tests").start } + end + + def stop_workers + @thread.kill + end + + def can_run? + begin + Qu.backend.connection.client.connect + rescue => e + return false + end + true + end +end diff --git a/activejob/test/support/integration/adapters/que.rb b/activejob/test/support/integration/adapters/que.rb new file mode 100644 index 0000000000..a5b9b3ec0a --- /dev/null +++ b/activejob/test/support/integration/adapters/que.rb @@ -0,0 +1,37 @@ +module QueJobsManager + def setup + require 'sequel' + ActiveJob::Base.queue_adapter = :que + que_url = ENV['QUE_DATABASE_URL'] || 'postgres://localhost/active_jobs_que_int_test' + uri = URI.parse(que_url) + user = uri.user||ENV['USER'] + pass = uri.password + db = uri.path[1..-1] + %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database "#{db}"' -U #{user} -t template1} + %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1} + Que.connection = Sequel.connect(que_url) + Que.migrate! + Que.mode = :off + Que.worker_count = 1 + rescue Sequel::DatabaseConnectionError + puts "Cannot run integration tests for que. To be able to run integration tests for que you need to install and start postgresql.\n" + exit + end + + def clear_jobs + Que.clear! + end + + def start_workers + @thread = Thread.new do + loop do + Que::Job.work("integration_tests") + sleep 0.5 + end + end + end + + def stop_workers + @thread.kill + end +end diff --git a/activejob/test/support/integration/adapters/queue_classic.rb b/activejob/test/support/integration/adapters/queue_classic.rb new file mode 100644 index 0000000000..81d1935132 --- /dev/null +++ b/activejob/test/support/integration/adapters/queue_classic.rb @@ -0,0 +1,45 @@ +module QueueClassicJobsManager + def setup + ENV['QC_DATABASE_URL'] ||= 'postgres://localhost/active_jobs_qc_int_test' + ENV['QC_LISTEN_TIME'] = "0.5" + uri = URI.parse(ENV['QC_DATABASE_URL']) + user = uri.user||ENV['USER'] + pass = uri.password + db = uri.path[1..-1] + %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database "#{db}"' -U #{user} -t template1} + %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1} + ActiveJob::Base.queue_adapter = :queue_classic + QC::Setup.create + rescue PG::ConnectionBad + puts "Cannot run integration tests for queue_classic. To be able to run integration tests for queue_classic you need to install and start postgresql.\n" + exit + end + + def clear_jobs + QC::Queue.new("integration_tests").delete_all + retried = false + rescue => e + puts "Got exception while trying to clear jobs: #{e.inspect}" + if retried + puts "Already retried. Raising exception" + raise e + else + puts "Retrying" + retried = true + QC::Conn.connection = QC::Conn.connect + retry + end + end + + def start_workers + @pid = fork do + QC::Conn.connection = QC::Conn.connect + worker = QC::Worker.new(q_name: 'integration_tests') + worker.start + end + end + + def stop_workers + Process.kill 'HUP', @pid + end +end diff --git a/activejob/test/support/integration/adapters/resque.rb b/activejob/test/support/integration/adapters/resque.rb new file mode 100644 index 0000000000..03ffd3fd62 --- /dev/null +++ b/activejob/test/support/integration/adapters/resque.rb @@ -0,0 +1,47 @@ +module ResqueJobsManager + def setup + ActiveJob::Base.queue_adapter = :resque + Resque.redis = Redis::Namespace.new 'active_jobs_int_test', redis: Redis.connect(url: "tcp://127.0.0.1:6379/12", :thread_safe => true) + Resque.logger = Rails.logger + unless can_run? + puts "Cannot run integration tests for resque. To be able to run integration tests for resque you need to install and start redis.\n" + exit + end + end + + def clear_jobs + Resque.queues.each { |queue_name| Resque.redis.del "queue:#{queue_name}" } + Resque.redis.keys("delayed:*").each { |key| Resque.redis.del "#{key}" } + Resque.redis.del "delayed_queue_schedule" + end + + def start_workers + @resque_thread = Thread.new do + Resque::Worker.new("integration_tests").work(0.5) + end + @scheduler_thread = Thread.new do + Resque::Scheduler.configure do |c| + c.poll_sleep_amount = 0.5 + c.dynamic = true + c.verbose = true + c.logfile = nil + end + Resque::Scheduler.master_lock.release! + Resque::Scheduler.run + end + end + + def stop_workers + @resque_thread.kill + @scheduler_thread.kill + end + + def can_run? + begin + Resque.redis.client.connect + rescue => e + return false + end + true + end +end diff --git a/activejob/test/support/integration/adapters/sidekiq.rb b/activejob/test/support/integration/adapters/sidekiq.rb new file mode 100644 index 0000000000..b3c3dcff22 --- /dev/null +++ b/activejob/test/support/integration/adapters/sidekiq.rb @@ -0,0 +1,58 @@ +require 'sidekiq/cli' +require 'sidekiq/api' + +module SidekiqJobsManager + + def setup + ActiveJob::Base.queue_adapter = :sidekiq + unless can_run? + puts "Cannot run integration tests for sidekiq. To be able to run integration tests for sidekiq you need to install and start redis.\n" + exit + end + end + + def clear_jobs + Sidekiq::ScheduledSet.new.clear + Sidekiq::Queue.new("integration_tests").clear + end + + def start_workers + fork do + sidekiq = Sidekiq::CLI.instance + logfile = Rails.root.join("log/sidekiq.log").to_s + pidfile = Rails.root.join("tmp/sidekiq.pid").to_s + sidekiq.parse([ "--require", Rails.root.to_s, + "--queue", "integration_tests", + "--logfile", logfile, + "--pidfile", pidfile, + "--environment", "test", + "--concurrency", "1", + "--timeout", "1", + "--daemon", + "--verbose" + ]) + require 'celluloid' + require 'sidekiq/scheduled' + Sidekiq.poll_interval = 0.5 + Sidekiq::Scheduled.const_set :INITIAL_WAIT, 1 + sidekiq.run + end + sleep 1 + end + + def stop_workers + pidfile = Rails.root.join("tmp/sidekiq.pid").to_s + Process.kill 'TERM', File.open(pidfile).read.to_i + FileUtils.rm_f pidfile + rescue + end + + def can_run? + begin + Sidekiq.redis { |conn| conn.connect } + rescue => e + return false + end + true + end +end diff --git a/activejob/test/support/integration/adapters/sneakers.rb b/activejob/test/support/integration/adapters/sneakers.rb new file mode 100644 index 0000000000..f21bb38a32 --- /dev/null +++ b/activejob/test/support/integration/adapters/sneakers.rb @@ -0,0 +1,90 @@ +require 'sneakers/runner' +require 'sneakers/publisher' +require 'timeout' + +module Sneakers + class Publisher + def safe_ensure_connected + @mutex.synchronize do + ensure_connection! unless connected? + end + end + end +end + + +module SneakersJobsManager + def setup + ActiveJob::Base.queue_adapter = :sneakers + Sneakers.configure :heartbeat => 2, + :amqp => 'amqp://guest:guest@localhost:5672', + :vhost => '/', + :exchange => 'active_jobs_sneakers_int_test', + :exchange_type => :direct, + :daemonize => true, + :threads => 1, + :workers => 1, + :pid_path => Rails.root.join("tmp/sneakers.pid").to_s, + :log => Rails.root.join("log/sneakers.log").to_s + unless can_run? + puts "Cannot run integration tests for sneakers. To be able to run integration tests for sneakers you need to install and start rabbitmq.\n" + exit + end + end + + def clear_jobs + bunny_queue.purge + end + + def start_workers + @pid = fork do + queues = %w(integration_tests) + workers = queues.map do |q| + worker_klass = "ActiveJobWorker"+Digest::MD5.hexdigest(q) + Sneakers.const_set(worker_klass, Class.new(ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper) do + from_queue q + end) + end + Sneakers::Runner.new(workers).run + end + begin + Timeout.timeout(10) do + while bunny_queue.status[:consumer_count] == 0 + sleep 0.5 + end + end + rescue Timeout::Error + stop_workers + raise "Failed to start sneakers worker" + end + end + + def stop_workers + Process.kill 'TERM', @pid + Process.kill 'TERM', File.open(Rails.root.join("tmp/sneakers.pid").to_s).read.to_i + rescue + end + + def can_run? + begin + bunny_publisher + rescue => e + return false + end + true + end + + protected + def bunny_publisher + @bunny_publisher ||= begin + p = ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper.send(:publisher) + p.safe_ensure_connected + p + end + end + + def bunny_queue + @queue ||= bunny_publisher.exchange.channel.queue "integration_tests", durable: true + end + +end diff --git a/activejob/test/support/integration/adapters/sucker_punch.rb b/activejob/test/support/integration/adapters/sucker_punch.rb new file mode 100644 index 0000000000..691ba35c90 --- /dev/null +++ b/activejob/test/support/integration/adapters/sucker_punch.rb @@ -0,0 +1,5 @@ +module SuckerPunchJobsManager + def setup + ActiveJob::Base.queue_adapter = :sucker_punch + end +end diff --git a/activejob/test/support/integration/dummy_app_template.rb b/activejob/test/support/integration/dummy_app_template.rb new file mode 100644 index 0000000000..28aae0f884 --- /dev/null +++ b/activejob/test/support/integration/dummy_app_template.rb @@ -0,0 +1,21 @@ +if ENV['AJADAPTER'] == 'delayed_job' + generate "delayed_job:active_record" + rake("db:migrate") +end + +initializer 'activejob.rb', <<-CODE +require "#{File.expand_path("../jobs_manager.rb", __FILE__)}" +JobsManager.current_manager.setup +CODE + +file 'app/jobs/test_job.rb', <<-CODE +class TestJob < ActiveJob::Base + queue_as :integration_tests + + def perform(x) + File.open(Rails.root.join("tmp/\#{x}"), "w+") do |f| + f.write x + end + end +end +CODE diff --git a/activejob/test/support/integration/helper.rb b/activejob/test/support/integration/helper.rb new file mode 100644 index 0000000000..ccd5036b36 --- /dev/null +++ b/activejob/test/support/integration/helper.rb @@ -0,0 +1,32 @@ +puts "\n\n" +puts "*** Running integration tests for #{ENV['AJADAPTER']} ***" +puts "\n\n" + +ENV["RAILS_ENV"] = "test" +ActiveJob::Base.queue_name_prefix = nil + +require 'rails/generators/rails/app/app_generator' + +dummy_app_path = Dir.mktmpdir + "/dummy" +dummy_app_template = File.expand_path("../dummy_app_template.rb", __FILE__) +args = Rails::Generators::ARGVScrubber.new(["new", dummy_app_path, "--skip-gemfile", "--skip-bundle", + "--skip-git", "--skip-spring", "-d", "sqlite3", "--skip-javascript", "--force", "--quite", + "--template", dummy_app_template]).prepare! +Rails::Generators::AppGenerator.start args + +require "#{dummy_app_path}/config/environment.rb" + +ActiveRecord::Migrator.migrations_paths = [ Rails.root.join('db/migrate').to_s ] +require 'rails/test_help' + +Rails.backtrace_cleaner.remove_silencers! + +require_relative 'test_case_helpers' +ActiveSupport::TestCase.send(:include, TestCaseHelpers) + +JobsManager.current_manager.start_workers + +Minitest.after_run do + JobsManager.current_manager.stop_workers + JobsManager.current_manager.clear_jobs +end diff --git a/activejob/test/support/integration/jobs_manager.rb b/activejob/test/support/integration/jobs_manager.rb new file mode 100644 index 0000000000..4df34aaeb1 --- /dev/null +++ b/activejob/test/support/integration/jobs_manager.rb @@ -0,0 +1,27 @@ +class JobsManager + @@managers = {} + attr :adapter_name + + def self.current_manager + @@managers[ENV['AJADAPTER']] ||= new(ENV['AJADAPTER']) + end + + def initialize(adapter_name) + @adapter_name = adapter_name + require_relative "adapters/#{adapter_name}" + extend "#{adapter_name.camelize}JobsManager".constantize + end + + def setup + ActiveJob::Base.queue_adapter = nil + end + + def clear_jobs + end + + def start_workers + end + + def stop_workers + end +end diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb new file mode 100644 index 0000000000..ee2f6aebea --- /dev/null +++ b/activejob/test/support/integration/test_case_helpers.rb @@ -0,0 +1,48 @@ +require 'active_support/concern' +require 'support/integration/jobs_manager' + +module TestCaseHelpers + extend ActiveSupport::Concern + + included do + self.use_transactional_fixtures = false + + setup do + clear_jobs + @id = "AJ-#{SecureRandom.uuid}" + end + + teardown do + clear_jobs + end + end + + protected + + def jobs_manager + JobsManager.current_manager + end + + def clear_jobs + jobs_manager.clear_jobs + end + + def adapter_is?(adapter) + ActiveJob::Base.queue_adapter.name.split("::").last.gsub(/Adapter$/, '').underscore==adapter.to_s + end + + def wait_for_jobs_to_finish_for(seconds=60) + begin + Timeout.timeout(seconds) do + while !job_executed do + sleep 0.25 + end + end + rescue Timeout::Error + end + end + + def job_executed + Dummy::Application.root.join("tmp/#{@id}").exist? + end +end -- cgit v1.2.3