mirror of
https://github.com/discourse/discourse.git
synced 2026-03-04 01:15:08 +08:00
Bulk tag operations (change, append, remove) were directly manipulating tag records via DiscourseTagging and TopicTag, bypassing PostRevisor entirely. This meant callbacks, plugin hooks, and other side effects tied to the revision pipeline were not triggered. This refactors the three bulk tag actions to go through `first_post.revise` instead, using `silent: true` to avoid sending notifications and `skip_revision: true` to avoid creating revision records, preserving the existing quiet behavior. Also adds eager loading of first_post and tags to avoid N+1 queries, simplifies tag input handling by normalizing both tag_ids and tag_names into a single code path, and refactors `notification_is_disabled?` in PostActionNotifier for readability.
745 lines
23 KiB
Ruby
745 lines
23 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.day.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
|
|
post2 = create_post(topic_id: post1.topic_id)
|
|
create_post(topic_id: post1.topic_id)
|
|
|
|
PostDestroyer.new(Fabricate(:admin), post2).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, :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)
|
|
create_post(topic_id: post1.topic_id)
|
|
create_post(topic_id: post1.topic_id)
|
|
|
|
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!(:first_post) { Fabricate(:post, topic: topic) }
|
|
|
|
describe "silent option" do
|
|
fab!(:admin)
|
|
|
|
before do
|
|
Jobs.run_immediately!
|
|
PostActionNotifier.enable
|
|
SiteSetting.create_revision_on_bulk_topic_moves = true
|
|
TopicUser.change(
|
|
Fabricate(:user),
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
CategoryUser.set_notification_level_for_category(
|
|
Fabricate(:user),
|
|
CategoryUser.notification_levels[:watching_first_post],
|
|
category.id,
|
|
)
|
|
end
|
|
|
|
it "does not create any notifications when silent is true" do
|
|
expect do
|
|
TopicsBulkAction.new(
|
|
admin,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: category.id,
|
|
silent: true,
|
|
).perform!
|
|
end.to not_change { Notification.count }
|
|
end
|
|
|
|
it "creates notifications when silent is false" do
|
|
expect do
|
|
TopicsBulkAction.new(
|
|
admin,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: category.id,
|
|
).perform!
|
|
end.to change { Notification.count }
|
|
end
|
|
end
|
|
|
|
context "when the user can edit the topic" do
|
|
context "when create_revision_on_bulk_topic_moves is enabled" do
|
|
before { SiteSetting.create_revision_on_bulk_topic_moves = true }
|
|
|
|
it "changes category and creates revision" do
|
|
old_category_id = topic.category_id
|
|
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: category.id,
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(topic.reload.category).to eq(category)
|
|
|
|
revision = topic.first_post.revisions.last
|
|
expect(revision.modifications).to eq({ "category_id" => [old_category_id, category.id] })
|
|
end
|
|
end
|
|
|
|
context "when create_revision_on_bulk_topic_moves is disabled" do
|
|
before { SiteSetting.create_revision_on_bulk_topic_moves = false }
|
|
|
|
it "changes category without revision" do
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: category.id,
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(topic.reload.category).to eq(category)
|
|
expect(topic.first_post.revisions.last).to be_nil
|
|
end
|
|
end
|
|
|
|
it "does nothing when category stays the same" do
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: topic.category_id,
|
|
).perform!
|
|
|
|
expect(topic_ids).to be_empty
|
|
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)
|
|
original_category = topic.category
|
|
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_category",
|
|
category_id: category.id,
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
expect(topic.reload.category).to eq(original_category)
|
|
end
|
|
end
|
|
|
|
context "when destination category does not allow the topic's tags" do
|
|
fab!(:destination_category, :category)
|
|
fab!(:other_tag) { Fabricate(:tag, name: "other-tag") }
|
|
fab!(:restricted_tag) { Fabricate(:tag, name: "restricted-tag") }
|
|
fab!(:source_category) { Fabricate(:category, tags: [restricted_tag]) }
|
|
fab!(:admin)
|
|
fab!(:topic_with_tag) { Fabricate(:topic, category: source_category, tags: [restricted_tag]) }
|
|
fab!(:first_post_for_tagged_topic) { Fabricate(:post, topic: topic_with_tag) }
|
|
|
|
before { destination_category.update!(tags: [other_tag]) }
|
|
|
|
it "does not change category" do
|
|
original_category = topic_with_tag.category
|
|
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
admin,
|
|
[topic_with_tag.id],
|
|
type: "change_category",
|
|
category_id: destination_category.id,
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
expect(topic_with_tag.reload.category).to eq(original_category)
|
|
end
|
|
|
|
it "logs a warning with error details" do
|
|
Rails.logger.expects(:warn).with(includes("restricted-tag"))
|
|
|
|
TopicsBulkAction.new(
|
|
admin,
|
|
[topic_with_tag.id],
|
|
type: "change_category",
|
|
category_id: destination_category.id,
|
|
).perform!
|
|
end
|
|
|
|
it "exposes errors via attr_reader" do
|
|
operator =
|
|
TopicsBulkAction.new(
|
|
admin,
|
|
[topic_with_tag.id],
|
|
type: "change_category",
|
|
category_id: destination_category.id,
|
|
)
|
|
operator.perform!
|
|
|
|
expect(operator.errors).to be_present
|
|
expect(operator.errors.values.sum).to eq(1)
|
|
end
|
|
end
|
|
|
|
context "when destination category has the same tag group as source" do
|
|
fab!(:restricted_tag) { Fabricate(:tag, name: "restricted-tag") }
|
|
fab!(:tag_group) { Fabricate(:tag_group, tags: [restricted_tag]) }
|
|
fab!(:source_category) { Fabricate(:category, tag_groups: [tag_group]) }
|
|
fab!(:destination_category) { Fabricate(:category, tag_groups: [tag_group]) }
|
|
fab!(:admin)
|
|
fab!(:topic_with_tag) { Fabricate(:topic, category: source_category, tags: [restricted_tag]) }
|
|
fab!(:first_post_for_tagged_topic) { Fabricate(:post, topic: topic_with_tag) }
|
|
|
|
it "changes category successfully" do
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
admin,
|
|
[topic_with_tag.id],
|
|
type: "change_category",
|
|
category_id: destination_category.id,
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([topic_with_tag.id])
|
|
expect(topic_with_tag.reload.category).to eq(destination_category)
|
|
end
|
|
end
|
|
|
|
context "when destination category has allow_global_tags enabled" do
|
|
fab!(:global_tag) { Fabricate(:tag, name: "global-tag") }
|
|
fab!(:source_category, :category)
|
|
fab!(:destination_category) { Fabricate(:category, allow_global_tags: true) }
|
|
fab!(:admin)
|
|
fab!(:topic_with_tag) { Fabricate(:topic, category: source_category, tags: [global_tag]) }
|
|
fab!(:first_post_for_tagged_topic) { Fabricate(:post, topic: topic_with_tag) }
|
|
|
|
it "changes category successfully for unrestricted tags" do
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
admin,
|
|
[topic_with_tag.id],
|
|
type: "change_category",
|
|
category_id: destination_category.id,
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([topic_with_tag.id])
|
|
expect(topic_with_tag.reload.category).to eq(destination_category)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "destroy_post_timing" do
|
|
fab!(:first_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
|
|
it "updates the notification level when user can see topic" do
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_notification_level",
|
|
notification_level_id: 2,
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(TopicUser.get(topic, topic.user).notification_level).to eq(2)
|
|
end
|
|
|
|
it "doesn't change level when user can't see topic" do
|
|
Guardian.any_instance.expects(:can_see?).returns(false)
|
|
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_notification_level",
|
|
notification_level_id: 2,
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
expect(TopicUser.get(topic, topic.user)).to be_blank
|
|
end
|
|
|
|
["", nil, :missing].each do |invalid_value|
|
|
it "raises error when notification_level_id is #{invalid_value.inspect}" do
|
|
options = { type: "change_notification_level" }
|
|
options[:notification_level_id] = invalid_value unless invalid_value == :missing
|
|
|
|
expect do
|
|
TopicsBulkAction.new(topic.user, [topic.id], **options).perform!
|
|
end.to raise_error(Discourse::InvalidParameters, /notification_level_id/)
|
|
end
|
|
end
|
|
end
|
|
|
|
%w[close archive unlist].each do |action|
|
|
describe action do
|
|
it "#{action}s topic when user can moderate" do
|
|
Guardian.any_instance.expects(:can_moderate?).returns(true)
|
|
Guardian.any_instance.expects(:can_create?).returns(true)
|
|
|
|
topic_ids = TopicsBulkAction.new(topic.user, [topic.id], type: action).perform!
|
|
|
|
expect(topic_ids).to eq([topic.id])
|
|
topic.reload
|
|
case action
|
|
when "close"
|
|
expect(topic).to be_closed
|
|
when "archive"
|
|
expect(topic).to be_archived
|
|
when "unlist"
|
|
expect(topic).not_to be_visible
|
|
end
|
|
end
|
|
|
|
it "doesn't #{action} topic when user can't moderate" do
|
|
Guardian.any_instance.expects(:can_moderate?).returns(false)
|
|
|
|
topic_ids = TopicsBulkAction.new(topic.user, [topic.id], type: action).perform!
|
|
|
|
expect(topic_ids).to be_blank
|
|
topic.reload
|
|
case action
|
|
when "close"
|
|
expect(topic).not_to be_closed
|
|
when "archive"
|
|
expect(topic).not_to be_archived
|
|
when "unlist"
|
|
expect(topic).to be_visible
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "reset_bump_dates" do
|
|
it "resets bump date when user can update" 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)
|
|
|
|
topic_ids = TopicsBulkAction.new(topic.user, [topic.id], type: "reset_bump_dates").perform!
|
|
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(topic.reload.bumped_at).to eq_time(post_created_at)
|
|
end
|
|
|
|
it "doesn't reset bump date when user can't update" 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)
|
|
|
|
topic_ids = TopicsBulkAction.new(topic.user, [topic.id], type: "reset_bump_dates").perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
expect(topic.reload.bumped_at).to eq_time(bumped_at)
|
|
end
|
|
end
|
|
|
|
describe "change_tags" do
|
|
fab!(:first_post) { Fabricate(:post, topic:) }
|
|
fab!(:tag1, :tag)
|
|
fab!(:tag2, :tag)
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
|
|
topic.tags = [tag1, tag2]
|
|
end
|
|
|
|
context "when the user can edit the topic" do
|
|
fab!(:tag3, :tag)
|
|
|
|
it "changes tags to specified tags" do
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_tags",
|
|
tag_ids: [tag1.id, tag3.id],
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(topic.reload.tags).to contain_exactly(tag1, tag3)
|
|
end
|
|
|
|
it "removes all tags with empty array" do
|
|
topic_ids =
|
|
TopicsBulkAction.new(topic.user, [topic.id], type: "change_tags", tag_ids: []).perform!
|
|
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(topic.reload.tags).to be_empty
|
|
end
|
|
end
|
|
|
|
context "when tagging fails due to tag restrictions" do
|
|
fab!(:restricted_tag) { Fabricate(:tag, name: "restricted-tag") }
|
|
fab!(:tag_group) do
|
|
Fabricate(:tag_group, tags: [restricted_tag], permissions: { staff: :full })
|
|
end
|
|
|
|
it "does not include the topic in changed_ids and logs a warning" do
|
|
Rails.logger.expects(:warn).with(includes("restricted-tag"))
|
|
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_tags",
|
|
tag_ids: [restricted_tag.id],
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
end
|
|
end
|
|
|
|
context "when the user can't edit the topic" do
|
|
fab!(:tag3, :tag)
|
|
|
|
it "doesn't change the tags" do
|
|
Guardian.any_instance.expects(:can_edit?).returns(false)
|
|
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "change_tags",
|
|
tag_ids: [tag3.id],
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
expect(topic.reload.tags).to contain_exactly(tag1, tag2)
|
|
end
|
|
end
|
|
|
|
it "does not send notifications" do
|
|
fab_tag3 = Fabricate(:tag)
|
|
topic_watcher = Fabricate(:user)
|
|
Jobs.run_immediately!
|
|
TopicUser.change(
|
|
topic_watcher,
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
|
|
expect do
|
|
TopicsBulkAction.new(
|
|
Fabricate(:admin),
|
|
[topic.id],
|
|
type: "change_tags",
|
|
tag_ids: [fab_tag3.id],
|
|
).perform!
|
|
end.to not_change { Notification.where(user: topic_watcher).count }
|
|
end
|
|
end
|
|
|
|
describe "append_tags" do
|
|
fab!(:first_post) { Fabricate(:post, topic:) }
|
|
fab!(:tag1, :tag)
|
|
fab!(:tag2, :tag)
|
|
fab!(:tag3, :tag)
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
|
|
topic.tags = [tag1, tag2]
|
|
end
|
|
|
|
context "when the user can edit the topic" do
|
|
it "appends existing tags" do
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "append_tags",
|
|
tag_ids: [tag3.id],
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(topic.reload.tags).to contain_exactly(tag1, tag2, tag3)
|
|
end
|
|
|
|
it "is a no-op when appending empty array" do
|
|
topic_ids =
|
|
TopicsBulkAction.new(topic.user, [topic.id], type: "append_tags", tag_ids: []).perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
expect(topic.reload.tags).to contain_exactly(tag1, tag2)
|
|
end
|
|
end
|
|
|
|
context "when tagging fails due to tag restrictions" do
|
|
fab!(:restricted_tag) { Fabricate(:tag, name: "restricted-tag") }
|
|
fab!(:tag_group) do
|
|
Fabricate(:tag_group, tags: [restricted_tag], permissions: { staff: :full })
|
|
end
|
|
|
|
it "does not include the topic in changed_ids and logs a warning" do
|
|
Rails.logger.expects(:warn).with(includes("restricted-tag"))
|
|
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "append_tags",
|
|
tag_ids: [restricted_tag.id],
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
expect(topic.reload.tags).to contain_exactly(tag1, tag2)
|
|
end
|
|
end
|
|
|
|
context "when the user can't edit the topic" do
|
|
it "doesn't change the tags" do
|
|
Guardian.any_instance.expects(:can_edit?).returns(false)
|
|
|
|
topic_ids =
|
|
TopicsBulkAction.new(
|
|
topic.user,
|
|
[topic.id],
|
|
type: "append_tags",
|
|
tag_ids: [tag3.id],
|
|
).perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
expect(topic.reload.tags).to contain_exactly(tag1, tag2)
|
|
end
|
|
end
|
|
|
|
it "does not send notifications" do
|
|
topic_watcher = Fabricate(:user)
|
|
Jobs.run_immediately!
|
|
TopicUser.change(
|
|
topic_watcher,
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
|
|
expect do
|
|
TopicsBulkAction.new(
|
|
Fabricate(:admin),
|
|
[topic.id],
|
|
type: "append_tags",
|
|
tag_ids: [tag3.id],
|
|
).perform!
|
|
end.to not_change { Notification.where(user: topic_watcher).count }
|
|
end
|
|
end
|
|
|
|
describe "remove_tags" do
|
|
fab!(:first_post) { Fabricate(:post, topic:) }
|
|
fab!(:tag1, :tag)
|
|
fab!(:tag2, :tag)
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
|
|
topic.tags = [tag1, tag2]
|
|
end
|
|
|
|
context "when the user can edit the topic" do
|
|
it "removes all tags and updates tag counts" do
|
|
expect(tag1.reload.staff_topic_count).to eq(1)
|
|
|
|
topic_ids = TopicsBulkAction.new(topic.user, [topic.id], type: "remove_tags").perform!
|
|
|
|
expect(topic_ids).to eq([topic.id])
|
|
expect(topic.reload.tags).to be_empty
|
|
expect(tag1.reload.staff_topic_count).to eq(0)
|
|
end
|
|
end
|
|
|
|
context "when the user can't edit the topic" do
|
|
it "doesn't remove the tags" do
|
|
Guardian.any_instance.expects(:can_edit?).returns(false)
|
|
|
|
topic_ids = TopicsBulkAction.new(topic.user, [topic.id], type: "remove_tags").perform!
|
|
|
|
expect(topic_ids).to eq([])
|
|
expect(topic.reload.tags).to contain_exactly(tag1, tag2)
|
|
end
|
|
end
|
|
|
|
it "does not send notifications" do
|
|
topic_watcher = Fabricate(:user)
|
|
Jobs.run_immediately!
|
|
TopicUser.change(
|
|
topic_watcher,
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
|
|
expect do
|
|
TopicsBulkAction.new(Fabricate(:admin), [topic.id], type: "remove_tags").perform!
|
|
end.to not_change { Notification.where(user: topic_watcher).count }
|
|
end
|
|
end
|
|
end
|