mirror of
https://github.com/discourse/discourse.git
synced 2024-11-27 08:33:39 +08:00
4967541275
Our internal implementation of #perform on jobs performs remapping. This happens cause we do "exception aggregation". Scheduled jobs run on every site in the multisite cluster, and we report one error per site that failed. During this aggregation we reshape the context from the original object shape returned by mini_scheduler The new integration test ensures this interface will remain stable even if decoupled parts of the code change shapes. Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
112 lines
2.7 KiB
Ruby
112 lines
2.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe ::Jobs::Base do
|
|
class GoodJob < ::Jobs::Base
|
|
attr_accessor :count
|
|
def execute(args)
|
|
self.count ||= 0
|
|
self.count += 1
|
|
end
|
|
end
|
|
|
|
class BadJob < ::Jobs::Base
|
|
class BadJobError < StandardError
|
|
end
|
|
|
|
attr_accessor :fail_count
|
|
|
|
def execute(args)
|
|
@fail_count ||= 0
|
|
@fail_count += 1
|
|
raise BadJobError
|
|
end
|
|
end
|
|
|
|
it 'handles correct jobs' do
|
|
job = GoodJob.new
|
|
job.perform({})
|
|
expect(job.count).to eq(1)
|
|
end
|
|
|
|
it 'handles errors in multisite' do
|
|
RailsMultisite::ConnectionManagement.expects(:all_dbs).returns(['default', 'default', 'default'])
|
|
# one exception per database
|
|
Discourse.expects(:handle_job_exception).times(3)
|
|
|
|
bad = BadJob.new
|
|
expect { bad.perform({}) }.to raise_error(Jobs::HandledExceptionWrapper)
|
|
expect(bad.fail_count).to eq(3)
|
|
end
|
|
|
|
describe '#perform' do
|
|
context 'when a job raises an error' do
|
|
before do
|
|
Discourse.reset_job_exception_stats!
|
|
end
|
|
|
|
after do
|
|
Discourse.reset_job_exception_stats!
|
|
end
|
|
|
|
it 'collects stats for failing jobs in Discourse.job_exception_stats' do
|
|
bad = BadJob.new
|
|
3.times do
|
|
# During test env handle_job_exception errors out
|
|
# in production this is suppressed
|
|
expect { bad.perform({}) }.to raise_error(BadJob::BadJobError)
|
|
end
|
|
|
|
expect(Discourse.job_exception_stats).to eq({ BadJob => 3 })
|
|
end
|
|
end
|
|
end
|
|
|
|
it 'delegates the process call to execute' do
|
|
::Jobs::Base.any_instance.expects(:execute).with('hello' => 'world')
|
|
::Jobs::Base.new.perform('hello' => 'world', 'sync_exec' => true)
|
|
end
|
|
|
|
it 'converts to an indifferent access hash' do
|
|
::Jobs::Base.any_instance.expects(:execute).with(instance_of(HashWithIndifferentAccess))
|
|
::Jobs::Base.new.perform('hello' => 'world', 'sync_exec' => true)
|
|
end
|
|
|
|
context "with fake jobs" do
|
|
let(:common_state) { [] }
|
|
|
|
let(:test_job_1) {
|
|
Class.new(Jobs::Base).tap do |klass|
|
|
state = common_state
|
|
klass.define_method(:execute) do |args|
|
|
state << "job_1_executed"
|
|
end
|
|
end
|
|
}
|
|
|
|
let(:test_job_2) {
|
|
Class.new(Jobs::Base).tap do |klass|
|
|
state = common_state
|
|
job_1 = test_job_1
|
|
klass.define_method(:execute) do |args|
|
|
state << "job_2_started"
|
|
Jobs.enqueue(job_1)
|
|
state << "job_2_finished"
|
|
end
|
|
end
|
|
}
|
|
|
|
it "runs jobs synchronously sequentially in tests" do
|
|
Jobs.run_immediately!
|
|
Jobs.enqueue(test_job_2)
|
|
|
|
expect(common_state).to eq([
|
|
"job_2_started",
|
|
"job_2_finished",
|
|
"job_1_executed"
|
|
])
|
|
end
|
|
|
|
end
|
|
|
|
end
|