mirror of
https://github.com/discourse/discourse.git
synced 2025-05-19 02:33:56 +08:00

When bulk removing tags, the tag topic count isn't updated. You have to wait for the daily "ensure consistency" job to run for it to update properly. This is causing downstream problems, like the inability to use the "remove unused tags" feature. The counter updating is handled on destroy by the join table model TopicTag. But we are calling #delete_all to perform the bulk tag removal, which bypasses callbacks. This changes from #delete_all to #destroy_all, which invokes callbacks and updates the counters.
588 lines
19 KiB
Ruby
588 lines
19 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe TopicsBulkAction do
|
|
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
|
fab!(:topic) { Fabricate(:topic, user: user) }
|
|
|
|
describe "#dismiss_topics" do
|
|
fab!(:user) { Fabricate(:user, created_at: 1.days.ago, refresh_auto_groups: true) }
|
|
fab!(:category)
|
|
fab!(:topic2) { Fabricate(:topic, category: category, created_at: 60.minutes.ago) }
|
|
fab!(:topic3) { Fabricate(:topic, category: category, created_at: 120.minutes.ago) }
|
|
|
|
before { topic.destroy! }
|
|
|
|
it "dismisses private messages" do
|
|
pm = Fabricate(:private_message_topic, recipient: user)
|
|
|
|
TopicsBulkAction.new(user, [pm.id], type: "dismiss_topics").perform!
|
|
|
|
expect(DismissedTopicUser.exists?(topic: pm)).to eq(true)
|
|
end
|
|
|
|
it "dismisses two topics" do
|
|
expect {
|
|
TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
|
|
}.to change { DismissedTopicUser.count }.by(2)
|
|
end
|
|
|
|
it "returns dismissed topic ids" do
|
|
expect(
|
|
TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!.sort,
|
|
).to match_array([topic2.id, topic3.id])
|
|
end
|
|
|
|
it "respects max_new_topics limit" do
|
|
SiteSetting.max_new_topics = 1
|
|
expect do
|
|
TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
|
|
end.to change { DismissedTopicUser.count }.by(1)
|
|
|
|
dismissed_topic_user = DismissedTopicUser.last
|
|
|
|
expect(dismissed_topic_user.user_id).to eq(user.id)
|
|
expect(dismissed_topic_user.topic_id).to eq(topic2.id)
|
|
expect(dismissed_topic_user.created_at).not_to be_nil
|
|
end
|
|
|
|
it "respects seen topics" do
|
|
Fabricate(:topic_user, user: user, topic: topic2, last_read_post_number: 1)
|
|
Fabricate(:topic_user, user: user, topic: topic3, last_read_post_number: 1)
|
|
expect do
|
|
TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
|
|
end.not_to change { DismissedTopicUser.count }
|
|
end
|
|
|
|
it "dismisses when topic user without last_read_post_number" do
|
|
Fabricate(:topic_user, user: user, topic: topic2, last_read_post_number: nil)
|
|
Fabricate(:topic_user, user: user, topic: topic3, last_read_post_number: nil)
|
|
expect do
|
|
TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
|
|
end.to change { DismissedTopicUser.count }.by(2)
|
|
end
|
|
|
|
it "respects new_topic_duration_minutes" do
|
|
user.user_option.update!(new_topic_duration_minutes: 70)
|
|
|
|
expect do
|
|
TopicsBulkAction.new(user, [Topic.all.pluck(:id)], type: "dismiss_topics").perform!
|
|
end.to change { DismissedTopicUser.count }.by(1)
|
|
|
|
dismissed_topic_user = DismissedTopicUser.last
|
|
|
|
expect(dismissed_topic_user.user_id).to eq(user.id)
|
|
expect(dismissed_topic_user.topic_id).to eq(topic2.id)
|
|
expect(dismissed_topic_user.created_at).not_to be_nil
|
|
end
|
|
|
|
it "doesn't dismiss topics the user can't see" do
|
|
group = Fabricate(:group)
|
|
private_category = Fabricate(:private_category, group: group)
|
|
topic2.update!(category_id: private_category.id)
|
|
|
|
expect do
|
|
TopicsBulkAction.new(user, [topic2.id, topic3.id], type: "dismiss_topics").perform!
|
|
end.to change { DismissedTopicUser.count }.by(1)
|
|
|
|
expect(DismissedTopicUser.where(user_id: user.id).pluck(:topic_id)).to eq([topic3.id])
|
|
|
|
group.add(user)
|
|
|
|
expect do
|
|
TopicsBulkAction.new(user, [topic2.id, topic3.id], type: "dismiss_topics").perform!
|
|
end.to change { DismissedTopicUser.count }.by(1)
|
|
|
|
expect(DismissedTopicUser.where(user_id: user.id).pluck(:topic_id)).to contain_exactly(
|
|
topic2.id,
|
|
topic3.id,
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "dismiss_posts" do
|
|
it "dismisses posts" do
|
|
post1 = create_post
|
|
p = create_post(topic_id: post1.topic_id)
|
|
create_post(topic_id: post1.topic_id)
|
|
|
|
PostDestroyer.new(Fabricate(:admin), p).destroy
|
|
|
|
TopicTrackingState.expects(:publish_dismiss_new_posts).with(
|
|
post1.user_id,
|
|
topic_ids: [post1.topic_id],
|
|
)
|
|
|
|
TopicsBulkAction.new(post1.user, [post1.topic_id], type: "dismiss_posts").perform!
|
|
|
|
tu = TopicUser.find_by(user_id: post1.user_id, topic_id: post1.topic_id)
|
|
|
|
expect(tu.last_read_post_number).to eq(3)
|
|
end
|
|
|
|
context "when the user is staff" do
|
|
fab!(:user) { Fabricate(:admin) }
|
|
|
|
context "when the highest_staff_post_number is > highest_post_number for a topic (e.g. whisper is last post)" do
|
|
it "dismisses posts" do
|
|
SiteSetting.whispers_allowed_groups = "#{Group::AUTO_GROUPS[:staff]}"
|
|
post1 = create_post(user: user)
|
|
p = create_post(topic_id: post1.topic_id)
|
|
create_post(topic_id: post1.topic_id)
|
|
|
|
whisper =
|
|
PostCreator.new(
|
|
user,
|
|
topic_id: post1.topic.id,
|
|
post_type: Post.types[:whisper],
|
|
raw: "this is a whispered reply",
|
|
).create
|
|
|
|
TopicsBulkAction.new(user, [post1.topic_id], type: "dismiss_posts").perform!
|
|
|
|
tu = TopicUser.find_by(user_id: user.id, topic_id: post1.topic_id)
|
|
|
|
expect(tu.last_read_post_number).to eq(4)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "invalid operation" do
|
|
let(:user) { Fabricate.build(:user) }
|
|
|
|
it "raises an error with an invalid operation" do
|
|
tba = TopicsBulkAction.new(user, [1], type: "rm_root")
|
|
expect { tba.perform! }.to raise_error(Discourse::InvalidParameters)
|
|
end
|
|
end
|
|
|
|
describe "change_category" do
|
|
fab!(:category)
|
|
fab!(:fist_post) { Fabricate(:post, topic: topic) }
|
|
|
|
context "when the user can edit the topic" do
|
|
context "with 'create_revision_on_bulk_topic_moves' setting enabled" do
|
|
before { SiteSetting.create_revision_on_bulk_topic_moves = true }
|
|
|
|
it "changes the category, creates a post revision and returns the topic_id" do
|
|
old_category_id = topic.category_id
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: category.id,
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic.category).to eq(category)
|
|
|
|
revision = topic.first_post.revisions.last
|
|
expect(revision).to be_present
|
|
expect(revision.modifications).to eq({ "category_id" => [old_category_id, category.id] })
|
|
end
|
|
|
|
it "doesn't do anything when category stays the same" do
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: topic.category_id,
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to be_empty
|
|
|
|
topic.reload
|
|
revision = topic.first_post.revisions.last
|
|
expect(revision).to be_nil
|
|
end
|
|
end
|
|
|
|
context "with 'create_revision_on_bulk_topic_moves' setting disabled" do
|
|
before { SiteSetting.create_revision_on_bulk_topic_moves = false }
|
|
|
|
it "changes the category, doesn't create a post revision and returns the topic_id" do
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: category.id,
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic.category).to eq(category)
|
|
|
|
revision = topic.first_post.revisions.last
|
|
expect(revision).to be_nil
|
|
end
|
|
|
|
it "doesn't do anything when category stays the same" do
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: topic.category_id,
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to be_empty
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when the user can't edit the topic" do
|
|
it "doesn't change the category" do
|
|
Guardian.any_instance.expects(:can_edit?).returns(false)
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: category.id,
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([])
|
|
topic.reload
|
|
expect(topic.category).not_to eq(category)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "destroy_post_timing" do
|
|
fab!(:fist_post) { Fabricate(:post, topic: topic) }
|
|
|
|
before { PostTiming.process_timings(topic.user, topic.id, 10, [[1, 10]]) }
|
|
|
|
it "delegates to PostTiming.destroy_for" do
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "destroy_post_timing")
|
|
topic_ids = nil
|
|
expect { topic_ids = tba.perform! }.to change { PostTiming.count }.by(-1)
|
|
expect(topic_ids).to contain_exactly(topic.id)
|
|
end
|
|
end
|
|
|
|
describe "delete" do
|
|
fab!(:topic) { Fabricate(:post).topic }
|
|
fab!(:moderator)
|
|
|
|
it "deletes the topic" do
|
|
tba = TopicsBulkAction.new(moderator, [topic.id], type: "delete")
|
|
tba.perform!
|
|
topic.reload
|
|
expect(topic).to be_trashed
|
|
end
|
|
end
|
|
|
|
describe "change_notification_level" do
|
|
context "when the user can see the topic" do
|
|
it "updates the notification level" do
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_notification_level",
|
|
notification_level_id: 2,
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(TopicUser.get(topic, topic.user).notification_level).to eq(2)
|
|
end
|
|
end
|
|
|
|
context "when the user can't see the topic" do
|
|
it "doesn't change the level" do
|
|
Guardian.any_instance.expects(:can_see?).returns(false)
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_notification_level",
|
|
notification_level_id: 2,
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([])
|
|
expect(TopicUser.get(topic, topic.user)).to be_blank
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "close" do
|
|
context "when the user can moderate the topic" do
|
|
it "closes the topic and returns the topic_id" do
|
|
Guardian.any_instance.expects(:can_moderate?).returns(true)
|
|
Guardian.any_instance.expects(:can_create?).returns(true)
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "close")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic).to be_closed
|
|
end
|
|
end
|
|
|
|
context "when the user can't edit the topic" do
|
|
it "doesn't close the topic" do
|
|
Guardian.any_instance.expects(:can_moderate?).returns(false)
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "close")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to be_blank
|
|
topic.reload
|
|
expect(topic).not_to be_closed
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "archive" do
|
|
context "when the user can moderate the topic" do
|
|
it "archives the topic and returns the topic_id" do
|
|
Guardian.any_instance.expects(:can_moderate?).returns(true)
|
|
Guardian.any_instance.expects(:can_create?).returns(true)
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "archive")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic).to be_archived
|
|
end
|
|
end
|
|
|
|
context "when the user can't edit the topic" do
|
|
it "doesn't archive the topic" do
|
|
Guardian.any_instance.expects(:can_moderate?).returns(false)
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "archive")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to be_blank
|
|
topic.reload
|
|
expect(topic).not_to be_archived
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "unlist" do
|
|
context "when the user can moderate the topic" do
|
|
it "unlists the topic and returns the topic_id" do
|
|
Guardian.any_instance.expects(:can_moderate?).returns(true)
|
|
Guardian.any_instance.expects(:can_create?).returns(true)
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "unlist")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic).not_to be_visible
|
|
end
|
|
end
|
|
|
|
context "when the user can't edit the topic" do
|
|
it "doesn't unlist the topic" do
|
|
Guardian.any_instance.expects(:can_moderate?).returns(false)
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "unlist")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to be_blank
|
|
topic.reload
|
|
expect(topic).to be_visible
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "reset_bump_dates" do
|
|
context "when the user can update bumped at" do
|
|
it "does reset the topic bump date" do
|
|
post_created_at = 1.day.ago
|
|
create_post(topic_id: topic.id, created_at: post_created_at)
|
|
topic.update!(bumped_at: 1.hour.ago)
|
|
Guardian.any_instance.expects(:can_update_bumped_at?).returns(true)
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "reset_bump_dates")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(topic.reload.bumped_at).to eq_time(post_created_at)
|
|
end
|
|
end
|
|
|
|
context "when the user can't update bumped at" do
|
|
it "doesn't reset the topic bump date" do
|
|
create_post(topic_id: topic.id, created_at: 1.day.ago)
|
|
bumped_at = 1.hour.ago
|
|
topic.update!(bumped_at: bumped_at)
|
|
Guardian.any_instance.expects(:can_update_bumped_at?).returns(false)
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "reset_bump_dates")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([])
|
|
expect(topic.reload.bumped_at).to eq_time(bumped_at)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "change_tags" do
|
|
fab!(:tag1) { Fabricate(:tag) }
|
|
fab!(:tag2) { Fabricate(:tag) }
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
|
|
topic.tags = [tag1, tag2]
|
|
end
|
|
|
|
it "can change the tags, and can create new tags" do
|
|
SiteSetting.create_tag_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_tags",
|
|
tags: ["newtag", tag1.name],
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic.tags.map(&:name)).to contain_exactly("newtag", tag1.name)
|
|
end
|
|
|
|
it "can change the tags but not create new ones" do
|
|
SiteSetting.create_tag_allowed_groups = Group::AUTO_GROUPS[:trust_level_4]
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_tags",
|
|
tags: ["newtag", tag1.name],
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic.tags.map(&:name)).to contain_exactly(tag1.name)
|
|
end
|
|
|
|
it "can remove all tags" do
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "change_tags", tags: [])
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic.tags.size).to eq(0)
|
|
end
|
|
|
|
context "when user can't edit topic" do
|
|
before { Guardian.any_instance.expects(:can_edit?).returns(false) }
|
|
|
|
it "doesn't change the tags" do
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_tags",
|
|
tags: ["newtag", tag1.name],
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([])
|
|
topic.reload
|
|
expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "append tags" do
|
|
fab!(:tag1) { Fabricate(:tag) }
|
|
fab!(:tag2) { Fabricate(:tag) }
|
|
fab!(:tag3) { Fabricate(:tag) }
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
|
|
topic.tags = [tag1, tag2]
|
|
end
|
|
|
|
it "can append new or existing tags" do
|
|
SiteSetting.create_tag_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "append_tags",
|
|
tags: [tag1.name, tag3.name, "newtag"],
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name, tag3.name, "newtag")
|
|
end
|
|
|
|
it "can append empty tags" do
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "append_tags", tags: [])
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name)
|
|
end
|
|
|
|
context "when the user can't create new topics" do
|
|
before { SiteSetting.create_tag_allowed_groups = Group::AUTO_GROUPS[:trust_level_4] }
|
|
|
|
it "can append existing tags but doesn't append new tags" do
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "append_tags",
|
|
tags: [tag3.name, "newtag"],
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name, tag3.name)
|
|
end
|
|
end
|
|
|
|
context "when user can't edit topic" do
|
|
before { Guardian.any_instance.expects(:can_edit?).returns(false) }
|
|
|
|
it "doesn't change the tags" do
|
|
tba =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "append_tags",
|
|
tags: ["newtag", tag3.name],
|
|
)
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([])
|
|
topic.reload
|
|
expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "remove_tags" do
|
|
fab!(:tag1) { Fabricate(:tag) }
|
|
fab!(:tag2) { Fabricate(:tag) }
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
|
|
TopicTag.create!(topic: topic, tag: tag1)
|
|
TopicTag.create!(topic: topic, tag: tag2)
|
|
end
|
|
|
|
it "can remove all tags" do
|
|
expect(tag1.reload.staff_topic_count).to eq(1)
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "remove_tags")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
expect(topic.tags.size).to eq(0)
|
|
expect(tag1.reload.staff_topic_count).to eq(0)
|
|
end
|
|
|
|
context "when user can't edit topic" do
|
|
before { Guardian.any_instance.expects(:can_edit?).returns(false) }
|
|
|
|
it "doesn't remove the tags" do
|
|
tba = TopicsBulkAction.new(topic.user, [topic.id], type: "remove_tags")
|
|
topic_ids = tba.perform!
|
|
expect(topic_ids).to eq([])
|
|
topic.reload
|
|
expect(topic.tags.map(&:name)).to contain_exactly(tag1.name, tag2.name)
|
|
end
|
|
end
|
|
end
|
|
end
|