discourse/spec/jobs/bulk_invite_spec.rb

174 lines
6.7 KiB
Ruby

# frozen_string_literal: true
RSpec.describe Jobs::BulkInvite do
describe "#execute" do
fab!(:user)
fab!(:admin)
fab!(:east_coast_user)
fab!(:group1) { Fabricate(:group, name: "group1") }
fab!(:group2) { Fabricate(:group, name: "group2") }
fab!(:topic)
let(:staged_user) { Fabricate(:user, staged: true, active: false) }
let(:email) { "test@discourse.org" }
let(:invites) do
[
{ email: user.email },
{ email: staged_user.email },
{ email: "test2@discourse.org" },
{ email: "test@discourse.org", groups: "GROUP1;group2", topic_id: topic.id },
{ email: "invalid" },
]
end
def parse_skipped_and_failed_emails(input)
skipped_invites_emails = input[/Skipped Invites for Emails?:\s+``` text\n(.+?)\n```/m, 1]
failed_invites_emails = input[/Failed Invites for Emails?:\s+``` text\n(.+?)\n```/m, 1]
{ skipped_invites: skipped_invites_emails, failed_invites: failed_invites_emails }
end
it "raises an error when the invites array is missing" do
expect { Jobs::BulkInvite.new.execute(current_user_id: user.id) }.to raise_error(
Discourse::InvalidParameters,
/invites/,
)
end
it "raises an error when current_user_id is not valid" do
expect { Jobs::BulkInvite.new.execute(invites: invites) }.to raise_error(
Discourse::InvalidParameters,
/current_user_id/,
)
end
it "creates the right invites" do
described_class.new.execute(current_user_id: admin.id, invites: invites)
expect(Invite.exists?(email: staged_user.email)).to eq(true)
expect(Invite.exists?(email: "test2@discourse.org")).to eq(true)
invite = Invite.last
expect(invite.email).to eq(email)
expect(invite.invited_groups.pluck(:group_id)).to contain_exactly(group1.id, group2.id)
expect(invite.topic_invites.pluck(:topic_id)).to contain_exactly(topic.id)
post = Post.last
expect(post.raw).to include("3 invites")
expect(post.raw).to include("1 skipped")
expect(post.raw).to include("0 warning")
expect(post.raw).to include("1 error")
end
it "handles daylight savings time correctly" do
# EDT (-04:00) transitions to EST (-05:00) on the first Sunday in November.
# Freeze time to the last Day of October, so that the creation and expiration date will be in different time zones.
Time.use_zone("Eastern Time (US & Canada)") do
freeze_time DateTime.parse("2023-10-31 06:00:00 -0400")
described_class.new.execute(current_user_id: east_coast_user.id, invites: invites)
invite = Invite.first
expect(invite.expires_at.hour).to equal(6)
end
end
it "does not create invited groups for automatic groups" do
group2.update!(automatic: true)
described_class.new.execute(current_user_id: admin.id, invites: invites)
invite = Invite.last
expect(invite.email).to eq(email)
expect(invite.invited_groups.pluck(:group_id)).to contain_exactly(group1.id)
post = Post.last
expect(post.raw).to include("1 warning")
end
it "does not create invited groups record if the user can not manage the group" do
group1.add_owner(user)
described_class.new.execute(current_user_id: user.id, invites: invites)
invite = Invite.last
expect(invite.email).to eq(email)
expect(invite.invited_groups.pluck(:group_id)).to contain_exactly(group1.id)
end
it "adds existing users to valid groups" do
existing_user = Fabricate(:user, email: "test@discourse.org")
group2.update!(automatic: true)
expect do
described_class.new.execute(current_user_id: admin.id, invites: invites)
end.to change { Invite.count }.by(2)
expect(Invite.exists?(email: staged_user.email)).to eq(true)
expect(Invite.exists?(email: "test2@discourse.org")).to eq(true)
expect(existing_user.reload.groups).to eq([group1])
end
it "can create staged users and prepopulate user fields" do
user_field = Fabricate(:user_field, name: "Location")
user_field_color = Fabricate(:user_field, field_type: "dropdown", name: "Color")
user_field_color.user_field_options.create!(value: "Red")
user_field_color.user_field_options.create!(value: "Green")
user_field_color.user_field_options.create!(value: "Blue")
described_class.new.execute(
current_user_id: admin.id,
invites: [
{ email: "test@discourse.org" }, # new user without user fields
{ email: user.email, location: "value 1", color: "blue" }, # existing user with user fields
{ email: staged_user.email, location: "value 2", color: "redd" }, # existing staged user with user fields
{ email: "test2@discourse.org", location: "value 3" }, # new staged user with user fields
],
)
expect(Invite.count).to eq(3)
expect(User.where(staged: true).find_by_email("test@discourse.org")).to eq(nil)
expect(user.user_fields[user_field.id.to_s]).to eq("value 1")
expect(user.user_fields[user_field_color.id.to_s]).to eq("Blue")
expect(staged_user.user_fields[user_field.id.to_s]).to eq("value 2")
expect(staged_user.user_fields[user_field_color.id.to_s]).to eq(nil)
new_staged_user = User.where(staged: true).find_by_email("test2@discourse.org")
expect(new_staged_user.user_fields[user_field.id.to_s]).to eq("value 3")
end
it "includes any skipped and failed emails in the private message" do
described_class.new.execute(
current_user_id: admin.id,
invites: [{ email: "bad_email" }, { email: user.email }, { email: "test@discourse.org" }],
)
post = Post.last
result = parse_skipped_and_failed_emails(post.raw)
expect(result[:skipped_invites]).to eq(user.email)
expect(result[:failed_invites]).to eq("bad_email")
end
context "when there are more than 200 invites" do
let(:bulk_invites) { [] }
before { 202.times { |i| bulk_invites << { email: "test_#{i}@discourse.org" } } }
it "rate limits email sending" do
described_class.new.execute(current_user_id: admin.id, invites: bulk_invites)
invite = Invite.last
expect(invite.email).to eq("test_201@discourse.org")
expect(invite.emailed_status).to eq(Invite.emailed_status_types[:bulk_pending])
expect(Jobs::ProcessBulkInviteEmails.jobs.size).to eq(1)
end
end
it "does not send an invite email when skip_email_bulk_invites is true" do
SiteSetting.skip_email_bulk_invites = true
described_class.new.execute(current_user_id: admin.id, invites: invites)
invite = Invite.last
expect(invite.emailed_status).to eq(Invite.emailed_status_types[:not_required])
end
end
end