Skip to content

Commit

Permalink
Improve tests in preparation for bumping Rails dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
rosa committed Aug 6, 2024
1 parent 3d40b1a commit 194a738
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 68 deletions.
32 changes: 14 additions & 18 deletions test/integration/concurrency_controls_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase

teardown do
terminate_process(@pid) if process_exists?(@pid)

SolidQueue::Job.destroy_all
SolidQueue::Process.destroy_all
SolidQueue::Semaphore.delete_all
end

test "run several conflicting jobs over the same record sequentially" do
Expand All @@ -33,8 +29,8 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase
SequentialUpdateResultJob.perform_later(@result, name: name)
end

wait_for_jobs_to_finish_for(3.seconds)
assert_no_pending_jobs
wait_for_jobs_to_finish_for(5.seconds)
assert_no_unfinished_jobs

assert_stored_sequence @result, ("A".."K").to_a
end
Expand All @@ -51,7 +47,7 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase
end

wait_for_jobs_to_finish_for(5.seconds)
assert_no_pending_jobs
assert_no_unfinished_jobs

assert_stored_sequence @result, ("A".."K").to_a
end
Expand All @@ -78,8 +74,8 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase
end
end

wait_for_jobs_to_finish_for(3.seconds)
assert_no_pending_jobs
wait_for_jobs_to_finish_for(5.seconds)
assert_no_unfinished_jobs

# C would have started in the beginning, seeing the status empty, and would finish after
# all other jobs, so it'll do the last update with only itself
Expand All @@ -96,7 +92,7 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase
SequentialUpdateResultJob.perform_later(@result, name: name)
end

wait_for_jobs_to_finish_for(3.seconds)
wait_for_jobs_to_finish_for(5.seconds)
assert_equal 3, SolidQueue::FailedExecution.count

assert_stored_sequence @result, [ "B", "D", "F" ] + ("G".."K").to_a
Expand All @@ -106,8 +102,8 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase
# Simulate a scenario where we got an available semaphore and some stuck jobs
job = SequentialUpdateResultJob.perform_later(@result, name: "A")

wait_for_jobs_to_finish_for(3.seconds)
assert_no_pending_jobs
wait_for_jobs_to_finish_for(5.seconds)
assert_no_unfinished_jobs

wait_while_with_timeout(1.second) { SolidQueue::Semaphore.where(value: 0).any? }
# Lock the semaphore so we can enqueue jobs and leave them blocked
Expand All @@ -128,8 +124,8 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase
assert SolidQueue::Semaphore.signal(job)

# And wait for the dispatcher to release the jobs
wait_for_jobs_to_finish_for(3.seconds)
assert_no_pending_jobs
wait_for_jobs_to_finish_for(5.seconds)
assert_no_unfinished_jobs

# We can't ensure the order between B and C, because it depends on which worker wins when
# unblocking, as one will try to unblock B and another C
Expand All @@ -139,8 +135,8 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase
test "rely on dispatcher to unblock blocked executions with an expired semaphore" do
# Simulate a scenario where we got an available semaphore and some stuck jobs
job = SequentialUpdateResultJob.perform_later(@result, name: "A")
wait_for_jobs_to_finish_for(3.seconds)
assert_no_pending_jobs
wait_for_jobs_to_finish_for(5.seconds)
assert_no_unfinished_jobs

wait_while_with_timeout(1.second) { SolidQueue::Semaphore.where(value: 0).any? }
# Lock the semaphore so we can enqueue jobs and leave them blocked
Expand All @@ -160,8 +156,8 @@ class ConcurrencyControlsTest < ActiveSupport::TestCase
SolidQueue::BlockedExecution.update_all(expires_at: 15.minutes.ago)

# And wait for dispatcher to release the jobs
wait_for_jobs_to_finish_for(3.seconds)
assert_no_pending_jobs
wait_for_jobs_to_finish_for(5.seconds)
assert_no_unfinished_jobs

# We can't ensure the order between B and C, because it depends on which worker wins when
# unblocking, as one will try to unblock B and another C
Expand Down
55 changes: 27 additions & 28 deletions test/integration/forked_processes_lifecycle_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,13 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase

teardown do
terminate_process(@pid) if process_exists?(@pid)

SolidQueue::Process.destroy_all
SolidQueue::Job.destroy_all
JobResult.delete_all
end

test "enqueue jobs in multiple queues" do
6.times { |i| enqueue_store_result_job("job_#{i}") }
6.times { |i| enqueue_store_result_job("job_#{i}", :default) }

wait_for_jobs_to_finish_for(0.5.seconds)
wait_for_jobs_to_finish_for(2.seconds)

assert_equal 12, JobResult.count
6.times { |i| assert_completed_job_results("job_#{i}", :background) }
Expand Down Expand Up @@ -63,17 +59,18 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase
signal_process(@pid, :TERM, wait: 0.1.second)
end

sleep(0.5.seconds)
sleep(1.seconds)
assert_clean_termination
end

test "quit supervisor while there are jobs in-flight" do
no_pause = enqueue_store_result_job("no pause")
pause = enqueue_store_result_job("pause", pause: 1.seconds)

signal_process(@pid, :QUIT, wait: 0.5.second)
wait_for_jobs_to_finish_for(2.5.seconds)
signal_process(@pid, :QUIT, wait: 0.4.second)
wait_for_jobs_to_finish_for(2.seconds, except: pause)

wait_while_with_timeout(2.seconds) { process_exists?(@pid) }
assert_not process_exists?(@pid)

assert_completed_job_results("no pause")
Expand All @@ -91,7 +88,7 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase
pause = enqueue_store_result_job("pause", pause: 0.2.seconds)

signal_process(@pid, :TERM, wait: 0.1.second)
wait_for_jobs_to_finish_for(0.5.seconds)
wait_for_jobs_to_finish_for(2.seconds)

assert_completed_job_results("no pause")
assert_completed_job_results("pause")
Expand All @@ -108,7 +105,7 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase
pause = enqueue_store_result_job("pause", pause: 0.2.seconds)

signal_process(@pid, :INT, wait: 0.1.second)
wait_for_jobs_to_finish_for(0.5.seconds)
wait_for_jobs_to_finish_for(2.second)

assert_completed_job_results("no pause")
assert_completed_job_results("pause")
Expand All @@ -124,8 +121,9 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase
no_pause = enqueue_store_result_job("no pause")
pause = enqueue_store_result_job("pause", pause: SolidQueue.shutdown_timeout + 10.second)

signal_process(@pid, :TERM, wait: 0.1.second)
wait_for_jobs_to_finish_for(SolidQueue.shutdown_timeout + 0.1.second)
signal_process(@pid, :TERM, wait: 0.5.second)

sleep(SolidQueue.shutdown_timeout + 0.5.second)

assert_completed_job_results("no pause")
assert_job_status(no_pause, :finished)
Expand All @@ -152,12 +150,12 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase
2.times { enqueue_store_result_job("no error", :default, pause: 0.01) }
error3 = enqueue_store_result_job("error", :default, exception: RuntimeError)

wait_for_jobs_to_finish_for(0.5.seconds)
wait_for_jobs_to_finish_for(2.second, except: [ error1, error2, error3 ])

assert_completed_job_results("no error", :background, 3)
assert_completed_job_results("no error", :default, 4)

assert_failures 3
wait_while_with_timeout(1.second) { SolidQueue::FailedExecution.count < 3 }
[ error1, error2, error3 ].each do |job|
assert_job_status(job, :failed)
end
Expand All @@ -177,7 +175,7 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase

2.times { enqueue_store_result_job("no exit", :background) }

wait_for_jobs_to_finish_for(5.seconds)
wait_for_jobs_to_finish_for(3.seconds, except: [ exit_job, pause_job ])

assert_completed_job_results("no exit", :default, 2)
assert_completed_job_results("no exit", :background, 4)
Expand Down Expand Up @@ -218,7 +216,7 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase
# And they can process jobs just fine
enqueue_store_result_job("no_pause")
enqueue_store_result_job("no_pause", :default)
wait_for_jobs_to_finish_for(0.2.seconds)
wait_for_jobs_to_finish_for(1.seconds)

assert_completed_job_results("no_pause", :background)
assert_completed_job_results("no_pause", :default)
Expand All @@ -232,15 +230,14 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase
enqueue_store_result_job("pause", :default, pause: 0.5.seconds)

worker = find_processes_registered_as("Worker").detect { |process| process.metadata["queues"].include? "background" }

signal_process(worker.pid, :KILL, wait: 0.3.second)
signal_process(worker.pid, :KILL, wait: 0.5.seconds)

# Worker didn't have time to clean up or finish the work
sleep(0.7.second)
sleep(0.5.second)
assert SolidQueue::Process.exists?(id: worker.id)

# And there's a new worker that has been registered for the background queue
wait_for_registered_processes(4, timeout: 3.second)
wait_for_registered_processes(4, timeout: 5.second)

# The job in the background queue was left claimed as the worker couldn't
# finish orderly
Expand All @@ -252,7 +249,7 @@ class ForkedProcessesLifecycleTest < ActiveSupport::TestCase
# The two current workers can process jobs just fine
enqueue_store_result_job("no_pause")
enqueue_store_result_job("no_pause", :default)
wait_for_jobs_to_finish_for(0.5.seconds)
sleep(2.seconds)

assert_completed_job_results("no_pause", :background)
assert_completed_job_results("no_pause", :default)
Expand Down Expand Up @@ -291,11 +288,15 @@ def enqueue_store_result_job(value, queue_name = :background, **options)
end

def assert_completed_job_results(value, queue_name = :background, count = 1)
assert_equal count, JobResult.where(queue_name: queue_name, status: "completed", value: value).count
skip_active_record_query_cache do
assert_equal count, JobResult.where(queue_name: queue_name, status: "completed", value: value).count
end
end

def assert_started_job_result(value, queue_name = :background, count = 1)
assert_equal count, JobResult.where(queue_name: queue_name, status: "started", value: value).count
skip_active_record_query_cache do
assert_equal count, JobResult.where(queue_name: queue_name, status: "started", value: value).count
end
end

def assert_job_status(active_job, status)
Expand All @@ -311,10 +312,8 @@ def assert_job_status(active_job, status)
end

def assert_no_claimed_jobs
assert SolidQueue::ClaimedExecution.none?
end

def assert_failures(count)
assert_equal count, SolidQueue::FailedExecution.count
skip_active_record_query_cache do
assert SolidQueue::ClaimedExecution.none?
end
end
end
6 changes: 2 additions & 4 deletions test/integration/puma/plugin_testing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,14 @@ module PluginTesting
teardown do
terminate_process(@pid, signal: :INT) if process_exists?(@pid)

wait_for_registered_processes 0, timeout: 1.second

JobResult.delete_all
wait_for_registered_processes 0, timeout: 2.seconds
end
end

test "perform jobs inside puma's process" do
StoreResultJob.perform_later(:puma_plugin)

wait_for_jobs_to_finish_for(1.second)
wait_for_jobs_to_finish_for(2.seconds)
assert_equal 1, JobResult.where(queue_name: :background, status: "completed", value: :puma_plugin).count
end

Expand Down
4 changes: 2 additions & 2 deletions test/integration/recurring_tasks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class RecurringTasksTest < ActiveSupport::TestCase
end

test "enqueue and process periodic tasks" do
wait_for_jobs_to_be_enqueued(2, timeout: 2.seconds)
wait_for_jobs_to_finish_for(2.seconds)
wait_for_jobs_to_be_enqueued(2, timeout: 2.5.seconds)
wait_for_jobs_to_finish_for(2.5.seconds)

terminate_process(@pid)

Expand Down
5 changes: 0 additions & 5 deletions test/models/solid_queue/job_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@
class SolidQueue::JobTest < ActiveSupport::TestCase
self.use_transactional_tests = false

teardown do
SolidQueue::Job.destroy_all
JobResult.delete_all
end

class NonOverlappingJob < ApplicationJob
limits_concurrency key: ->(job_result, **) { job_result }

Expand Down
16 changes: 13 additions & 3 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,24 @@ class ActiveSupport::TestCase
if SolidQueue.supervisor_pidfile && File.exist?(SolidQueue.supervisor_pidfile)
File.delete(SolidQueue.supervisor_pidfile)
end

unless self.class.use_transactional_tests
SolidQueue::Job.destroy_all
SolidQueue::Process.destroy_all
SolidQueue::Semaphore.delete_all
SolidQueue::RecurringTask.delete_all
JobResult.delete_all
end
end

private
def wait_for_jobs_to_finish_for(timeout = 1.second)
wait_while_with_timeout(timeout) { SolidQueue::Job.where(finished_at: nil).any? }
def wait_for_jobs_to_finish_for(timeout = 1.second, except: [])
wait_while_with_timeout(timeout) do
SolidQueue::Job.where.not(active_job_id: Array(except).map(&:job_id)).where(finished_at: nil).any?
end
end

def assert_no_pending_jobs
def assert_no_unfinished_jobs
skip_active_record_query_cache do
assert SolidQueue::Job.where(finished_at: nil).none?
end
Expand Down
10 changes: 6 additions & 4 deletions test/unit/dispatcher_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ class DispatcherTest < ActiveSupport::TestCase

teardown do
@dispatcher.stop
SolidQueue::Job.delete_all
SolidQueue::Process.delete_all
end

test "dispatcher is registered as process" do
Expand Down Expand Up @@ -87,6 +85,10 @@ class DispatcherTest < ActiveSupport::TestCase
sleep 0.2
end
end

@dispatcher.stop
wait_for_registered_processes(0, timeout: 1.second)
assert_no_registered_processes
end

test "run more than one instance of the dispatcher without recurring tasks" do
Expand All @@ -100,13 +102,13 @@ class DispatcherTest < ActiveSupport::TestCase
@dispatcher.start
another_dispatcher.start

sleep 0.5
sleep(0.7.seconds)

assert_equal 0, SolidQueue::ScheduledExecution.count
assert_equal 15, SolidQueue::ReadyExecution.count

ensure
another_dispatcher.stop
another_dispatcher&.stop
end

test "run more than one instance of the dispatcher with recurring tasks" do
Expand Down
4 changes: 1 addition & 3 deletions test/unit/fork_supervisor_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ class ForkSupervisorTest < ActiveSupport::TestCase
teardown do
SolidQueue.supervisor_pidfile = @previous_pidfile
File.delete(@pidfile) if File.exist?(@pidfile)

SolidQueue::Process.destroy_all
end

test "start" do
Expand All @@ -32,7 +30,7 @@ class ForkSupervisorTest < ActiveSupport::TestCase
test "start with provided configuration" do
config_as_hash = { workers: [], dispatchers: [ { batch_size: 100 } ] }
pid = run_supervisor_as_fork(load_configuration_from: config_as_hash)
wait_for_registered_processes(2) # supervisor + dispatcher
wait_for_registered_processes(2, timeout: 2) # supervisor + dispatcher

assert_registered_supervisor(pid)
assert_registered_workers(count: 0)
Expand Down
1 change: 1 addition & 0 deletions test/unit/hooks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

class HooksTest < ActiveSupport::TestCase
test "solid_queue_record hook ran" do
SolidQueue::Record
assert Rails.application.config.x.solid_queue_record_hook_ran
end
end
Loading

0 comments on commit 194a738

Please sign in to comment.