discourse/spec/jobs/problem_check_spec.rb
Ted Johansson a72dc2f420
DEV: Introduce a problem checks API (#25783)
Previously, problem checks were all added as either class methods or blocks in AdminDashboardData. Another set of class methods were used to add and run problem checks.

As of this PR, problem checks are promoted to first-class citizens. Each problem check receives their own class. This class of course contains the implementation for running the check, but also configuration items like retry strategies (for scheduled checks.)

In addition, the parent class ProblemCheck also serves as a registry for checks. For example we can get a list of all existing check classes through ProblemCheck.checks, or just the ones running on a schedule through ProblemCheck.scheduled.

After this refactor, the task of adding a new check is significantly simplified. You add a class that inherits ProblemCheck, you implement it, add a test, and you're good to go.
2024-02-23 11:20:32 +08:00

131 lines
3.3 KiB
Ruby

# frozen_string_literal: true
RSpec.describe Jobs::ProblemCheck do
after do
Discourse.redis.flushdb
::ProblemCheck.send(:remove_const, "TestCheck")
end
context "when there are problems" do
before do
::ProblemCheck::TestCheck =
Class.new(::ProblemCheck) do
self.max_retries = 0
def call
[
::ProblemCheck::Problem.new("Big problem"),
::ProblemCheck::Problem.new(
"Yuge problem",
priority: "high",
identifier: "config_is_a_mess",
),
]
end
end
end
it "adds the messages to the Redis problems array" do
described_class.new.execute(check_identifier: :test_check)
problems = AdminDashboardData.load_found_scheduled_check_problems
expect(problems.map(&:to_s)).to contain_exactly("Big problem", "Yuge problem")
end
end
context "with multiple problems with the same identifier" do
before do
::ProblemCheck::TestCheck =
Class.new(::ProblemCheck) do
self.max_retries = 0
def call
[
::ProblemCheck::Problem.new(
"Yuge problem",
priority: "high",
identifier: "config_is_a_mess",
),
::ProblemCheck::Problem.new(
"Nasty problem",
priority: "high",
identifier: "config_is_a_mess",
),
]
end
end
end
it "does not add the same problem twice" do
described_class.new.execute(check_identifier: :test_check)
problems = AdminDashboardData.load_found_scheduled_check_problems
expect(problems.map(&:to_s)).to match_array(["Yuge problem"])
end
end
context "when there are retries remaining" do
before do
::ProblemCheck::TestCheck =
Class.new(::ProblemCheck) do
self.max_retries = 2
def call
[::ProblemCheck::Problem.new("Yuge problem")]
end
end
end
it "schedules a retry" do
expect_enqueued_with(
job: :problem_check,
args: {
check_identifier: :test_check,
retry_count: 1,
},
) { described_class.new.execute(check_identifier: :test_check) }
end
end
context "when there are no retries remaining" do
before do
::ProblemCheck::TestCheck =
Class.new(::ProblemCheck) do
self.max_retries = 1
def call
[::ProblemCheck::Problem.new("Yuge problem")]
end
end
end
it "does not schedule a retry" do
expect_not_enqueued_with(job: :problem_check) do
described_class.new.execute(check_identifier: :test_identifier, retry_count: 1)
end
end
end
context "when the check unexpectedly errors out" do
before do
::ProblemCheck::TestCheck =
Class.new(::ProblemCheck) do
self.max_retries = 1
def call
raise StandardError.new("Something went wrong")
end
end
end
it "does not add a problem to the Redis array" do
described_class.new.execute(check_identifier: :test_check)
expect(AdminDashboardData.load_found_scheduled_check_problems).to be_empty
end
end
end