discourse/spec/services/problem_check_spec.rb
Ted Johansson 8ca5fb706a
DEV: Remove admin notices when destroying problem check trackers (#35976)
When a problem check is run with an invalid target, which can happen if
a target is dynamically removed from the list, we destroy the problem
check tracker.

This makes it so that we also clean up any associated admin notices when
this happens.

It also marks `target` as required. This is because we now use
`__NULL__` to indicate "no target". This allows us to clean up the Ruby
implementation a bit. (Context: this is required for uniqueness checks
to work, since PostgreSQL considers `NULL` values to be distinct.)
2025-11-13 10:34:28 +08:00

176 lines
5.3 KiB
Ruby

# frozen_string_literal: true
RSpec.describe ProblemCheck do
around do |example|
ScheduledCheck = Class.new(described_class) { self.perform_every = 30.minutes }
RealtimeCheck = Class.new(described_class)
InlineCheck = Class.new(described_class) { self.inline = true }
PluginCheck = Class.new(described_class)
DisabledCheck = Class.new(described_class) { self.enabled = false }
MultiTargetCheck = Class.new(described_class) { self.targets = -> { %w[foo bar] } }
FailingCheck =
Class.new(described_class) do
def call
problem
end
def translation_key
"failing_check"
end
end
PassingCheck =
Class.new(described_class) do
def call
no_problem
end
def translation_key
"passing_check"
end
end
stub_const(
described_class,
"CORE_PROBLEM_CHECKS",
[
ScheduledCheck,
RealtimeCheck,
InlineCheck,
DisabledCheck,
MultiTargetCheck,
FailingCheck,
PassingCheck,
],
&example
)
Object.send(:remove_const, ScheduledCheck.name)
Object.send(:remove_const, RealtimeCheck.name)
Object.send(:remove_const, InlineCheck.name)
Object.send(:remove_const, DisabledCheck.name)
Object.send(:remove_const, MultiTargetCheck.name)
Object.send(:remove_const, PluginCheck.name)
Object.send(:remove_const, FailingCheck.name)
Object.send(:remove_const, PassingCheck.name)
end
let(:scheduled_check) { ScheduledCheck }
let(:realtime_check) { RealtimeCheck }
let(:inline_check) { InlineCheck }
let(:enabled_check) { RealtimeCheck }
let(:disabled_check) { DisabledCheck }
let(:multi_target_check) { MultiTargetCheck }
let(:plugin_check) { PluginCheck }
let(:failing_check) { FailingCheck }
let(:passing_check) { PassingCheck }
describe ".[]" do
it { expect(described_class[:scheduled_check]).to eq(scheduled_check) }
it { expect(described_class[:foo]).to eq(nil) }
end
describe ".identifier" do
it { expect(scheduled_check.identifier).to eq(:scheduled_check) }
end
describe ".checks" do
it { expect(described_class.checks).to include(scheduled_check, realtime_check, inline_check) }
end
describe ".scheduled" do
it { expect(described_class.scheduled).to include(scheduled_check) }
it { expect(described_class.scheduled).not_to include(realtime_check) }
it { expect(described_class.scheduled).not_to include(inline_check) }
end
describe ".realtime" do
it { expect(described_class.realtime).to include(realtime_check) }
it { expect(described_class.realtime).not_to include(scheduled_check) }
it { expect(described_class.realtime).not_to include(inline_check) }
end
describe ".scheduled?" do
it { expect(scheduled_check).to be_scheduled }
it { expect(realtime_check).to_not be_scheduled }
it { expect(inline_check).to_not be_scheduled }
end
describe ".realtime?" do
it { expect(realtime_check).to be_realtime }
it { expect(scheduled_check).to_not be_realtime }
it { expect(inline_check).to_not be_realtime }
end
describe ".inline?" do
it { expect(inline_check).to be_inline }
it { expect(realtime_check).to_not be_inline }
it { expect(scheduled_check).to_not be_inline }
end
describe ".enabled?" do
it { expect(enabled_check).to be_enabled }
it { expect(disabled_check).not_to be_enabled }
end
describe ".targeted?" do
it { expect(scheduled_check).not_to be_targeted }
it { expect(multi_target_check).to be_targeted }
end
describe "plugin problem check registration" do
before { DiscoursePluginRegistry.register_problem_check(PluginCheck, stub(enabled?: enabled)) }
after { DiscoursePluginRegistry.reset! }
context "when the plugin is enabled" do
let(:enabled) { true }
it { expect(described_class.checks).to include(plugin_check) }
end
context "when the plugin is disabled" do
let(:enabled) { false }
it { expect(described_class.checks).not_to include(plugin_check) }
end
end
describe "#run" do
context "when check is failing" do
it { expect { failing_check.new.run }.to change { ProblemCheckTracker.failing.count }.by(1) }
end
context "when check is passing" do
it { expect { passing_check.new.run }.to change { ProblemCheckTracker.passing.count }.by(1) }
end
context "when targeted check is initialized with no target" do
context "when a tracker exists" do
before do
ProblemCheckTracker.create!(
identifier: "multi_target_check",
target: ProblemCheck::NO_TARGET,
)
end
it "deletes the tracker" do
expect { multi_target_check.new.run }.to change { ProblemCheckTracker.count }.by(-1)
end
end
context "when a tracker does not exist" do
it "does nothing" do
expect { multi_target_check.new.run }.not_to change { ProblemCheckTracker.count }
end
end
end
context "when targeted check has an outdated target" do
before { ProblemCheckTracker.create!(identifier: "multi_target_check", target: "baz") }
it "deletes the tracker" do
expect { multi_target_check.new("baz").run }.to change { ProblemCheckTracker.count }.by(-1)
end
end
end
end