0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-08 17:53:55 +08:00
discourse/spec/services/upcoming_changes/action/track_status_changes_spec.rb
Martin Brennan 02edc8a05b
FEATURE: Change upcoming change available notification cadence (#40157)
Since we have been using the upcoming change system
more heavily, we noticed that notifications for available changes
(i.e. changes that have been added at promotion status - 1 OR
changes that recently reached promotion status - 1) are crowding
the admin's notifications a lot.

To address this, we already allowed admins to opt out of these
in 0c46a2e805 . However, we also
want to reduce the cadence of these notifications for admins who do want
to opt out completely.

This commit changes upcoming change available notifications to be
collected & sent in a weekly job, rather than in the job that runs
every 20 minutes & logs added/status changed events.

This weekly job will also handle consolidating existing unread upcoming
change available notifications, and making sure that we do not send
N new notifications per change when an admin has no existing unread
notification.

Upcoming change promoted notifications will still happen "instantly"
2026-05-25 09:38:33 +10:00

239 lines
7.3 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe UpcomingChanges::Action::TrackStatusChanges do
let(:enable_upload_debug_mode_status) { :experimental }
let(:show_user_menu_avatars_status) { :beta }
before do
mock_upcoming_change_metadata(
{
enable_upload_debug_mode: {
impact: "other,developers",
status: enable_upload_debug_mode_status,
impact_type: "other",
impact_role: "developers",
},
show_user_menu_avatars: {
impact: "feature,all_members",
status: show_user_menu_avatars_status,
impact_type: "feature",
impact_role: "all_members",
},
},
)
end
def scoped_events
UpcomingChangeEvent.where(
upcoming_change_name: %i[enable_upload_debug_mode show_user_menu_avatars],
)
end
fab!(:admin_1, :admin)
fab!(:admin_2, :admin)
let(:added_changes) { [] }
let(:removed_changes) { [] }
describe ".call" do
subject(:result) do
described_class.call(all_admins: [admin_1, admin_2], added_changes:, removed_changes:)
end
before do
scoped_events.where(event_type: :status_changed).delete_all
scoped_events.where(event_type: :added).delete_all
UpcomingChangeEvent.create!(
event_type: :added,
upcoming_change_name: :enable_upload_debug_mode,
)
UpcomingChangeEvent.create!(event_type: :added, upcoming_change_name: :show_user_menu_avatars)
end
context "when there are no previous status changes" do
it "creates a status_changed event for the current status" do
expect { result }.to change { scoped_events.where(event_type: :status_changed).count }.by(2)
end
it "sets previous_value to nil in the event data" do
result
event =
scoped_events.find_by(
event_type: :status_changed,
upcoming_change_name: :enable_upload_debug_mode,
)
expect(event.event_data["previous_value"]).to be_nil
end
it "sets new_value to the current status in the event data" do
result
event =
scoped_events.find_by(
event_type: :status_changed,
upcoming_change_name: :enable_upload_debug_mode,
)
expect(event.event_data["new_value"]).to eq("experimental")
end
it "returns N/A as previous_value in the result" do
expect(result[:enable_upload_debug_mode]).to eq(
{ previous_value: "N/A", new_value: :experimental },
)
end
end
it "clears the latest new feature created_at cache" do
Discourse.redis.set("latest_new_feature_created_at", Time.zone.now.iso8601)
result
expect(Discourse.redis.get("latest_new_feature_created_at")).to be_nil
end
context "when there are added changes in the same run" do
let(:added_changes) { [:enable_upload_debug_mode] }
before do
scoped_events.where(event_type: :added).delete_all
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: :enable_upload_debug_mode,
event_data: {
"previous_value" => nil,
"new_value" => "alpha",
},
)
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: :show_user_menu_avatars,
event_data: {
"previous_value" => nil,
"new_value" => "beta",
},
)
end
it "does not create additional status change events for added changes" do
expect { result }.not_to change {
scoped_events.where(
event_type: :status_changed,
upcoming_change_name: :enable_upload_debug_mode,
).count
}
end
end
context "when there are removed changes in the same run" do
let(:removed_changes) { [:old_removed_change] }
before do
UpcomingChangeEvent.create!(event_type: :added, upcoming_change_name: :old_removed_change)
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: :old_removed_change,
event_data: {
"previous_value" => nil,
"new_value" => "beta",
},
)
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: :enable_upload_debug_mode,
event_data: {
"previous_value" => nil,
"new_value" => "experimental",
},
)
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: :show_user_menu_avatars,
event_data: {
"previous_value" => nil,
"new_value" => "beta",
},
)
end
it "does not create a status change event for removed changes" do
expect { result }.not_to change {
UpcomingChangeEvent.where(
event_type: :status_changed,
upcoming_change_name: :old_removed_change,
).count
}
end
end
context "when the status has changed from a previous value" do
let(:show_user_menu_avatars_status) { :stable }
before do
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: :show_user_menu_avatars,
event_data: {
"previous_value" => nil,
"new_value" => "beta",
},
)
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: :enable_upload_debug_mode,
event_data: {
"previous_value" => nil,
"new_value" => "experimental",
},
)
end
it "creates a status_changed event with correct data" do
expect { result }.to change {
scoped_events.where(
event_type: :status_changed,
upcoming_change_name: :show_user_menu_avatars,
).count
}.by(1)
end
it "records the previous and new status values" do
result
expect(
scoped_events
.where(event_type: :status_changed, upcoming_change_name: :show_user_menu_avatars)
.order(:created_at)
.last,
).to have_attributes(event_data: { "previous_value" => "beta", "new_value" => "stable" })
end
it "returns the status change in the result" do
expect(result[:show_user_menu_avatars]).to eq(
{ previous_value: "beta", new_value: :stable },
)
end
end
context "when status has not changed" do
let(:show_user_menu_avatars_status) { :beta }
before do
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: :show_user_menu_avatars,
event_data: {
"previous_value" => nil,
"new_value" => "beta",
},
)
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: :enable_upload_debug_mode,
event_data: {
"previous_value" => nil,
"new_value" => "experimental",
},
)
end
it "does not create a new status_changed event" do
expect { result }.not_to change { scoped_events.where(event_type: :status_changed).count }
end
end
end
end