0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-15 14:06:35 +08:00
discourse/plugins/automation/spec/scripts/send_pms_spec.rb
Sam f9fce4712b
FEATURE: support target_user_ids for private messages (#37708)
Add `target_user_ids` as an alternative to `target_usernames` when
creating private messages via PostCreator and TopicCreator. The two
options are mutually exclusive and raise ArgumentError if both are
provided.

This avoids username-based lookups where the caller already has user
IDs, and prevents stale references when users rename after a pending
PM is created.

Key changes:
- PostCreator/TopicCreator: accept `target_user_ids`, validate
  mutual exclusivity with `target_usernames`, extract shared
  `add_users_from_scope` in TopicCreator
- Automation PendingPm: migrate from `sender`/`target_usernames`
  string columns to `sender_id`/`target_user_ids` integer columns
  with a backfill migration
- Scriptable::Utils.send_pm: resolve sender upfront, support
  integer sender param, store IDs in pending PMs
2026-02-12 09:21:40 +11:00

103 lines
3.3 KiB
Ruby
Vendored

# frozen_string_literal: true
describe "SendPms" do
fab!(:automation) do
Fabricate(:automation, script: DiscourseAutomation::Scripts::SEND_PMS, trigger: "stalled_wiki")
end
before do
SiteSetting.discourse_automation_enabled = true
automation.upsert_field!("sender", "user", { value: Discourse.system_user.username })
automation.upsert_field!(
"sendable_pms",
"pms",
{
value: [
{ title: "Hello {{receiver_username}}", raw: "Message for @{{receiver_username}}" },
],
},
)
end
context "with stalled_wiki trigger" do
fab!(:user) { Fabricate(:user, admin: true) }
fab!(:post) { Fabricate(:post, user: user) }
before do
automation.upsert_field!("stalled_after", "choices", { value: "PT1H" }, target: "trigger")
automation.upsert_field!("retriggered_after", "choices", { value: "PT1H" }, target: "trigger")
post.revise(user, { wiki: true }, { force_new_version: true, revised_at: 2.hours.ago })
end
it "sends PM with placeholders replaced" do
expect { Jobs::DiscourseAutomation::StalledWikiTracker.new.execute(nil) }.to change {
Topic.where(archetype: Archetype.private_message).count
}.by(1)
pm = Topic.last
expect(pm.title).to eq("Hello #{user.username}")
expect(pm.first_post.raw).to eq("Message for @#{user.username}")
end
end
context "with user_added_to_group trigger" do
fab!(:user)
fab!(:group)
before do
automation.update!(trigger: "user_added_to_group")
automation.upsert_field!("joined_group", "group", { value: group.id }, target: "trigger")
end
it "sends PM when user joins group" do
expect { group.add(user) }.to change {
Topic.where(archetype: Archetype.private_message).count
}.by(1)
pm = Topic.last
expect(pm.title).to eq("Hello #{user.username}")
expect(pm.allowed_users).to include(user, Discourse.system_user)
end
context "with custom sender" do
fab!(:sender) { Fabricate(:user, refresh_auto_groups: true) }
fab!(:user_2) { Fabricate(:user, refresh_auto_groups: true) }
before do
SiteSetting.unique_posts_mins = 1
automation.upsert_field!("sender", "user", { value: sender.username })
end
it "bypasses similarity validation for multiple recipients" do
expect { group.add(user) }.to change { Topic.count }.by(1)
expect { group.add(user_2) }.to change { Topic.count }.by(1)
end
end
end
context "with delay" do
fab!(:user)
before do
automation.update!(trigger: DiscourseAutomation::Triggers::RECURRING)
automation.upsert_field!("receiver", "user", { value: user.username })
automation.upsert_field!(
"sendable_pms",
"pms",
{ value: [{ title: "Delayed", raw: "Content", delay: 5 }] },
)
end
it "creates a pending PM instead of sending immediately" do
expect { automation.trigger! }.to change { DiscourseAutomation::PendingPm.count }.by(
1,
).and not_change { Topic.count }
pending_pm = DiscourseAutomation::PendingPm.last
expect(pending_pm.title).to eq("Delayed")
expect(pending_pm.sender_id).to eq(Discourse.system_user.id)
expect(pending_pm.target_user_ids).to eq([user.id])
expect(pending_pm.execute_at).to be_within(1.minute).of(5.minutes.from_now)
end
end
end