discourse/spec/models/group_user_spec.rb
Krzysztof Kotlarek d75a0b1165
FIX: Remove groups when promotion is recalculated. (#31058)
The group has `grant_trust_level` setting which automatically updates
the trust level when the user is added to the group.

Similarly, when the user is removed from the group, the trust level is
recalculated.

There was a bug that when the trust level was downgraded, the user was
not removed from automatic groups like for example `trust_level_3`.
2025-01-30 14:24:18 +11:00

336 lines
11 KiB
Ruby

# frozen_string_literal: true
RSpec.describe GroupUser do
fab!(:group)
fab!(:user)
describe "callbacks" do
it "increments and decrements `Group#user_count` when record is created and destroyed" do
group_user = GroupUser.new(user: user, group: group)
expect do group_user.save! end.to change { group.reload.user_count }.from(0).to(1)
expect do group_user.destroy! end.to change { group.reload.user_count }.from(1).to(0)
end
end
it "correctly sets notification level" do
moderator = Fabricate(:moderator)
gu = GroupUser.find_by(user_id: moderator.id, group_id: Group::AUTO_GROUPS[:moderators])
expect(gu.notification_level).to eq(NotificationLevels.all[:tracking])
group = Group.create!(name: "bob")
group.add(moderator)
group.save
gu = GroupUser.find_by(user_id: moderator.id, group_id: group.id)
expect(gu.notification_level).to eq(NotificationLevels.all[:watching])
group.remove(moderator)
group.save
group.default_notification_level = 1
group.save
group.add(moderator)
group.save
gu = GroupUser.find_by(user_id: moderator.id, group_id: group.id)
expect(gu.notification_level).to eq(NotificationLevels.all[:regular])
end
describe "default category notifications" do
fab!(:group)
fab!(:user)
fab!(:category1) { Fabricate(:category) }
fab!(:category2) { Fabricate(:category) }
fab!(:category3) { Fabricate(:category) }
fab!(:category4) { Fabricate(:category) }
fab!(:category5) { Fabricate(:category) }
def levels
CategoryUser.notification_levels
end
it "doesn't change anything with no configured defaults" do
expect { group.add(user) }.to_not change { CategoryUser.count }
end
it "adds new category notifications" do
group.muted_category_ids = [category1.id]
group.regular_category_ids = [category2.id]
group.tracking_category_ids = [category3.id]
group.watching_category_ids = [category4.id]
group.watching_first_post_category_ids = [category5.id]
group.save!
expect { group.add(user) }.to change { CategoryUser.count }.by(5)
h = CategoryUser.notification_levels_for(user)
expect(h[category1.id]).to eq(levels[:muted])
expect(h[category2.id]).to eq(levels[:regular])
expect(h[category3.id]).to eq(levels[:tracking])
expect(h[category4.id]).to eq(levels[:watching])
expect(h[category5.id]).to eq(levels[:watching_first_post])
end
it "only upgrades notifications" do
CategoryUser.create!(
user: user,
category_id: category1.id,
notification_level: levels[:muted],
)
CategoryUser.create!(
user: user,
category_id: category2.id,
notification_level: levels[:tracking],
)
CategoryUser.create!(
user: user,
category_id: category3.id,
notification_level: levels[:watching_first_post],
)
CategoryUser.create!(
user: user,
category_id: category4.id,
notification_level: levels[:watching],
)
group.regular_category_ids = [category1.id]
group.watching_first_post_category_ids = [category2.id, category3.id, category4.id]
group.save!
group.add(user)
h = CategoryUser.notification_levels_for(user)
expect(h[category1.id]).to eq(levels[:regular])
expect(h[category2.id]).to eq(levels[:watching_first_post])
expect(h[category3.id]).to eq(levels[:watching_first_post])
expect(h[category4.id]).to eq(levels[:watching])
end
it "merges notifications" do
CategoryUser.create!(
user: user,
category_id: category1.id,
notification_level: CategoryUser.notification_levels[:tracking],
)
CategoryUser.create!(
user: user,
category_id: category2.id,
notification_level: CategoryUser.notification_levels[:watching],
)
CategoryUser.create!(
user: user,
category_id: category4.id,
notification_level: CategoryUser.notification_levels[:watching_first_post],
)
group.muted_category_ids = [category3.id]
group.tracking_category_ids = [category4.id]
group.save!
group.add(user)
h = CategoryUser.notification_levels_for(user)
expect(h[category1.id]).to eq(levels[:tracking])
expect(h[category2.id]).to eq(levels[:watching])
expect(h[category3.id]).to eq(levels[:muted])
expect(h[category4.id]).to eq(levels[:watching_first_post])
end
end
describe "default tag notifications" do
fab!(:group)
fab!(:user)
fab!(:tag1) { Fabricate(:tag) }
fab!(:tag2) { Fabricate(:tag) }
fab!(:tag3) { Fabricate(:tag) }
fab!(:tag4) { Fabricate(:tag) }
fab!(:tag5) { Fabricate(:tag) }
fab!(:synonym1) { Fabricate(:tag, target_tag: tag1) }
def levels
TagUser.notification_levels
end
it "doesn't change anything with no configured defaults" do
expect { group.add(user) }.to_not change { TagUser.count }
end
it "adds new tag notifications" do
group.muted_tags = [synonym1.name]
group.regular_tags = [tag2.name]
group.tracking_tags = [tag3.name]
group.watching_tags = [tag4.name]
group.watching_first_post_tags = [tag5.name]
group.save!
expect { group.add(user) }.to change { TagUser.count }.by(5)
expect(TagUser.lookup(user, :muted).pluck(:tag_id)).to eq([tag1.id])
expect(TagUser.lookup(user, :regular).pluck(:tag_id)).to eq([tag2.id])
expect(TagUser.lookup(user, :tracking).pluck(:tag_id)).to eq([tag3.id])
expect(TagUser.lookup(user, :watching).pluck(:tag_id)).to eq([tag4.id])
expect(TagUser.lookup(user, :watching_first_post).pluck(:tag_id)).to eq([tag5.id])
end
it "only upgrades notifications" do
TagUser.create!(user: user, tag_id: tag1.id, notification_level: levels[:muted])
TagUser.create!(user: user, tag_id: tag2.id, notification_level: levels[:tracking])
TagUser.create!(user: user, tag_id: tag3.id, notification_level: levels[:watching_first_post])
TagUser.create!(user: user, tag_id: tag4.id, notification_level: levels[:watching])
group.regular_tags = [tag1.name]
group.watching_first_post_tags = [tag2.name, tag3.name, tag4.name]
group.save!
group.add(user)
expect(TagUser.lookup(user, :muted).pluck(:tag_id)).to be_empty
expect(TagUser.lookup(user, :regular).pluck(:tag_id)).to eq([tag1.id])
expect(TagUser.lookup(user, :tracking).pluck(:tag_id)).to be_empty
expect(TagUser.lookup(user, :watching).pluck(:tag_id)).to eq([tag4.id])
expect(TagUser.lookup(user, :watching_first_post).pluck(:tag_id)).to contain_exactly(
tag2.id,
tag3.id,
)
end
it "merges notifications" do
TagUser.create!(user: user, tag_id: tag1.id, notification_level: levels[:tracking])
TagUser.create!(user: user, tag_id: tag2.id, notification_level: levels[:watching])
TagUser.create!(user: user, tag_id: tag4.id, notification_level: levels[:watching_first_post])
group.muted_tags = [tag3.name]
group.tracking_tags = [tag2.name]
group.save!
group.add(user)
expect(TagUser.lookup(user, :muted).pluck(:tag_id)).to eq([tag3.id])
expect(TagUser.lookup(user, :tracking).pluck(:tag_id)).to eq([tag1.id])
expect(TagUser.lookup(user, :watching).pluck(:tag_id)).to eq([tag2.id])
expect(TagUser.lookup(user, :watching_first_post).pluck(:tag_id)).to eq([tag4.id])
end
end
describe "#ensure_consistency!" do
fab!(:group)
fab!(:group_2) { Fabricate(:group) }
fab!(:pm_post) { Fabricate(:private_message_post) }
fab!(:pm_topic) { pm_post.topic.tap { |t| t.allowed_groups << group } }
fab!(:user) do
Fabricate(:user, last_seen_at: Time.zone.now).tap do |u|
group.add(u)
group_2.add(u)
TopicUser.change(
u.id,
pm_topic.id,
notification_level: TopicUser.notification_levels[:tracking],
last_read_post_number: pm_post.post_number,
)
end
end
# User that is not tracking topic
fab!(:user_2) do
Fabricate(:user, last_seen_at: Time.zone.now).tap do |u|
group.add(u)
TopicUser.change(
u.id,
pm_topic.id,
notification_level: TopicUser.notification_levels[:regular],
last_read_post_number: pm_post.post_number,
)
end
end
# User that has not been seen
fab!(:user_3) do
Fabricate(:user).tap do |u|
group.add(u)
TopicUser.change(
u.id,
pm_topic.id,
notification_level: TopicUser.notification_levels[:tracking],
last_read_post_number: pm_post.post_number,
)
end
end
it "updates first unread pm timestamp correctly" do
freeze_time 10.minutes.from_now
post = create_post(user: pm_topic.user, topic_id: pm_topic.id)
expect { GroupUser.ensure_consistency! }.to_not change {
group.group_users.find_by(user_id: user_3.id).first_unread_pm_at
}
expect(post.topic.updated_at).to_not eq_time(10.minutes.ago)
expect(group.group_users.find_by(user_id: user.id).first_unread_pm_at).to eq_time(
post.topic.updated_at,
)
expect(group_2.group_users.find_by(user_id: user.id).first_unread_pm_at).to eq_time(
10.minutes.ago,
)
expect(group.group_users.find_by(user_id: user_2.id).first_unread_pm_at).to eq_time(
10.minutes.ago,
)
end
end
describe "#destroy!" do
fab!(:group)
it "removes `primary_group_id`, `flair_group_id` and exec `match_primary_group_changes` method on user model" do
user = Fabricate(:user, primary_group: group, flair_group: group)
group_user = Fabricate(:group_user, group: group, user: user)
user.expects(:match_primary_group_changes).once
group_user.destroy!
user.reload
expect(user.primary_group_id).to be_nil
expect(user.flair_group_id).to be_nil
end
it "restores previous trust level" do
user = Fabricate(:user)
expect(user.trust_level).to eq(1)
user.change_trust_level!(1, log_action_for: Discourse.system_user)
user.change_trust_level!(2, log_action_for: Discourse.system_user)
group.update!(grant_trust_level: 4)
group_user = Fabricate(:group_user, group: group, user: user)
expect(user.reload.trust_level).to eq(4)
expect(user.groups.where(automatic: true).map(&:name)).to eq(
%w[trust_level_0 trust_level_1 trust_level_2 trust_level_3 trust_level_4],
)
group_user.destroy!
# keep in mind that we do not restore tl3, cause reqs can be lost
expect(user.reload.trust_level).to eq(2)
expect(user.groups.where(automatic: true).map(&:name)).to eq(
%w[trust_level_0 trust_level_1 trust_level_2],
)
end
it "protects user trust level if all requirements are met" do
Promotion.stubs(:tl2_met?).returns(true)
user = Fabricate(:user)
expect(user.trust_level).to eq(1)
group.update!(grant_trust_level: 1)
Promotion.recalculate(user)
expect(user.reload.trust_level).to eq(2)
group_user = Fabricate(:group_user, group: group, user: user)
expect_not_enqueued_with(
job: :send_system_message,
args: {
user_id: user.id,
message_type: "tl2_promotion_message",
},
) { group_user.destroy! }
expect(user.reload.trust_level).to eq(2)
end
end
end