mirror of
https://github.com/discourse/discourse.git
synced 2026-08-10 23:59:31 +08:00
RSpec setup becomes harder to follow at either extreme: trivial fixture wrappers hide lifecycle and intent, while forcing every named operation inline repeats low-level protocol and configuration details. This change documents and applies a test-setup hierarchy: - use `fab!`, `let`, `let!`, `subject`, and inline `Fabricate` according to lifecycle and role; - use a small example-group method when parameterized behavior gives one spec useful vocabulary; - move helpers into auto-loaded `spec/support` only when they are shared across spec files; - use fabricators and page objects for the data shapes and system-test interfaces they own. Core and plugin support files are loaded centrally by `rails_helper`, so plugin-specific support loaders are unnecessary. The migration specs encountered during the sweep are removed according to repository policy; production migrations are unchanged.
377 lines
11 KiB
Ruby
Vendored
377 lines
11 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
RSpec.describe ProblemCheckTracker do
|
|
describe "validations" do
|
|
let(:record) { described_class.new(identifier: "twitter_login") }
|
|
|
|
it { expect(record).to validate_presence_of(:identifier) }
|
|
it { expect(record).to validate_uniqueness_of(:identifier).scoped_to(:target) }
|
|
|
|
it { expect(record).to validate_numericality_of(:blips).is_greater_than_or_equal_to(0) }
|
|
|
|
it { expect(record).to validate_presence_of(:target) }
|
|
end
|
|
|
|
describe "callbacks" do
|
|
describe "before_destroy (silence the alarm)" do
|
|
let(:tracker) do
|
|
ProblemCheckTracker.create!(identifier: "twitter_login", target: ProblemCheck::NO_TARGET)
|
|
end
|
|
|
|
before { tracker.problem! }
|
|
|
|
it "removes any associated admin notices" do
|
|
expect { tracker.destroy }.to change { AdminNotice.count }.by(-1)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe ".[]" do
|
|
fab!(:twitter_login_tracker) { Fabricate(:problem_check_tracker, identifier: "twitter_login") }
|
|
|
|
context "when the problem check tracker already exists" do
|
|
it { expect(described_class[:twitter_login]).not_to be_new_record }
|
|
end
|
|
|
|
context "when the problem check tracker doesn't exist yet" do
|
|
it { expect(described_class[:facebook_login]).to be_previously_new_record }
|
|
end
|
|
end
|
|
|
|
describe "#check" do
|
|
fab!(:twitter_login_tracker) { Fabricate(:problem_check_tracker, identifier: "twitter_login") }
|
|
fab!(:missing_check_tracker) { Fabricate(:problem_check_tracker, identifier: "missing_check") }
|
|
|
|
context "when the tracker has a corresponding check" do
|
|
it { expect(described_class[:twitter_login].check.new).to be_a(ProblemCheck) }
|
|
end
|
|
|
|
context "when the checking logic of the tracker has been removed or renamed" do
|
|
it do
|
|
expect { described_class[:missing_check].check }.to change { described_class.count }.by(-1)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#ready_to_run?" do
|
|
let(:problem_tracker) { described_class.new(next_run_at:) }
|
|
|
|
context "when the next run timestamp is not set" do
|
|
let(:next_run_at) { nil }
|
|
|
|
it { expect(problem_tracker).to be_ready_to_run }
|
|
end
|
|
|
|
context "when the next run timestamp is in the past" do
|
|
let(:next_run_at) { 5.minutes.ago }
|
|
|
|
it { expect(problem_tracker).to be_ready_to_run }
|
|
end
|
|
|
|
context "when the next run timestamp is in the future" do
|
|
let(:next_run_at) { 5.minutes.from_now }
|
|
|
|
it { expect(problem_tracker).not_to be_ready_to_run }
|
|
end
|
|
end
|
|
|
|
describe "#failing?" do
|
|
before { freeze_time }
|
|
|
|
let(:problem_tracker) { described_class.new(last_problem_at:, last_run_at:, last_success_at:) }
|
|
|
|
context "when the last run passed" do
|
|
let(:last_run_at) { 1.minute.ago }
|
|
let(:last_success_at) { 1.minute.ago }
|
|
let(:last_problem_at) { 11.minutes.ago }
|
|
|
|
it { expect(problem_tracker).not_to be_failing }
|
|
end
|
|
|
|
context "when the last run had a problem" do
|
|
let(:last_run_at) { 1.minute.ago }
|
|
let(:last_success_at) { 11.minutes.ago }
|
|
let(:last_problem_at) { 1.minute.ago }
|
|
|
|
it { expect(problem_tracker).to be_failing }
|
|
end
|
|
end
|
|
|
|
describe "#passing?" do
|
|
before { freeze_time }
|
|
|
|
let(:problem_tracker) { described_class.new(last_problem_at:, last_run_at:, last_success_at:) }
|
|
|
|
context "when the last run passed" do
|
|
let(:last_run_at) { 1.minute.ago }
|
|
let(:last_success_at) { 1.minute.ago }
|
|
let(:last_problem_at) { 11.minutes.ago }
|
|
|
|
it { expect(problem_tracker).to be_passing }
|
|
end
|
|
|
|
context "when the last run had a problem" do
|
|
let(:last_run_at) { 1.minute.ago }
|
|
let(:last_success_at) { 11.minutes.ago }
|
|
let(:last_problem_at) { 1.minute.ago }
|
|
|
|
it { expect(problem_tracker).not_to be_passing }
|
|
end
|
|
end
|
|
|
|
describe "#ignored?" do
|
|
let(:problem_tracker) { described_class.new(ignored_at:) }
|
|
|
|
context "when the ignored timestamp is set" do
|
|
let(:ignored_at) { 1.day.ago }
|
|
|
|
it { expect(problem_tracker).to be_ignored }
|
|
end
|
|
|
|
context "when the ignored timestamp is not set" do
|
|
let(:ignored_at) { nil }
|
|
|
|
it { expect(problem_tracker).not_to be_ignored }
|
|
end
|
|
end
|
|
|
|
describe "#watched?" do
|
|
let(:problem_tracker) { described_class.new(ignored_at:) }
|
|
|
|
context "when the ignored timestamp is set" do
|
|
let(:ignored_at) { 1.day.ago }
|
|
|
|
it { expect(problem_tracker).not_to be_watched }
|
|
end
|
|
|
|
context "when the ignored timestamp is not set" do
|
|
let(:ignored_at) { nil }
|
|
|
|
it { expect(problem_tracker).to be_watched }
|
|
end
|
|
end
|
|
|
|
describe "#ignore!" do
|
|
let(:problem_tracker) { Fabricate(:problem_check_tracker, ignored_at:) }
|
|
|
|
context "when not currently ignored" do
|
|
let(:ignored_at) { nil }
|
|
|
|
it "sets the ignore timestamp" do
|
|
freeze_time
|
|
|
|
expect { problem_tracker.ignore! }.to change { problem_tracker.ignored_at }.from(nil).to(
|
|
be_within_one_second_of Time.current
|
|
)
|
|
end
|
|
end
|
|
|
|
context "when already ignored" do
|
|
let(:ignored_at) { 1.day.ago }
|
|
|
|
it "does not touch the ignore timestamp" do
|
|
expect { problem_tracker.ignore! }.not_to change { problem_tracker.ignored_at }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#watch!" do
|
|
let(:problem_tracker) { Fabricate(:problem_check_tracker, ignored_at:) }
|
|
|
|
context "when not currently ignored" do
|
|
let(:ignored_at) { nil }
|
|
|
|
it "does not touch the ignore timestamp" do
|
|
expect { problem_tracker.watch! }.not_to change { problem_tracker.ignored_at }
|
|
end
|
|
end
|
|
|
|
context "when currently ignored" do
|
|
let(:ignored_at) { 1.day.ago }
|
|
|
|
it "clears the ignore timestamp" do
|
|
expect { problem_tracker.watch! }.to change { problem_tracker.ignored_at }.from(
|
|
be_within_one_second_of ignored_at
|
|
).to(nil)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#problem!" do
|
|
let(:problem_tracker) do
|
|
Fabricate(
|
|
:problem_check_tracker,
|
|
identifier: "twitter_login",
|
|
target: "foo",
|
|
ignored_at:,
|
|
**original_attributes,
|
|
)
|
|
end
|
|
|
|
let(:original_attributes) do
|
|
{
|
|
blips:,
|
|
last_problem_at: 1.week.ago,
|
|
last_success_at: 24.hours.ago,
|
|
last_run_at: 24.hours.ago,
|
|
next_run_at: nil,
|
|
}
|
|
end
|
|
|
|
let(:blips) { 0 }
|
|
let(:updated_attributes) { { blips: 1 } }
|
|
let(:ignored_at) { nil }
|
|
|
|
it do
|
|
freeze_time
|
|
|
|
expect { problem_tracker.problem!(next_run_at: 24.hours.from_now) }.to change {
|
|
problem_tracker.attributes
|
|
}.to(hash_including(updated_attributes))
|
|
end
|
|
|
|
context "when the maximum number of blips have been surpassed" do
|
|
let(:blips) { 1 }
|
|
|
|
context "when the check isn't being ignored" do
|
|
let(:ignored_at) { nil }
|
|
|
|
it "sounds the alarm" do
|
|
expect { problem_tracker.problem!(next_run_at: 24.hours.from_now) }.to change {
|
|
AdminNotice.problem.count
|
|
}.by(1)
|
|
end
|
|
end
|
|
|
|
context "when the check is being ignored" do
|
|
let(:ignored_at) { 1.day.ago }
|
|
|
|
it "does not sound the alarm" do
|
|
expect { problem_tracker.problem!(next_run_at: 24.hours.from_now) }.not_to change {
|
|
AdminNotice.problem.count
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when the details of the problem change but the problem remains" do
|
|
let(:blips) { 1 }
|
|
|
|
it "updates the notice" do
|
|
original_details = {
|
|
themes_list:
|
|
"<ul><li><a href=\"/admin/customize/themes/13\">discourse-blank-theme</a></li> <li><a href=\"/admin/customize/themes/31\">Simple Theme</a></li></ul>",
|
|
base_path: "",
|
|
}
|
|
|
|
expect do problem_tracker.problem!(details: original_details) end.to change {
|
|
AdminNotice.problem.count
|
|
}.by(1)
|
|
|
|
admin_notice = AdminNotice.problem.find_by(identifier: "twitter_login")
|
|
|
|
expect(
|
|
admin_notice.details.merge(target: problem_tracker.target).with_indifferent_access,
|
|
).to eq(original_details.merge(target: problem_tracker.target).with_indifferent_access)
|
|
|
|
new_details = {
|
|
themes_list: "<ul><li><a href=\"/admin/customize/themes/31\">Simple Theme</a></li></ul>",
|
|
base_path: "",
|
|
}
|
|
expect do problem_tracker.problem!(details: new_details) end.not_to change {
|
|
AdminNotice.problem.count
|
|
}
|
|
|
|
admin_notice.reload
|
|
|
|
expect(
|
|
admin_notice.details.merge(target: problem_tracker.target).with_indifferent_access,
|
|
).to eq(new_details.merge(target: problem_tracker.target).with_indifferent_access)
|
|
end
|
|
end
|
|
|
|
context "when there's an alarm sounding for multi-target trackers" do
|
|
let(:blips) { 1 }
|
|
let!(:existing_admin_notice) do
|
|
Fabricate(
|
|
:admin_notice,
|
|
subject: "problem",
|
|
identifier: "twitter_login",
|
|
details: {
|
|
target: target,
|
|
},
|
|
)
|
|
end
|
|
|
|
context "when the alarm is for a different target" do
|
|
let(:target) { "bar" }
|
|
|
|
it "sounds the alarm" do
|
|
expect { problem_tracker.problem!(next_run_at: 24.hours.from_now) }.to change {
|
|
AdminNotice.problem.count
|
|
}.by(1)
|
|
end
|
|
end
|
|
|
|
context "when the alarm is for a the same target" do
|
|
let(:target) { "foo" }
|
|
|
|
it "does not duplicate the alarm" do
|
|
expect { problem_tracker.problem!(next_run_at: 24.hours.from_now) }.not_to change {
|
|
AdminNotice.problem.count
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when there are still blips to go" do
|
|
let(:blips) { 0 }
|
|
|
|
before { ProblemCheck::TwitterLogin.stubs(:max_blips).returns(1) }
|
|
|
|
it "does not sound the alarm" do
|
|
expect { problem_tracker.problem!(next_run_at: 24.hours.from_now) }.not_to change {
|
|
AdminNotice.problem.count
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#no_problem!" do
|
|
let(:next_run_at) { 24.hours.from_now.round(6) }
|
|
|
|
let(:problem_tracker) do
|
|
Fabricate(:problem_check_tracker, identifier: "twitter_login", **original_attributes)
|
|
end
|
|
|
|
let(:original_attributes) do
|
|
{
|
|
blips: 0,
|
|
last_problem_at: 1.week.ago,
|
|
last_success_at: Time.current,
|
|
last_run_at: 24.hours.ago,
|
|
next_run_at: nil,
|
|
}
|
|
end
|
|
|
|
let(:updated_attributes) { { blips: 0, next_run_at: } }
|
|
|
|
it do
|
|
freeze_time
|
|
|
|
expect { problem_tracker.no_problem!(next_run_at:) }.to change {
|
|
problem_tracker.attributes
|
|
}.to(hash_including(updated_attributes))
|
|
end
|
|
|
|
context "when there's an alarm sounding" do
|
|
before { problem_tracker.problem! }
|
|
|
|
it "silences the alarm" do
|
|
expect { problem_tracker.no_problem!(next_run_at: 24.hours.from_now) }.to change {
|
|
AdminNotice.problem.count
|
|
}.by(-1)
|
|
end
|
|
end
|
|
end
|
|
end
|