0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-11 02:59:07 +08:00
discourse/plugins/discourse-assign/spec/requests/topics_bulk_assign_spec.rb
Régis Hanol 0f5ab6e940
FIX: Resolve groups when bulk assigning topics (#41867)
Previously, picking a group in the bulk assign modal always failed with
a bodyless 500: the client only sent `username`, `group_name` was not a
permitted bulk action parameter, and the bulk operation only ever
resolved a `User` — so `Assigner` received `nil`, treated it as a
`Group`, and crashed dereferencing it.

This change routes both the bulk and single-topic paths through one
`DiscourseAssign::AssigneeResolver`, makes `Assigner#assign` return a
reason instead of raising for caller-supplied input, and surfaces
per-topic refusals through `@errors` so a partially applied bulk assign
explains itself instead of reporting success.

Ref - t/188206
2026-07-21 16:18:58 +02:00

143 lines
4.6 KiB
Ruby
Vendored

# frozen_string_literal: true
require_relative "../support/assign_allowed_group"
RSpec.describe TopicsController do
include_context "with group that is allowed to assign"
fab!(:actor, :user)
fab!(:allowed_user, :user)
fab!(:topic1) { Fabricate(:post).topic }
fab!(:topic2) { Fabricate(:post).topic }
before do
SiteSetting.assign_enabled = true
SiteSetting.enable_assign_status = true
add_to_assign_allowed_group(actor)
sign_in(actor)
end
def bulk_assign(operation, ids: [topic1.id, topic2.id])
put "/topics/bulk.json", params: { topic_ids: ids, operation: operation }
end
describe "#bulk with the assign operation" do
it "assigns the selected topics to a group, carrying the note and the status" do
bulk_assign(
{
type: "assign",
group_name: assign_allowed_group.name,
note: "handle this",
status: "In Progress",
},
)
expect(response.status).to eq(200)
expect(response.parsed_body["topic_ids"]).to contain_exactly(topic1.id, topic2.id)
expect(topic1.reload.assignment).to have_attributes(
assigned_to: assign_allowed_group,
assigned_to_type: "Group",
note: "handle this",
status: "In Progress",
)
expect(topic2.reload.assignment.assigned_to).to eq(assign_allowed_group)
end
it "returns a 400 when the operation names no assignee" do
bulk_assign({ type: "assign" })
expect(response.status).to eq(400)
expect(Assignment.count).to eq(0)
end
it "returns a 404 when the group name matches no group" do
bulk_assign({ type: "assign", group_name: "no-such-group" })
expect(response.status).to eq(404)
end
it "returns a 422 when the status is not one of the configured statuses" do
bulk_assign({ type: "assign", group_name: assign_allowed_group.name, status: "Bogus" })
expect(response.status).to eq(422)
expect(Assignment.count).to eq(0)
end
it "returns a 403 without disclosing a group the acting user cannot see" do
hidden_group =
Fabricate(
:group,
visibility_level: Group.visibility_levels[:staff],
assignable_level: Group::ALIAS_LEVELS[:everyone],
)
bulk_assign({ type: "assign", group_name: hidden_group.name })
expect(response.status).to eq(403)
expect(response.body).not_to include(hidden_group.name)
expect(Assignment.count).to eq(0)
end
it "returns a 403 when the acting user is not allowed to assign" do
sign_in(Fabricate(:user))
bulk_assign({ type: "assign", group_name: assign_allowed_group.name })
expect(response.status).to eq(403)
expect(Assignment.count).to eq(0)
end
it "returns the per-topic failures alongside the topics that succeeded" do
add_to_assign_allowed_group(allowed_user)
private_group = Fabricate(:group, users: [actor])
hidden_topic = Fabricate(:topic, category: Fabricate(:private_category, group: private_group))
bulk_assign(
{ type: "assign", username: allowed_user.username },
ids: [hidden_topic.id, topic2.id],
)
expect(response.status).to eq(200)
expect(response.parsed_body["topic_ids"]).to contain_exactly(topic2.id)
expect(response.parsed_body["errors"]).to eq(
I18n.t(
"discourse_assign.forbidden_assignee_cant_see_topic",
username: allowed_user.username,
) =>
1,
)
end
it "reports the topics already assigned to the same assignee as succeeded" do
Assigner.new(topic1, actor).assign(assign_allowed_group)
bulk_assign({ type: "assign", group_name: assign_allowed_group.name })
expect(response.status).to eq(200)
expect(response.parsed_body["topic_ids"]).to contain_exactly(topic1.id, topic2.id)
expect(response.parsed_body["errors"]).to be_blank
end
it "keeps assigning the rest of the selection when a PM invite is refused" do
SiteSetting.invite_on_assign = true
pm = Fabricate(:private_message_topic, user: actor, recipient: allowed_user)
Fabricate(:post, topic: pm)
assign_allowed_group.add(Fabricate(:user))
bulk_assign(
{ type: "assign", group_name: assign_allowed_group.name },
ids: [topic1.id, pm.id],
)
expect(response.status).to eq(200)
expect(response.parsed_body["topic_ids"]).to contain_exactly(topic1.id)
expect(response.parsed_body["errors"]).to eq(
I18n.t(
"discourse_assign.forbidden_group_assignee_not_pm_participant",
group: assign_allowed_group.name,
) =>
1,
)
end
end
end