2862 lines
102 KiB
Ruby
2862 lines
102 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec::Matchers.define :add_notification do |user, notification_type|
|
|
match(notify_expectation_failures: true) do |actual|
|
|
notifications = user.notifications
|
|
before = notifications.count
|
|
|
|
actual.call
|
|
|
|
expect(notifications.count).to eq(before + 1),
|
|
"expected 1 new notification, got #{notifications.count - before}"
|
|
|
|
last_notification_type = notifications.last.notification_type
|
|
expect(last_notification_type).to eq(Notification.types[notification_type]),
|
|
"expected notification type to be '#{notification_type}', got '#{Notification.types.key(last_notification_type)}'"
|
|
end
|
|
|
|
match_when_negated do |actual|
|
|
expect { actual.call }.to_not change {
|
|
user.notifications.where(notification_type: Notification.types[notification_type]).count
|
|
}
|
|
end
|
|
|
|
supports_block_expectations
|
|
end
|
|
|
|
RSpec::Matchers.define_negated_matcher :not_add_notification, :add_notification
|
|
|
|
RSpec.describe PostAlerter do
|
|
fab!(:category)
|
|
|
|
fab!(:topic)
|
|
fab!(:post)
|
|
|
|
fab!(:private_message_topic)
|
|
fab!(:private_message_topic_post1) { Fabricate(:post, topic: private_message_topic) }
|
|
fab!(:private_message_topic_post2) { Fabricate(:post, topic: private_message_topic) }
|
|
|
|
fab!(:group)
|
|
|
|
fab!(:admin)
|
|
fab!(:evil_trout) { Fabricate(:evil_trout, refresh_auto_groups: true) }
|
|
fab!(:coding_horror)
|
|
fab!(:walterwhite) { Fabricate(:walter_white, refresh_auto_groups: true) }
|
|
fab!(:user)
|
|
fab!(:tl2_user) { Fabricate(:user, trust_level: TrustLevel[2]) }
|
|
|
|
fab!(:private_category) do
|
|
Fabricate(
|
|
:private_category,
|
|
group: group,
|
|
email_in: "test@test.com",
|
|
email_in_allow_strangers: true,
|
|
)
|
|
end
|
|
|
|
def create_post_with_alerts(args = {})
|
|
post = Fabricate(:post, args)
|
|
PostAlerter.post_created(post)
|
|
end
|
|
|
|
def setup_push_notification_subscription_for(user:)
|
|
2.times do |i|
|
|
UserApiKey.create!(
|
|
user_id: user.id,
|
|
client_id: "xxx#{i}",
|
|
application_name: "iPhone#{i}",
|
|
scopes: ["notifications"].map { |name| UserApiKeyScope.new(name: name) },
|
|
push_url: "https://site2.com/push",
|
|
)
|
|
end
|
|
end
|
|
|
|
context "with private message" do
|
|
it "notifies for pms correctly" do
|
|
pm = Fabricate(:topic, archetype: "private_message", category_id: nil)
|
|
op = Fabricate(:post, user: pm.user)
|
|
pm.allowed_users << pm.user
|
|
PostAlerter.post_created(op)
|
|
|
|
reply = Fabricate(:post, user: pm.user, topic: pm, reply_to_post_number: 1)
|
|
PostAlerter.post_created(reply)
|
|
|
|
reply2 = Fabricate(:post, topic: pm, reply_to_post_number: 1)
|
|
PostAlerter.post_created(reply2)
|
|
|
|
# we get a green notification for a reply
|
|
expect(Notification.where(user_id: pm.user_id).pick(:notification_type)).to eq(
|
|
Notification.types[:private_message],
|
|
)
|
|
|
|
TopicUser.change(
|
|
pm.user_id,
|
|
pm.id,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
|
|
Notification.destroy_all
|
|
|
|
reply3 = Fabricate(:post, topic: pm)
|
|
PostAlerter.post_created(reply3)
|
|
|
|
# no notification cause we are tracking
|
|
expect(Notification.where(user_id: pm.user_id).count).to eq(0)
|
|
|
|
Notification.destroy_all
|
|
|
|
reply4 = Fabricate(:post, topic: pm, reply_to_post_number: 1)
|
|
PostAlerter.post_created(reply4)
|
|
|
|
# yes notification cause we were replied to
|
|
expect(Notification.where(user_id: pm.user_id).count).to eq(1)
|
|
end
|
|
|
|
it "prioritises 'private_message' type even if direct mention" do
|
|
pm = Fabricate(:topic, archetype: "private_message", category_id: nil)
|
|
op =
|
|
Fabricate(:post, topic: pm, user: pm.user, raw: "Hello @#{user.username}, nice to meet you")
|
|
pm.allowed_users << pm.user
|
|
pm.allowed_users << user
|
|
TopicUser.create!(
|
|
user_id: user.id,
|
|
topic_id: pm.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
PostAlerter.post_created(op)
|
|
|
|
expect(Notification.where(user_id: user.id).pick(:notification_type)).to eq(
|
|
Notification.types[:private_message],
|
|
)
|
|
end
|
|
|
|
context "with group inboxes" do
|
|
fab!(:user1) { Fabricate(:user) }
|
|
fab!(:user2) { Fabricate(:user) }
|
|
fab!(:group) do
|
|
Fabricate(:group, users: [user2], name: "TestGroup", default_notification_level: 2)
|
|
end
|
|
fab!(:watching_first_post_group) do
|
|
Fabricate(
|
|
:group,
|
|
name: "some_group",
|
|
users: [evil_trout, coding_horror],
|
|
messageable_level: Group::ALIAS_LEVELS[:everyone],
|
|
default_notification_level: NotificationLevels.all[:watching_first_post],
|
|
)
|
|
end
|
|
fab!(:pm) do
|
|
Fabricate(:topic, archetype: "private_message", category_id: nil, allowed_groups: [group])
|
|
end
|
|
fab!(:op) { Fabricate(:post, user: pm.user, topic: pm) }
|
|
|
|
it "triggers :before_create_notifications_for_users" do
|
|
pm.allowed_users << user1
|
|
events = DiscourseEvent.track_events { PostAlerter.post_created(op) }
|
|
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[user1], op],
|
|
)
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[user2], op],
|
|
)
|
|
end
|
|
|
|
it "triggers group summary notification" do
|
|
Jobs.run_immediately!
|
|
TopicUser.change(
|
|
user2.id,
|
|
pm.id,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
|
|
PostAlerter.post_created(op)
|
|
group_summary_notification = Notification.where(user_id: user2.id)
|
|
|
|
expect(group_summary_notification.count).to eq(1)
|
|
expect(group_summary_notification.first.notification_type).to eq(
|
|
Notification.types[:group_message_summary],
|
|
)
|
|
|
|
notification_payload = JSON.parse(group_summary_notification.first.data)
|
|
expect(notification_payload["group_name"]).to eq(group.name)
|
|
expect(notification_payload["inbox_count"]).to eq(1)
|
|
|
|
# archiving the only PM clears the group summary notification
|
|
GroupArchivedMessage.archive!(group.id, pm)
|
|
expect(Notification.where(user_id: user2.id)).to be_blank
|
|
|
|
# moving to inbox the only PM restores the group summary notification
|
|
GroupArchivedMessage.move_to_inbox!(group.id, pm)
|
|
group_summary_notification = Notification.where(user_id: user2.id)
|
|
expect(group_summary_notification.first.notification_type).to eq(
|
|
Notification.types[:group_message_summary],
|
|
)
|
|
|
|
updated_payload = JSON.parse(group_summary_notification.first.data)
|
|
expect(updated_payload["group_name"]).to eq(group.name)
|
|
expect(updated_payload["inbox_count"]).to eq(1)
|
|
|
|
# adding a second PM updates the count
|
|
pm2 =
|
|
Fabricate(:topic, archetype: "private_message", category_id: nil, allowed_groups: [group])
|
|
op2 = Fabricate(:post, user: pm2.user, topic: pm2)
|
|
TopicUser.change(
|
|
user2.id,
|
|
pm2.id,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
|
|
PostAlerter.post_created(op2)
|
|
group_summary_notification = Notification.where(user_id: user2.id)
|
|
updated_payload = JSON.parse(group_summary_notification.first.data)
|
|
|
|
expect(updated_payload["group_name"]).to eq(group.name)
|
|
expect(updated_payload["inbox_count"]).to eq(2)
|
|
|
|
# archiving the second PM quietly updates the group summary count for the acting user
|
|
GroupArchivedMessage.archive!(group.id, pm2, acting_user_id: user2.id)
|
|
group_summary_notification = Notification.where(user_id: user2.id)
|
|
expect(group_summary_notification.first.read).to eq(true)
|
|
updated_payload = JSON.parse(group_summary_notification.first.data)
|
|
|
|
expect(updated_payload["inbox_count"]).to eq(1)
|
|
|
|
# moving to inbox the second PM quietly updates the group summary count for the acting user
|
|
GroupArchivedMessage.move_to_inbox!(group.id, pm2, acting_user_id: user2.id)
|
|
group_summary_notification = Notification.where(user_id: user2.id)
|
|
expect(group_summary_notification.first.read).to eq(true)
|
|
updated_payload = JSON.parse(group_summary_notification.first.data)
|
|
|
|
expect(updated_payload["group_name"]).to eq(group.name)
|
|
expect(updated_payload["inbox_count"]).to eq(2)
|
|
end
|
|
|
|
it "updates the consolidated group summary inbox count and bumps the notification" do
|
|
user2.update!(last_seen_at: 5.minutes.ago)
|
|
TopicUser.change(
|
|
user2.id,
|
|
pm.id,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
PostAlerter.post_created(op)
|
|
|
|
starting_count =
|
|
Notification
|
|
.where(user_id: user2.id, notification_type: Notification.types[:group_message_summary])
|
|
.pluck("data::json ->> 'inbox_count'")
|
|
.last
|
|
.to_i
|
|
|
|
another_pm =
|
|
Fabricate(:topic, archetype: "private_message", category_id: nil, allowed_groups: [group])
|
|
another_post = Fabricate(:post, user: another_pm.user, topic: another_pm)
|
|
TopicUser.change(
|
|
user2.id,
|
|
another_pm.id,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
|
|
message_data =
|
|
MessageBus
|
|
.track_publish("/notification/#{user2.id}") { PostAlerter.post_created(another_post) }
|
|
.first
|
|
.data
|
|
|
|
expect(Notification.where(user: user2).count).to eq(1)
|
|
expect(message_data.dig(:last_notification, :notification, :data, :inbox_count)).to eq(
|
|
starting_count + 1,
|
|
)
|
|
expect(message_data[:unread_notifications]).to eq(1)
|
|
end
|
|
|
|
it "sends a PM notification when replying to a member tracking the topic" do
|
|
group.add(user1)
|
|
|
|
post = Fabricate(:post, topic: pm, user: user1)
|
|
TopicUser.change(
|
|
user1.id,
|
|
pm.id,
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
)
|
|
|
|
expect {
|
|
create_post_with_alerts(
|
|
raw: "this is a reply to your post...",
|
|
topic: pm,
|
|
user: user2,
|
|
reply_to_post_number: post.post_number,
|
|
)
|
|
}.to change(
|
|
user1.notifications.where(notification_type: Notification.types[:private_message]),
|
|
:count,
|
|
).by(1)
|
|
end
|
|
|
|
it "notifies a group member if someone replies to their post" do
|
|
group.add(user1)
|
|
|
|
post = Fabricate(:post, topic: pm, user: user1)
|
|
TopicUser.change(
|
|
user1.id,
|
|
pm.id,
|
|
notification_level: TopicUser.notification_levels[:regular],
|
|
)
|
|
|
|
expect {
|
|
create_post_with_alerts(
|
|
raw: "this is a reply to your post...",
|
|
topic: pm,
|
|
user: user2,
|
|
reply_to_post_number: post.post_number,
|
|
)
|
|
}.to change(user1.notifications, :count).by(1)
|
|
end
|
|
|
|
it "notifies a group member if someone quotes their post" do
|
|
group.add(user1)
|
|
|
|
post = Fabricate(:post, topic: pm, user: user1)
|
|
TopicUser.change(
|
|
user1.id,
|
|
pm.id,
|
|
notification_level: TopicUser.notification_levels[:regular],
|
|
)
|
|
quote_raw = <<~MD
|
|
[quote="#{user1.username}, post:1, topic:#{pm.id}"]#{post.raw}[/quote]
|
|
MD
|
|
|
|
expect { create_post_with_alerts(raw: quote_raw, topic: pm, user: user2) }.to change(
|
|
user1.notifications,
|
|
:count,
|
|
).by(1)
|
|
end
|
|
|
|
it "Doesn't notify non-admin users when their post is quoted inside a whisper" do
|
|
group.add(admin)
|
|
|
|
TopicUser.change(
|
|
user2.id,
|
|
pm.id,
|
|
notification_level: TopicUser.notification_levels[:regular],
|
|
)
|
|
quote_raw = <<~MD
|
|
[quote="#{user2.username}, post:1, topic:#{pm.id}"]#{op.raw}[/quote]
|
|
MD
|
|
|
|
expect {
|
|
create_post_with_alerts(
|
|
raw: quote_raw,
|
|
topic: pm,
|
|
user: admin,
|
|
post_type: Post.types[:whisper],
|
|
)
|
|
}.not_to change(user2.notifications, :count)
|
|
end
|
|
|
|
context "with watching_first_post notification level" do
|
|
it "notifies group members of first post" do
|
|
post =
|
|
PostCreator.create!(
|
|
user,
|
|
title: "Hi there, welcome to my topic",
|
|
raw: "This is my awesome message",
|
|
archetype: Archetype.private_message,
|
|
target_group_names: watching_first_post_group.name,
|
|
)
|
|
|
|
PostAlerter.new.after_save_post(post, true)
|
|
|
|
expect(
|
|
evil_trout
|
|
.notifications
|
|
.where(notification_type: Notification.types[:private_message])
|
|
.count,
|
|
).to eq(1)
|
|
expect(
|
|
coding_horror
|
|
.notifications
|
|
.where(notification_type: Notification.types[:private_message])
|
|
.count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "doesn't notify group members of replies" do
|
|
post =
|
|
PostCreator.create!(
|
|
user,
|
|
title: "Hi there, welcome to my topic",
|
|
raw: "This is my awesome message",
|
|
archetype: Archetype.private_message,
|
|
target_group_names: watching_first_post_group.name,
|
|
)
|
|
|
|
expect(
|
|
evil_trout
|
|
.notifications
|
|
.where(notification_type: Notification.types[:private_message])
|
|
.count,
|
|
).to eq(0)
|
|
expect(
|
|
coding_horror
|
|
.notifications
|
|
.where(notification_type: Notification.types[:private_message])
|
|
.count,
|
|
).to eq(0)
|
|
|
|
PostAlerter.new.after_save_post(post, true)
|
|
|
|
expect(
|
|
evil_trout
|
|
.notifications
|
|
.where(notification_type: Notification.types[:private_message])
|
|
.count,
|
|
).to eq(1)
|
|
expect(
|
|
coding_horror
|
|
.notifications
|
|
.where(notification_type: Notification.types[:private_message])
|
|
.count,
|
|
).to eq(1)
|
|
|
|
reply =
|
|
Fabricate(
|
|
:post,
|
|
raw: "Reply to PM",
|
|
user: user,
|
|
topic: post.topic,
|
|
reply_to_post_number: post.post_number,
|
|
)
|
|
|
|
expect do PostAlerter.new.after_save_post(reply, false) end.to_not change {
|
|
Notification.count
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with unread" do
|
|
it "does not return whispers as unread posts" do
|
|
_whisper =
|
|
Fabricate(
|
|
:post,
|
|
raw: "this is a whisper post",
|
|
user: admin,
|
|
topic: post.topic,
|
|
reply_to_post_number: post.post_number,
|
|
post_type: Post.types[:whisper],
|
|
)
|
|
|
|
expect(PostAlerter.new.first_unread_post(post.user, post.topic)).to be_blank
|
|
end
|
|
end
|
|
|
|
context "with edits" do
|
|
it "notifies correctly on edits" do
|
|
Jobs.run_immediately!
|
|
PostActionNotifier.enable
|
|
|
|
post = Fabricate(:post, raw: "I love waffles")
|
|
|
|
expect do post.revise(admin, raw: "I made a revision") end.to add_notification(
|
|
post.user,
|
|
:edited,
|
|
)
|
|
|
|
# lets also like this post which should trigger a notification
|
|
expect do
|
|
PostActionCreator.new(admin, post, PostActionType.types[:like]).perform
|
|
end.to add_notification(post.user, :liked)
|
|
|
|
# skip this notification cause we already notified on an edit by the same user
|
|
# in the previous edit
|
|
freeze_time 2.hours.from_now
|
|
|
|
expect do post.revise(admin, raw: "I made another revision") end.to_not change {
|
|
Notification.count
|
|
}
|
|
|
|
# this we do not skip cause 1 day has passed
|
|
freeze_time 23.hours.from_now
|
|
|
|
expect do post.revise(admin, raw: "I made another revision xyz") end.to add_notification(
|
|
post.user,
|
|
:edited,
|
|
)
|
|
|
|
expect do post.revise(Fabricate(:admin), raw: "I made a revision") end.to add_notification(
|
|
post.user,
|
|
:edited,
|
|
)
|
|
|
|
freeze_time 2.hours.from_now
|
|
|
|
expect do post.revise(admin, raw: "I made another revision") end.to add_notification(
|
|
post.user,
|
|
:edited,
|
|
)
|
|
end
|
|
|
|
it "notifies flaggers when flagged post gets unhidden by edit" do
|
|
post = create_post
|
|
|
|
PostActionNotifier.enable
|
|
Reviewable.set_priorities(high: 4.0)
|
|
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:low]
|
|
|
|
PostActionCreator.spam(evil_trout, post)
|
|
PostActionCreator.spam(walterwhite, post)
|
|
|
|
post.reload
|
|
expect(post.hidden).to eq(true)
|
|
|
|
expect { post.revise(post.user, raw: post.raw + " ha I edited it ") }.to add_notification(
|
|
evil_trout,
|
|
:edited,
|
|
).and add_notification(walterwhite, :edited)
|
|
|
|
post.reload
|
|
expect(post.hidden).to eq(false)
|
|
|
|
notification = walterwhite.notifications.last
|
|
expect(notification.topic_id).to eq(post.topic.id)
|
|
expect(notification.post_number).to eq(post.post_number)
|
|
expect(notification.data_hash["display_username"]).to eq(post.user.username)
|
|
|
|
PostActionCreator.create(coding_horror, post, :spam)
|
|
PostActionCreator.create(walterwhite, post, :off_topic)
|
|
|
|
post.reload
|
|
expect(post.hidden).to eq(true)
|
|
|
|
expect {
|
|
post.revise(post.user, raw: post.raw + " ha I edited it again ")
|
|
}.to not_add_notification(evil_trout, :edited).and not_add_notification(
|
|
coding_horror,
|
|
:edited,
|
|
).and not_add_notification(walterwhite, :edited)
|
|
end
|
|
end
|
|
|
|
context "with quotes" do
|
|
fab!(:category)
|
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
|
|
|
it "does not notify for muted users" do
|
|
post = Fabricate(:post, raw: '[quote="Eviltrout, post:1"]whatup[/quote]', topic: topic)
|
|
MutedUser.create!(user_id: evil_trout.id, muted_user_id: post.user_id)
|
|
|
|
expect { PostAlerter.post_created(post) }.not_to change(evil_trout.notifications, :count)
|
|
end
|
|
|
|
it "does not notify for ignored users" do
|
|
post = Fabricate(:post, raw: '[quote="EvilTrout, post:1"]whatup[/quote]', topic: topic)
|
|
Fabricate(:ignored_user, user: evil_trout, ignored_user: post.user)
|
|
|
|
expect { PostAlerter.post_created(post) }.not_to change(evil_trout.notifications, :count)
|
|
end
|
|
|
|
it "does not notify for users with new reply notification" do
|
|
post = Fabricate(:post, raw: '[quote="eviltRout, post:1"]whatup[/quote]', topic: topic)
|
|
notification =
|
|
Notification.create!(
|
|
topic: post.topic,
|
|
post_number: post.post_number,
|
|
read: false,
|
|
notification_type: Notification.types[:replied],
|
|
user: evil_trout,
|
|
data: { topic_title: "test topic" }.to_json,
|
|
)
|
|
expect { PostAlerter.post_edited(post) }.not_to change(evil_trout.notifications, :count)
|
|
|
|
notification.destroy
|
|
expect { PostAlerter.post_edited(post) }.to change(evil_trout.notifications, :count).by(1)
|
|
end
|
|
|
|
it "does not collapse quote notifications" do
|
|
expect {
|
|
2.times do
|
|
create_post_with_alerts(raw: '[quote="eviltrout, post:1"]whatup[/quote]', topic: topic)
|
|
end
|
|
}.to change(evil_trout.notifications, :count).by(2)
|
|
end
|
|
|
|
it "won't notify the user a second time on revision" do
|
|
p1 = create_post_with_alerts(raw: '[quote="Evil Trout, post:1"]whatup[/quote]')
|
|
expect {
|
|
p1.revise(p1.user, raw: '[quote="Evil Trout, post:1"]whatup now?[/quote]')
|
|
}.not_to change(evil_trout.notifications, :count)
|
|
end
|
|
|
|
it "doesn't notify the poster" do
|
|
topic = create_post_with_alerts.topic
|
|
expect {
|
|
Fabricate(
|
|
:post,
|
|
topic: topic,
|
|
user: topic.user,
|
|
raw: '[quote="Bruce Wayne, post:1"]whatup[/quote]',
|
|
)
|
|
}.not_to change(topic.user.notifications, :count)
|
|
end
|
|
|
|
it "triggers :before_create_notifications_for_users" do
|
|
post = Fabricate(:post, raw: '[quote="eviltrout, post:1"]whatup[/quote]')
|
|
events = DiscourseEvent.track_events { PostAlerter.post_created(post) }
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[evil_trout], post],
|
|
)
|
|
end
|
|
|
|
context "with notifications when prioritizing full names" do
|
|
before do
|
|
SiteSetting.prioritize_username_in_ux = false
|
|
SiteSetting.display_name_on_posts = true
|
|
end
|
|
|
|
it "sends to correct user" do
|
|
quote = <<~MD
|
|
[quote="#{evil_trout.name}, post:1, username:#{evil_trout.username}"]whatup[/quote]
|
|
MD
|
|
|
|
expect { create_post_with_alerts(raw: quote, topic: topic) }.to change(
|
|
evil_trout.notifications,
|
|
:count,
|
|
).by(1)
|
|
end
|
|
|
|
it "sends to correct users when nested quotes with multiple users" do
|
|
quote = <<~MD
|
|
[quote="#{evil_trout.name}, post:1, username:#{evil_trout.username}"]this [quote="#{walterwhite.name}, post:2, username:#{walterwhite.username}"]whatup[/quote][/quote]
|
|
MD
|
|
|
|
expect { create_post_with_alerts(raw: quote, topic: topic) }.to change(
|
|
evil_trout.notifications,
|
|
:count,
|
|
).by(1).and change(walterwhite.notifications, :count).by(1)
|
|
end
|
|
|
|
it "sends to correct users when multiple quotes" do
|
|
user = Fabricate(:user)
|
|
quote = <<~MD
|
|
[quote="#{evil_trout.name}, post:1, username:#{evil_trout.username}"]"username:#{user.username}" [/quote]/n [quote="#{walterwhite.name}, post:2, username:#{walterwhite.username}"]whatup[/quote]
|
|
MD
|
|
|
|
expect { create_post_with_alerts(raw: quote, topic: topic) }.to change(
|
|
evil_trout.notifications,
|
|
:count,
|
|
).by(1).and change(walterwhite.notifications, :count).by(1).and not_change(
|
|
user.notifications,
|
|
:count,
|
|
)
|
|
end
|
|
|
|
it "sends to correct user when user has a full name that matches another user's username" do
|
|
user_with_matching_full_name = Fabricate(:user, name: evil_trout.username)
|
|
quote = <<~MD
|
|
[quote="#{user_with_matching_full_name.name}, post:1, username:#{user_with_matching_full_name.username}"]this [/quote]
|
|
MD
|
|
|
|
expect { create_post_with_alerts(raw: quote, topic: topic) }.to change(
|
|
user_with_matching_full_name.notifications,
|
|
:count,
|
|
).by(1).and not_change(evil_trout.notifications, :count)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with linked" do
|
|
let(:post1) { create_post }
|
|
let(:user) { post1.user }
|
|
let(:linking_post) { create_post(raw: "my magic topic\n##{Discourse.base_url}#{post1.url}") }
|
|
|
|
before { Jobs.run_immediately! }
|
|
|
|
it "will notify correctly on linking" do
|
|
linking_post
|
|
|
|
expect(user.notifications.count).to eq(1)
|
|
|
|
watcher = Fabricate(:user)
|
|
TopicUser.create!(
|
|
user_id: watcher.id,
|
|
topic_id: topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
|
|
create_post(
|
|
topic_id: topic.id,
|
|
user: user,
|
|
raw: "my magic topic\n##{Discourse.base_url}#{post1.url}",
|
|
)
|
|
|
|
user.reload
|
|
expect(user.notifications.where(notification_type: Notification.types[:linked]).count).to eq(
|
|
1,
|
|
)
|
|
|
|
expect(watcher.notifications.count).to eq(1)
|
|
|
|
# don't notify on reflection
|
|
post1.reload
|
|
expect(PostAlerter.new.extract_linked_users(post1).length).to eq(0)
|
|
end
|
|
|
|
it "triggers :before_create_notifications_for_users" do
|
|
events = DiscourseEvent.track_events { linking_post }
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[user], linking_post],
|
|
)
|
|
end
|
|
|
|
it "doesn't notify the linked user if the user is staged and the category is restricted and allows strangers" do
|
|
staged_user = Fabricate(:staged, refresh_auto_groups: true)
|
|
group_member = Fabricate(:user, refresh_auto_groups: true)
|
|
group.add(group_member)
|
|
|
|
staged_user_post = create_post(user: staged_user, category: private_category)
|
|
|
|
create_post(
|
|
user: group_member,
|
|
category: private_category,
|
|
raw: "my magic topic\n##{Discourse.base_url}#{staged_user_post.url}",
|
|
)
|
|
|
|
staged_user.reload
|
|
expect(
|
|
staged_user.notifications.where(notification_type: Notification.types[:linked]).count,
|
|
).to eq(0)
|
|
end
|
|
end
|
|
|
|
context "with @here" do
|
|
let(:post) do
|
|
create_post_with_alerts(raw: "Hello @here how are you?", user: tl2_user, topic: topic)
|
|
end
|
|
fab!(:other_post) { Fabricate(:post, topic: topic) }
|
|
|
|
before { Jobs.run_immediately! }
|
|
|
|
it "does not notify unrelated users" do
|
|
expect { post }.not_to change(evil_trout.notifications, :count)
|
|
end
|
|
|
|
it "does not work if user here exists" do
|
|
Fabricate(:user, username: SiteSetting.here_mention)
|
|
expect { post }.not_to change(other_post.user.notifications, :count)
|
|
end
|
|
|
|
it "notifies users who replied" do
|
|
post2 = Fabricate(:post, topic: topic, post_type: Post.types[:whisper])
|
|
post3 = Fabricate(:post, topic: topic)
|
|
|
|
expect { post }.to change(other_post.user.notifications, :count).by(1).and not_change(
|
|
post2.user.notifications,
|
|
:count,
|
|
).and change(post3.user.notifications, :count).by(1)
|
|
end
|
|
|
|
it "notifies users who whispered" do
|
|
post2 = Fabricate(:post, topic: topic, post_type: Post.types[:whisper])
|
|
post3 = Fabricate(:post, topic: topic)
|
|
|
|
tl2_user.grant_admin!
|
|
|
|
expect { post }.to change(other_post.user.notifications, :count).by(1).and change(
|
|
post2.user.notifications,
|
|
:count,
|
|
).by(1).and change(post3.user.notifications, :count).by(1)
|
|
end
|
|
|
|
it "notifies only last max_here_mentioned users" do
|
|
SiteSetting.max_here_mentioned = 2
|
|
3.times { Fabricate(:post, topic: topic) }
|
|
expect { post }.to change { Notification.count }.by(2)
|
|
end
|
|
end
|
|
|
|
context "with @group mentions" do
|
|
fab!(:group) do
|
|
Fabricate(:group, name: "group", mentionable_level: Group::ALIAS_LEVELS[:everyone])
|
|
end
|
|
let(:post) { create_post_with_alerts(raw: "Hello @group how are you?") }
|
|
before { group.add(evil_trout) }
|
|
|
|
it "notifies users correctly" do
|
|
expect { post }.to change(evil_trout.notifications, :count).by(1)
|
|
|
|
expect(GroupMention.count).to eq(1)
|
|
|
|
Fabricate(:group, name: "group-alt", mentionable_level: Group::ALIAS_LEVELS[:everyone])
|
|
|
|
expect {
|
|
create_post_with_alerts(raw: "Hello, @group-alt should not trigger a notification?")
|
|
}.not_to change(evil_trout.notifications, :count)
|
|
|
|
expect(GroupMention.count).to eq(2)
|
|
|
|
group.update_columns(mentionable_level: Group::ALIAS_LEVELS[:members_mods_and_admins])
|
|
expect { create_post_with_alerts(raw: "Hello @group you are not mentionable") }.not_to change(
|
|
evil_trout.notifications,
|
|
:count,
|
|
)
|
|
|
|
expect(GroupMention.count).to eq(3)
|
|
|
|
group.update_columns(mentionable_level: Group::ALIAS_LEVELS[:owners_mods_and_admins])
|
|
group.add_owner(user)
|
|
expect {
|
|
create_post_with_alerts(raw: "Hello @group the owner can mention you", user: user)
|
|
}.to change(evil_trout.notifications, :count).by(1)
|
|
|
|
expect(GroupMention.count).to eq(4)
|
|
end
|
|
|
|
it "takes private mention as precedence" do
|
|
expect {
|
|
create_post_with_alerts(raw: "Hello @group and @eviltrout, nice to meet you")
|
|
}.to change(evil_trout.notifications, :count).by(1)
|
|
expect(evil_trout.notifications.last.notification_type).to eq(Notification.types[:mentioned])
|
|
end
|
|
|
|
it "triggers :before_create_notifications_for_users" do
|
|
events = DiscourseEvent.track_events { post }
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[evil_trout], post],
|
|
)
|
|
end
|
|
end
|
|
|
|
context "with @mentions" do
|
|
let(:mention_post) { create_post_with_alerts(user: user, raw: "Hello @eviltrout") }
|
|
let(:topic) { mention_post.topic }
|
|
|
|
before { Jobs.run_immediately! }
|
|
|
|
it "notifies a user" do
|
|
expect { mention_post }.to change(evil_trout.notifications, :count).by(1)
|
|
end
|
|
|
|
it "won't notify the user a second time on revision" do
|
|
mention_post
|
|
expect {
|
|
mention_post.revise(
|
|
mention_post.user,
|
|
raw: "New raw content that still mentions @eviltrout",
|
|
)
|
|
}.not_to change(evil_trout.notifications, :count)
|
|
end
|
|
|
|
it "doesn't notify the user who created the topic in regular mode" do
|
|
topic.notify_regular!(user)
|
|
mention_post
|
|
expect {
|
|
create_post_with_alerts(user: user, raw: "second post", topic: topic)
|
|
}.not_to change(user.notifications, :count)
|
|
end
|
|
|
|
it "triggers :before_create_notifications_for_users" do
|
|
events = DiscourseEvent.track_events { mention_post }
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[evil_trout], mention_post],
|
|
)
|
|
end
|
|
|
|
it "notification comes from editor if mention is added later" do
|
|
post = create_post_with_alerts(user: user, raw: "No mention here.")
|
|
expect { post.revise(admin, raw: "Mention @eviltrout in this edit.") }.to change(
|
|
evil_trout.notifications,
|
|
:count,
|
|
)
|
|
n = evil_trout.notifications.last
|
|
expect(n.data_hash["original_username"]).to eq(admin.username)
|
|
end
|
|
|
|
it "doesn't notify the last post editor if they mention themselves" do
|
|
post = create_post_with_alerts(user: user, raw: "Post without a mention.")
|
|
expect { post.revise(evil_trout, raw: "O hai, @eviltrout!") }.not_to change(
|
|
evil_trout.notifications,
|
|
:count,
|
|
)
|
|
end
|
|
|
|
fab!(:alice) { Fabricate(:user, username: "alice") }
|
|
fab!(:bob) { Fabricate(:user, username: "bob") }
|
|
fab!(:carol) { Fabricate(:admin, username: "carol") }
|
|
fab!(:dave) { Fabricate(:user, username: "dave") }
|
|
fab!(:eve) { Fabricate(:user, username: "eve") }
|
|
fab!(:group) do
|
|
Fabricate(:group, name: "group", mentionable_level: Group::ALIAS_LEVELS[:everyone])
|
|
end
|
|
|
|
before { group.bulk_add([alice.id, eve.id]) }
|
|
|
|
def create_post_with_alerts(args = {})
|
|
post = Fabricate(:post, args)
|
|
PostAlerter.post_created(post)
|
|
end
|
|
|
|
def set_topic_notification_level(user, topic, level_name)
|
|
TopicUser.change(
|
|
user.id,
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[level_name],
|
|
)
|
|
end
|
|
|
|
context "with topic" do
|
|
fab!(:topic) { Fabricate(:topic, user: alice) }
|
|
|
|
%i[watching tracking regular].each do |notification_level|
|
|
context "when notification level is '#{notification_level}'" do
|
|
before { set_topic_notification_level(alice, topic, notification_level) }
|
|
|
|
it "notifies about @username mention" do
|
|
args = { user: bob, topic: topic, raw: "Hello @alice" }
|
|
expect { create_post_with_alerts(args) }.to add_notification(alice, :mentioned)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when notification level is 'muted'" do
|
|
before { set_topic_notification_level(alice, topic, :muted) }
|
|
|
|
it "does not notify about @username mention" do
|
|
args = { user: bob, topic: topic, raw: "Hello @alice" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(alice, :mentioned)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with message to users" do
|
|
fab!(:pm_topic) do
|
|
Fabricate(
|
|
:private_message_topic,
|
|
user: alice,
|
|
topic_allowed_users: [
|
|
Fabricate.build(:topic_allowed_user, user: alice),
|
|
Fabricate.build(:topic_allowed_user, user: bob),
|
|
Fabricate.build(:topic_allowed_user, user: Discourse.system_user),
|
|
],
|
|
)
|
|
end
|
|
|
|
context "when user is part of conversation" do
|
|
%i[watching tracking regular].each do |notification_level|
|
|
context "when notification level is '#{notification_level}'" do
|
|
before { set_topic_notification_level(alice, pm_topic, notification_level) }
|
|
let(:expected_notification) do
|
|
notification_level == :watching ? :private_message : :mentioned
|
|
end
|
|
|
|
it "notifies about @username mention" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @alice" }
|
|
expect { create_post_with_alerts(args) }.to add_notification(
|
|
alice,
|
|
expected_notification,
|
|
)
|
|
end
|
|
|
|
it "notifies about @username mentions by non-human users" do
|
|
args = { user: Discourse.system_user, topic: pm_topic, raw: "Hello @alice" }
|
|
expect { create_post_with_alerts(args) }.to add_notification(
|
|
alice,
|
|
expected_notification,
|
|
)
|
|
end
|
|
|
|
it "notifies about @group mention when allowed user is part of group" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @group" }
|
|
expect { create_post_with_alerts(args) }.to add_notification(alice, :group_mentioned)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when notification level is 'muted'" do
|
|
before { set_topic_notification_level(alice, pm_topic, :muted) }
|
|
|
|
it "does not notify about @username mention" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @alice" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(alice, :mentioned)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when user is not part of conversation" do
|
|
it "does not notify about @username mention even though mentioned user is an admin" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @carol" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(carol, :mentioned)
|
|
end
|
|
|
|
it "does not notify about @username mention by non-human user even though mentioned user is an admin" do
|
|
args = { user: Discourse.system_user, topic: pm_topic, raw: "Hello @carol" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(carol, :mentioned)
|
|
end
|
|
|
|
it "does not notify about @username mention when mentioned user is not allowed to see message" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @dave" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(dave, :mentioned)
|
|
end
|
|
|
|
it "does not notify about @group mention when user is not an allowed user" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @group" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(eve, :group_mentioned)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with message to group" do
|
|
fab!(:some_group) do
|
|
Fabricate(:group, name: "some_group", mentionable_level: Group::ALIAS_LEVELS[:everyone])
|
|
end
|
|
fab!(:pm_topic) do
|
|
Fabricate(
|
|
:private_message_topic,
|
|
user: alice,
|
|
topic_allowed_groups: [Fabricate.build(:topic_allowed_group, group: group)],
|
|
topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: Discourse.system_user)],
|
|
)
|
|
end
|
|
|
|
before { some_group.bulk_add([alice.id, carol.id]) }
|
|
|
|
context "when group is part of conversation" do
|
|
%i[watching tracking regular].each do |notification_level|
|
|
context "when notification level is '#{notification_level}'" do
|
|
before { set_topic_notification_level(alice, pm_topic, notification_level) }
|
|
|
|
it "notifies about @group mention" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @group" }
|
|
expect { create_post_with_alerts(args) }.to add_notification(alice, :group_mentioned)
|
|
end
|
|
|
|
it "notifies about @group mentions by non-human users" do
|
|
args = { user: Discourse.system_user, topic: pm_topic, raw: "Hello @group" }
|
|
expect { create_post_with_alerts(args) }.to add_notification(alice, :group_mentioned)
|
|
end
|
|
|
|
it "notifies about @username mention when user belongs to allowed group" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @alice" }
|
|
expect { create_post_with_alerts(args) }.to add_notification(alice, :mentioned)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when notification level is 'muted'" do
|
|
before { set_topic_notification_level(alice, pm_topic, :muted) }
|
|
|
|
it "does not notify about @group mention" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @group" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(
|
|
alice,
|
|
:group_mentioned,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when group is not part of conversation" do
|
|
it "does not notify about @group mention even though mentioned user is an admin" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @some_group" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(carol, :group_mentioned)
|
|
end
|
|
|
|
it "does not notify about @group mention by non-human user even though mentioned user is an admin" do
|
|
args = { user: Discourse.system_user, topic: pm_topic, raw: "Hello @some_group" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(carol, :group_mentioned)
|
|
end
|
|
|
|
it "does not notify about @username mention when user doesn't belong to allowed group" do
|
|
args = { user: bob, topic: pm_topic, raw: "Hello @dave" }
|
|
expect { create_post_with_alerts(args) }.to_not add_notification(dave, :mentioned)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe ".create_notification" do
|
|
fab!(:topic) { Fabricate(:private_message_topic, user: user, created_at: 1.hour.ago) }
|
|
fab!(:post) { Fabricate(:post, topic: topic, created_at: 1.hour.ago) }
|
|
let(:type) { Notification.types[:private_message] }
|
|
|
|
it "creates a notification for PMs" do
|
|
post.revise(user, { raw: "This is the revised post" }, revised_at: Time.zone.now)
|
|
|
|
expect { PostAlerter.new.create_notification(user, type, post) }.to change {
|
|
user.notifications.count
|
|
}.by(1)
|
|
|
|
expect(user.notifications.last.data_hash["topic_title"]).to eq(topic.title)
|
|
end
|
|
|
|
it "keeps the original title for PMs" do
|
|
original_title = topic.title
|
|
|
|
post.revise(user, { title: "This is the revised title" }, revised_at: Time.now)
|
|
|
|
expect { PostAlerter.new.create_notification(user, type, post) }.to change {
|
|
user.notifications.count
|
|
}.by(1)
|
|
|
|
expect(user.notifications.last.data_hash["topic_title"]).to eq(original_title)
|
|
end
|
|
|
|
it "triggers :pre_notification_alert" do
|
|
events = DiscourseEvent.track_events { PostAlerter.new.create_notification(user, type, post) }
|
|
|
|
payload = {
|
|
notification_type: type,
|
|
post_number: post.post_number,
|
|
topic_title: post.topic.title,
|
|
topic_id: post.topic.id,
|
|
excerpt: post.excerpt(400, text_entities: true, strip_links: true, remap_emoji: true),
|
|
username: post.username,
|
|
post_url: post.url,
|
|
}
|
|
|
|
expect(events).to include(event_name: :pre_notification_alert, params: [user, payload])
|
|
end
|
|
|
|
it "does not alert when revising and changing notification type" do
|
|
PostAlerter.new.create_notification(user, type, post)
|
|
|
|
post.revise(
|
|
user,
|
|
{ raw: "Editing post to fake include a mention of @eviltrout" },
|
|
revised_at: Time.now,
|
|
)
|
|
|
|
events =
|
|
DiscourseEvent.track_events do
|
|
PostAlerter.new.create_notification(user, Notification.types[:mentioned], post)
|
|
end
|
|
|
|
payload = {
|
|
notification_type: type,
|
|
post_number: post.post_number,
|
|
topic_title: post.topic.title,
|
|
topic_id: post.topic.id,
|
|
excerpt: post.excerpt(400, text_entities: true, strip_links: true, remap_emoji: true),
|
|
username: post.username,
|
|
post_url: post.url,
|
|
}
|
|
|
|
expect(events).not_to include(event_name: :pre_notification_alert, params: [user, payload])
|
|
end
|
|
|
|
it "triggers :before_create_notification" do
|
|
type = Notification.types[:private_message]
|
|
events =
|
|
DiscourseEvent.track_events do
|
|
PostAlerter.new.create_notification(user, type, post, { revision_number: 1 })
|
|
end
|
|
expect(events).to include(
|
|
event_name: :before_create_notification,
|
|
params: [user, type, post, { revision_number: 1 }],
|
|
)
|
|
end
|
|
|
|
it "applies modifiers to notification_data" do
|
|
Plugin::Instance
|
|
.new
|
|
.register_modifier(:notification_data) do |notification_data|
|
|
notification_data[:silly_key] = "silly value"
|
|
notification_data
|
|
end
|
|
|
|
notification = PostAlerter.new.create_notification(user, type, post)
|
|
expect(notification.data_hash[:silly_key]).to eq("silly value")
|
|
|
|
DiscoursePluginRegistry.clear_modifiers!
|
|
end
|
|
end
|
|
|
|
describe ".push_notification" do
|
|
let(:mention_post) { create_post_with_alerts(user: user, raw: "Hello @eviltrout :heart:") }
|
|
let(:topic) { mention_post.topic }
|
|
|
|
before do
|
|
SiteSetting.allowed_user_api_push_urls = "https://site.com/push|https://site2.com/push"
|
|
setup_push_notification_subscription_for(user: evil_trout)
|
|
end
|
|
|
|
describe "DiscoursePluginRegistry#push_notification_filters" do
|
|
after { DiscoursePluginRegistry.reset_register!(:push_notification_filters) }
|
|
|
|
it "sends push notifications when all filters pass" do
|
|
evil_trout.update!(last_seen_at: 10.minutes.ago)
|
|
Plugin::Instance.new.register_push_notification_filter { |user, payload| true }
|
|
|
|
alerts =
|
|
MessageBus.track_publish("/notification-alert/#{evil_trout.id}") do
|
|
expect { mention_post }.to change { Jobs::PushNotification.jobs.count }.by(1)
|
|
end
|
|
|
|
expect(alerts).not_to be_empty
|
|
end
|
|
|
|
it "does not send push notifications when a filters returns false" do
|
|
Plugin::Instance.new.register_push_notification_filter { |user, payload| false }
|
|
|
|
alerts =
|
|
MessageBus.track_publish("/notification-alert/#{evil_trout.id}") do
|
|
expect { mention_post }.not_to change { Jobs::PushNotification.jobs.count }
|
|
end
|
|
|
|
expect(alerts).to be_empty
|
|
end
|
|
end
|
|
|
|
it "triggers the push notification event" do
|
|
events = DiscourseEvent.track_events { mention_post }
|
|
|
|
push_notification_event = events.find { |event| event[:event_name] == :push_notification }
|
|
expect(push_notification_event).to be_present
|
|
expect(push_notification_event[:params][0].username).to eq("eviltrout")
|
|
expect(push_notification_event[:params][1][:username]).to eq(user.username)
|
|
expect(push_notification_event[:params][1][:excerpt]).to eq("Hello @eviltrout ❤")
|
|
end
|
|
|
|
it "pushes nothing to suspended users" do
|
|
evil_trout.update_columns(suspended_till: 1.year.from_now)
|
|
expect { mention_post }.to_not change { Jobs::PushNotification.jobs.count }
|
|
|
|
events = DiscourseEvent.track_events { mention_post }
|
|
expect(events.find { |event| event[:event_name] == :push_notification }).not_to be_present
|
|
end
|
|
|
|
it "pushes nothing when the user is in 'do not disturb'" do
|
|
Fabricate(
|
|
:do_not_disturb_timing,
|
|
user: evil_trout,
|
|
starts_at: Time.zone.now,
|
|
ends_at: 1.day.from_now,
|
|
)
|
|
|
|
expect { mention_post }.to_not change { Jobs::PushNotification.jobs.count }
|
|
|
|
events = DiscourseEvent.track_events { mention_post }
|
|
expect(events.find { |event| event[:event_name] == :push_notification }).not_to be_present
|
|
end
|
|
|
|
it "correctly pushes notifications if configured correctly" do
|
|
Jobs.run_immediately!
|
|
body = nil
|
|
headers = nil
|
|
|
|
stub_request(:post, "https://site2.com/push").to_return do |request|
|
|
body = request.body
|
|
headers = request.headers
|
|
{ status: 200, body: "OK" }
|
|
end
|
|
|
|
set_subfolder "/subpath"
|
|
payload = {
|
|
"secret_key" => SiteSetting.push_api_secret_key,
|
|
"url" => Discourse.base_url,
|
|
"title" => SiteSetting.title,
|
|
"description" => SiteSetting.site_description,
|
|
"notifications" => [
|
|
{
|
|
"notification_type" => 1,
|
|
"post_number" => 1,
|
|
"topic_title" => topic.title,
|
|
"topic_id" => topic.id,
|
|
"excerpt" => "Hello @eviltrout ❤",
|
|
"username" => user.username,
|
|
"url" => UrlHelper.absolute(Discourse.base_path + mention_post.url),
|
|
"client_id" => "xxx0",
|
|
},
|
|
{
|
|
"notification_type" => 1,
|
|
"post_number" => 1,
|
|
"topic_title" => topic.title,
|
|
"topic_id" => topic.id,
|
|
"excerpt" => "Hello @eviltrout ❤",
|
|
"username" => user.username,
|
|
"url" => UrlHelper.absolute(Discourse.base_path + mention_post.url),
|
|
"client_id" => "xxx1",
|
|
},
|
|
],
|
|
}
|
|
|
|
post = mention_post
|
|
|
|
expect(JSON.parse(body)).to eq(payload)
|
|
expect(headers["Content-Type"]).to eq("application/json")
|
|
|
|
TopicUser.change(
|
|
evil_trout.id,
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
|
|
post = Fabricate(:post, topic: post.topic, user_id: evil_trout.id)
|
|
user2 = Fabricate(:user)
|
|
|
|
# if we collapse a reply notification we should get notified on the correct post
|
|
new_post =
|
|
create_post_with_alerts(
|
|
topic: post.topic,
|
|
user_id: user.id,
|
|
reply_to_post_number: post.post_number,
|
|
raw: "this is my first reply",
|
|
)
|
|
|
|
changes = {
|
|
"notification_type" => Notification.types[:posted],
|
|
"post_number" => new_post.post_number,
|
|
"username" => new_post.user.username,
|
|
"excerpt" => new_post.raw,
|
|
"url" => UrlHelper.absolute(Discourse.base_path + new_post.url),
|
|
}
|
|
|
|
payload["notifications"][0].merge! changes
|
|
payload["notifications"][1].merge! changes
|
|
|
|
expect(JSON.parse(body)).to eq(payload)
|
|
|
|
new_post =
|
|
create_post_with_alerts(
|
|
topic: post.topic,
|
|
user_id: user2.id,
|
|
reply_to_post_number: post.post_number,
|
|
raw: "this is my second reply",
|
|
)
|
|
|
|
changes = {
|
|
"post_number" => new_post.post_number,
|
|
"username" => new_post.user.username,
|
|
"excerpt" => new_post.raw,
|
|
"url" => UrlHelper.absolute(Discourse.base_path + new_post.url),
|
|
}
|
|
|
|
payload["notifications"][0].merge! changes
|
|
payload["notifications"][1].merge! changes
|
|
|
|
expect(JSON.parse(body)).to eq(payload)
|
|
end
|
|
|
|
it "does not have invalid HTML in the excerpt" do
|
|
Fabricate(:category, slug: "random")
|
|
Jobs.run_immediately!
|
|
body = nil
|
|
|
|
stub_request(:post, "https://site2.com/push").to_return do |request|
|
|
body = request.body
|
|
{ status: 200, body: "OK" }
|
|
end
|
|
create_post_with_alerts(user: user, raw: "this, @eviltrout, is a test with #random")
|
|
expect(JSON.parse(body)["notifications"][0]["excerpt"]).to eq(
|
|
"this, @eviltrout, is a test with #random",
|
|
)
|
|
end
|
|
|
|
it "delays push notification for active online user" do
|
|
SiteSetting.push_notification_time_window_mins = 10
|
|
evil_trout.update!(last_seen_at: 5.minutes.ago)
|
|
|
|
expect { mention_post }.to change { Jobs::PushNotification.jobs.count }
|
|
expect(Jobs::PushNotification.jobs[0]["at"]).to be_within(30.second).of(
|
|
5.minutes.from_now.to_f,
|
|
)
|
|
end
|
|
|
|
context "with push subscriptions" do
|
|
before do
|
|
Fabricate(:push_subscription, user: evil_trout)
|
|
SiteSetting.push_notification_time_window_mins = 10
|
|
end
|
|
|
|
it "delays sending push notification for active online user" do
|
|
evil_trout.update!(last_seen_at: 5.minutes.ago)
|
|
|
|
expect { mention_post }.to change { Jobs::SendPushNotification.jobs.count }
|
|
expect(Jobs::SendPushNotification.jobs[0]["at"]).not_to be_nil
|
|
end
|
|
|
|
it "delays sending push notification for active online user for the correct delay ammount" do
|
|
evil_trout.update!(last_seen_at: 5.minutes.ago)
|
|
|
|
# SiteSetting.push_notification_time_window_mins is 10
|
|
# last_seen_at is 5 minutes ago
|
|
# 10 minutes from now - 5 minutes ago = 5 minutes
|
|
delay = 5.minutes.from_now.to_f
|
|
|
|
expect { mention_post }.to change { Jobs::SendPushNotification.jobs.count }
|
|
expect(Jobs::SendPushNotification.jobs[0]["at"]).to be_within(30.second).of(delay)
|
|
end
|
|
|
|
it "does not delay push notification for inactive offline user" do
|
|
evil_trout.update!(last_seen_at: 40.minutes.ago)
|
|
|
|
expect { mention_post }.to change { Jobs::SendPushNotification.jobs.count }
|
|
expect(Jobs::SendPushNotification.jobs[0]["at"]).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe ".create_notification_alert" do
|
|
before { evil_trout.update_columns(last_seen_at: 10.minutes.ago) }
|
|
|
|
it "publishes notification to notification-alert MessageBus channel" do
|
|
messages =
|
|
MessageBus.track_publish("/notification-alert/#{evil_trout.id}") do
|
|
PostAlerter.create_notification_alert(
|
|
user: evil_trout,
|
|
post: post,
|
|
notification_type: Notification.types[:mentioned],
|
|
excerpt: "excerpt",
|
|
username: "username",
|
|
)
|
|
end
|
|
|
|
expect(messages.size).to eq(1)
|
|
expect(messages.first.data[:username]).to eq("username")
|
|
expect(messages.first.data[:post_url]).to eq(post.url)
|
|
end
|
|
|
|
let(:modifier_block) do
|
|
Proc.new do |payload|
|
|
payload[:username] = "gotcha"
|
|
payload[:post_url] = "stolen_url"
|
|
payload
|
|
end
|
|
end
|
|
|
|
it "applies the post_alerter_live_notification_payload modifier" do
|
|
plugin_instance = Plugin::Instance.new
|
|
plugin_instance.register_modifier(:post_alerter_live_notification_payload, &modifier_block)
|
|
|
|
messages =
|
|
MessageBus.track_publish("/notification-alert/#{evil_trout.id}") do
|
|
PostAlerter.create_notification_alert(
|
|
user: evil_trout,
|
|
post: post,
|
|
notification_type: Notification.types[:mentioned],
|
|
excerpt: "excerpt",
|
|
username: "username",
|
|
)
|
|
end
|
|
|
|
expect(messages.size).to eq(1)
|
|
expect(messages.first.data[:username]).to eq("gotcha")
|
|
expect(messages.first.data[:post_url]).to eq("stolen_url")
|
|
ensure
|
|
DiscoursePluginRegistry.unregister_modifier(
|
|
plugin_instance,
|
|
:post_alerter_live_notification_payload,
|
|
&modifier_block
|
|
)
|
|
end
|
|
|
|
it "does nothing for suspended users" do
|
|
evil_trout.update_columns(suspended_till: 1.year.from_now)
|
|
|
|
events = nil
|
|
messages =
|
|
MessageBus.track_publish do
|
|
events =
|
|
DiscourseEvent.track_events do
|
|
PostAlerter.create_notification_alert(
|
|
user: evil_trout,
|
|
post: post,
|
|
notification_type: Notification.types[:custom],
|
|
excerpt: "excerpt",
|
|
username: "username",
|
|
)
|
|
end
|
|
end
|
|
|
|
expect(events.size).to eq(0)
|
|
expect(messages.size).to eq(0)
|
|
expect(Jobs::PushNotification.jobs.size).to eq(0)
|
|
end
|
|
|
|
it "does not publish to MessageBus /notification-alert if the user has not been seen for > 30 days, but still sends a push notification" do
|
|
evil_trout.update_columns(last_seen_at: 31.days.ago)
|
|
|
|
SiteSetting.allowed_user_api_push_urls = "https://site2.com/push"
|
|
UserApiKey.create!(
|
|
user_id: evil_trout.id,
|
|
client_id: "xxx#1",
|
|
application_name: "iPhone1",
|
|
scopes: ["notifications"].map { |name| UserApiKeyScope.new(name: name) },
|
|
push_url: "https://site2.com/push",
|
|
)
|
|
|
|
events = nil
|
|
messages =
|
|
MessageBus.track_publish do
|
|
events =
|
|
DiscourseEvent.track_events do
|
|
PostAlerter.create_notification_alert(
|
|
user: evil_trout,
|
|
post: post,
|
|
notification_type: Notification.types[:custom],
|
|
excerpt: "excerpt",
|
|
username: "username",
|
|
)
|
|
end
|
|
end
|
|
|
|
expect(events.map { |event| event[:event_name] }).to include(
|
|
:pre_notification_alert,
|
|
:push_notification,
|
|
:post_notification_alert,
|
|
)
|
|
expect(messages.size).to eq(0)
|
|
expect(Jobs::PushNotification.jobs.size).to eq(1)
|
|
end
|
|
end
|
|
|
|
describe "watching_first_post" do
|
|
fab!(:user)
|
|
fab!(:category)
|
|
fab!(:tag)
|
|
fab!(:topic) { Fabricate(:topic, category: category, tags: [tag]) }
|
|
fab!(:post) { Fabricate(:post, topic: topic) }
|
|
|
|
it "doesn't notify people who aren't watching" do
|
|
PostAlerter.post_created(post)
|
|
expect(
|
|
user.notifications.where(notification_type: Notification.types[:watching_first_post]).count,
|
|
).to eq(0)
|
|
end
|
|
|
|
it "notifies the user who is following the first post category" do
|
|
level = CategoryUser.notification_levels[:watching_first_post]
|
|
CategoryUser.set_notification_level_for_category(user, level, category.id)
|
|
PostAlerter.new.after_save_post(post, true)
|
|
expect(
|
|
user.notifications.where(notification_type: Notification.types[:watching_first_post]).count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "doesn't notify when the record is not new" do
|
|
level = CategoryUser.notification_levels[:watching_first_post]
|
|
CategoryUser.set_notification_level_for_category(user, level, category.id)
|
|
PostAlerter.new.after_save_post(post, false)
|
|
expect(
|
|
user.notifications.where(notification_type: Notification.types[:watching_first_post]).count,
|
|
).to eq(0)
|
|
end
|
|
|
|
it "notifies the user who is following the first post tag" do
|
|
level = TagUser.notification_levels[:watching_first_post]
|
|
TagUser.change(user.id, tag.id, level)
|
|
PostAlerter.post_created(post)
|
|
expect(
|
|
user.notifications.where(notification_type: Notification.types[:watching_first_post]).count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "notifies the user who is following the first post group" do
|
|
GroupUser.create(group_id: group.id, user_id: user.id)
|
|
GroupUser.create(group_id: group.id, user_id: post.user.id)
|
|
topic.topic_allowed_groups.create(group_id: group.id)
|
|
|
|
level = GroupUser.notification_levels[:watching_first_post]
|
|
GroupUser.where(user_id: user.id, group_id: group.id).update_all(notification_level: level)
|
|
|
|
PostAlerter.post_created(post)
|
|
expect(
|
|
user.notifications.where(notification_type: Notification.types[:watching_first_post]).count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "triggers :before_create_notifications_for_users" do
|
|
level = CategoryUser.notification_levels[:watching_first_post]
|
|
CategoryUser.set_notification_level_for_category(user, level, category.id)
|
|
events = DiscourseEvent.track_events { PostAlerter.new.after_save_post(post, true) }
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[user], post],
|
|
)
|
|
end
|
|
|
|
it "sends a push notification when user has a push subscription" do
|
|
setup_push_notification_subscription_for(user: user)
|
|
|
|
level = CategoryUser.notification_levels[:watching_first_post]
|
|
CategoryUser.set_notification_level_for_category(user, level, category.id)
|
|
events =
|
|
DiscourseEvent.track_events(:push_notification) do
|
|
PostAlerter.new.after_save_post(post, true)
|
|
end
|
|
|
|
expect(
|
|
events.detect do |e|
|
|
e[:params][0] == user &&
|
|
e[:params][1][:notification_type] == Notification.types[:watching_first_post]
|
|
end,
|
|
).to be_present
|
|
end
|
|
end
|
|
|
|
context "with replies" do
|
|
it "triggers :before_create_notifications_for_users" do
|
|
_post = Fabricate(:post, user: user, topic: topic)
|
|
reply = Fabricate(:post, topic: topic, reply_to_post_number: 1)
|
|
events = DiscourseEvent.track_events { PostAlerter.post_created(reply) }
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[user], reply],
|
|
)
|
|
end
|
|
|
|
it "notifies about regular reply" do
|
|
_post = Fabricate(:post, user: user, topic: topic)
|
|
|
|
reply = Fabricate(:post, topic: topic, reply_to_post_number: 1)
|
|
PostAlerter.post_created(reply)
|
|
|
|
expect(user.notifications.where(notification_type: Notification.types[:replied]).count).to eq(
|
|
1,
|
|
)
|
|
end
|
|
|
|
it "does not notify admins when suppress_secured_categories_from_admin is enabled" do
|
|
SiteSetting.suppress_secured_categories_from_admin = true
|
|
|
|
topic = Fabricate(:topic, category: private_category)
|
|
post = Fabricate(:post, raw: "hello @#{admin.username} how are you today?", topic: topic)
|
|
|
|
PostAlerter.post_created(post)
|
|
|
|
expect(admin.notifications.count).to eq(0)
|
|
end
|
|
|
|
it "doesn't notify regular user about whispered reply" do
|
|
_post = Fabricate(:post, user: user, topic: topic)
|
|
|
|
whispered_reply =
|
|
Fabricate(
|
|
:post,
|
|
user: admin,
|
|
topic: topic,
|
|
post_type: Post.types[:whisper],
|
|
reply_to_post_number: 1,
|
|
)
|
|
PostAlerter.post_created(whispered_reply)
|
|
|
|
expect(user.notifications.where(notification_type: Notification.types[:replied]).count).to eq(
|
|
0,
|
|
)
|
|
end
|
|
|
|
it "notifies staff user about whispered reply" do
|
|
admin1 = Fabricate(:admin)
|
|
admin2 = Fabricate(:admin)
|
|
|
|
_post = Fabricate(:post, user: user, topic: topic)
|
|
|
|
whispered_reply1 =
|
|
Fabricate(
|
|
:post,
|
|
user: admin1,
|
|
topic: topic,
|
|
post_type: Post.types[:whisper],
|
|
reply_to_post_number: 1,
|
|
)
|
|
whispered_reply2 =
|
|
Fabricate(
|
|
:post,
|
|
user: admin2,
|
|
topic: topic,
|
|
post_type: Post.types[:whisper],
|
|
reply_to_post_number: 2,
|
|
)
|
|
PostAlerter.post_created(whispered_reply1)
|
|
PostAlerter.post_created(whispered_reply2)
|
|
|
|
expect(
|
|
admin1.notifications.where(notification_type: Notification.types[:replied]).count,
|
|
).to eq(1)
|
|
|
|
TopicUser.change(
|
|
admin1.id,
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
|
|
# this should change nothing cause the moderator post has an action code
|
|
# if we have an action code then we should never have notifications, this is rare but
|
|
# assign whispers are like this
|
|
whispered_reply3 =
|
|
topic.add_moderator_post(
|
|
admin2,
|
|
"i am a reply",
|
|
post_type: Post.types[:whisper],
|
|
action_code: "moderator_thing",
|
|
)
|
|
PostAlerter.post_created(whispered_reply3)
|
|
|
|
# if this whisper is not ignored like it should we would see a posted notification and no replied notifications
|
|
notifications = admin1.notifications.where(topic_id: topic.id).to_a
|
|
|
|
expect(notifications.first.notification_type).to eq(Notification.types[:replied])
|
|
expect(notifications.length).to eq(1)
|
|
expect(notifications.first.post_number).to eq(whispered_reply2.post_number)
|
|
end
|
|
|
|
it "sends email notifications only to users not on CC list of incoming email" do
|
|
alice = Fabricate(:user, username: "alice", email: "alice@example.com")
|
|
bob = Fabricate(:user, username: "bob", email: "bob@example.com")
|
|
carol = Fabricate(:user, username: "carol", email: "carol@example.com", staged: true)
|
|
dave = Fabricate(:user, username: "dave", email: "dave@example.com", staged: true)
|
|
erin = Fabricate(:user, username: "erin", email: "erin@example.com")
|
|
|
|
topic =
|
|
Fabricate(
|
|
:private_message_topic,
|
|
topic_allowed_users: [
|
|
Fabricate.build(:topic_allowed_user, user: alice),
|
|
Fabricate.build(:topic_allowed_user, user: bob),
|
|
Fabricate.build(:topic_allowed_user, user: carol),
|
|
Fabricate.build(:topic_allowed_user, user: dave),
|
|
Fabricate.build(:topic_allowed_user, user: erin),
|
|
],
|
|
)
|
|
_post = Fabricate(:post, user: alice, topic: topic)
|
|
|
|
TopicUser.change(
|
|
alice.id,
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
TopicUser.change(
|
|
bob.id,
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
TopicUser.change(
|
|
erin.id,
|
|
topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
|
|
email =
|
|
Fabricate(
|
|
:incoming_email,
|
|
raw: <<~RAW,
|
|
Return-Path: <bob@example.com>
|
|
From: Bob <bob@example.com>
|
|
To: meta+1234@discoursemail.com, dave@example.com
|
|
CC: carol@example.com, erin@example.com
|
|
Subject: Hello world
|
|
Date: Fri, 15 Jan 2016 00:12:43 +0100
|
|
Message-ID: <12345@example.com>
|
|
Mime-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: quoted-printable
|
|
|
|
This post was created by email.
|
|
RAW
|
|
from_address: "bob@example.com",
|
|
to_addresses: "meta+1234@discoursemail.com;dave@example.com",
|
|
cc_addresses: "carol@example.com;erin@example.com",
|
|
)
|
|
reply =
|
|
Fabricate(
|
|
:post_via_email,
|
|
user: bob,
|
|
topic: topic,
|
|
incoming_email: email,
|
|
reply_to_post_number: 1,
|
|
)
|
|
|
|
NotificationEmailer.expects(:process_notification).with { |n| n.user_id == alice.id }.once
|
|
NotificationEmailer.expects(:process_notification).with { |n| n.user_id == bob.id }.never
|
|
NotificationEmailer.expects(:process_notification).with { |n| n.user_id == carol.id }.never
|
|
NotificationEmailer.expects(:process_notification).with { |n| n.user_id == dave.id }.never
|
|
NotificationEmailer.expects(:process_notification).with { |n| n.user_id == erin.id }.never
|
|
|
|
PostAlerter.post_created(reply)
|
|
|
|
expect(alice.notifications.count).to eq(1)
|
|
expect(bob.notifications.count).to eq(0)
|
|
expect(carol.notifications.count).to eq(1)
|
|
expect(dave.notifications.count).to eq(1)
|
|
expect(erin.notifications.count).to eq(1)
|
|
end
|
|
|
|
it "does not send email notifications to staged users when notification originates in mailinglist mirror category" do
|
|
category = Fabricate(:mailinglist_mirror_category)
|
|
topic = Fabricate(:topic, category: category)
|
|
user = Fabricate(:staged)
|
|
_post = Fabricate(:post, user: user, topic: topic)
|
|
reply = Fabricate(:post, topic: topic, reply_to_post_number: 1)
|
|
|
|
NotificationEmailer.expects(:process_notification).never
|
|
expect { PostAlerter.post_created(reply) }.not_to change(user.notifications, :count)
|
|
|
|
category.mailinglist_mirror = false
|
|
NotificationEmailer.expects(:process_notification).once
|
|
expect { PostAlerter.post_created(reply) }.to change(user.notifications, :count).by(1)
|
|
end
|
|
|
|
it "creates a notification of type `replied` instead of `posted` for the topic author if they're watching the topic" do
|
|
Jobs.run_immediately!
|
|
|
|
u1 = Fabricate(:admin)
|
|
u2 = Fabricate(:admin)
|
|
|
|
topic = create_topic(user: u1)
|
|
|
|
u1.notifications.destroy_all
|
|
|
|
expect do create_post(topic: topic, user: u2) end.to change {
|
|
u1.reload.notifications.count
|
|
}.by(1)
|
|
expect(
|
|
u1.notifications.exists?(
|
|
topic_id: topic.id,
|
|
notification_type: Notification.types[:replied],
|
|
post_number: 1,
|
|
read: false,
|
|
),
|
|
).to eq(true)
|
|
end
|
|
|
|
it "it doesn't notify about small action posts when the topic author is watching the topic " do
|
|
Jobs.run_immediately!
|
|
|
|
u1 = Fabricate(:admin)
|
|
u2 = Fabricate(:admin)
|
|
|
|
topic = create_topic(user: u1)
|
|
|
|
u1.notifications.destroy_all
|
|
|
|
expect do topic.update_status("closed", true, u2, message: "hello world") end.not_to change {
|
|
u1.reload.notifications.count
|
|
}
|
|
end
|
|
end
|
|
|
|
context "with category" do
|
|
context "with watching" do
|
|
it "triggers :before_create_notifications_for_users" do
|
|
topic = Fabricate(:topic, category: category)
|
|
post = Fabricate(:post, topic: topic)
|
|
level = CategoryUser.notification_levels[:watching]
|
|
CategoryUser.set_notification_level_for_category(user, level, category.id)
|
|
events = DiscourseEvent.track_events { PostAlerter.post_created(post) }
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[user], post],
|
|
)
|
|
end
|
|
|
|
it "notifies staff about whispered post" do
|
|
topic = Fabricate(:topic, category: category)
|
|
level = CategoryUser.notification_levels[:watching]
|
|
CategoryUser.set_notification_level_for_category(admin, level, category.id)
|
|
CategoryUser.set_notification_level_for_category(user, level, category.id)
|
|
whispered_post =
|
|
Fabricate(:post, user: Fabricate(:admin), topic: topic, post_type: Post.types[:whisper])
|
|
expect { PostAlerter.post_created(whispered_post) }.to add_notification(
|
|
admin,
|
|
:watching_category_or_tag,
|
|
)
|
|
expect { PostAlerter.post_created(whispered_post) }.not_to add_notification(
|
|
user,
|
|
:watching_category_or_tag,
|
|
)
|
|
end
|
|
|
|
it "notifies a staged user about a private post, but only if the user has access" do
|
|
staged_member = Fabricate(:staged)
|
|
staged_non_member = Fabricate(:staged)
|
|
group_member = Fabricate(:user)
|
|
|
|
group.add(group_member)
|
|
group.add(staged_member)
|
|
|
|
level = CategoryUser.notification_levels[:watching]
|
|
CategoryUser.set_notification_level_for_category(group_member, level, private_category.id)
|
|
CategoryUser.set_notification_level_for_category(staged_member, level, private_category.id)
|
|
CategoryUser.set_notification_level_for_category(
|
|
staged_non_member,
|
|
level,
|
|
private_category.id,
|
|
)
|
|
|
|
topic = Fabricate(:topic, category: private_category, user: group_member)
|
|
post = Fabricate(:post, topic: topic)
|
|
|
|
expect { PostAlerter.post_created(post) }.to add_notification(
|
|
staged_member,
|
|
:watching_category_or_tag,
|
|
).and not_add_notification(staged_non_member, :watching_category_or_tag)
|
|
end
|
|
|
|
it "does not update existing unread notification" do
|
|
CategoryUser.set_notification_level_for_category(
|
|
user,
|
|
CategoryUser.notification_levels[:watching],
|
|
category.id,
|
|
)
|
|
topic = Fabricate(:topic, category: category)
|
|
|
|
post = Fabricate(:post, topic: topic)
|
|
PostAlerter.post_created(post)
|
|
notification = Notification.last
|
|
expect(notification.topic_id).to eq(topic.id)
|
|
expect(notification.post_number).to eq(1)
|
|
|
|
post = Fabricate(:post, topic: topic)
|
|
PostAlerter.post_created(post)
|
|
notification = Notification.last
|
|
expect(notification.topic_id).to eq(topic.id)
|
|
expect(notification.post_number).to eq(1)
|
|
notification_data = JSON.parse(notification.data)
|
|
expect(notification_data["display_username"]).to eq(I18n.t("embed.replies", count: 2))
|
|
end
|
|
|
|
it "sends a push notification when user has a push subscription" do
|
|
setup_push_notification_subscription_for(user: user)
|
|
|
|
topic = Fabricate(:topic, category: category)
|
|
post = Fabricate(:post, topic: topic)
|
|
level = CategoryUser.notification_levels[:watching]
|
|
CategoryUser.set_notification_level_for_category(user, level, category.id)
|
|
events = DiscourseEvent.track_events(:push_notification) { PostAlerter.post_created(post) }
|
|
|
|
expect(
|
|
events.detect do |e|
|
|
e[:params][0] == user &&
|
|
e[:params][1][:notification_type] == Notification.types[:watching_category_or_tag]
|
|
end,
|
|
).to be_present
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with tags" do
|
|
context "with watching" do
|
|
it "triggers :before_create_notifications_for_users" do
|
|
tag = Fabricate(:tag)
|
|
topic = Fabricate(:topic, tags: [tag])
|
|
post = Fabricate(:post, topic: topic)
|
|
level = TagUser.notification_levels[:watching]
|
|
TagUser.change(user.id, tag.id, level)
|
|
events = DiscourseEvent.track_events { PostAlerter.post_created(post) }
|
|
expect(events).to include(
|
|
event_name: :before_create_notifications_for_users,
|
|
params: [[user], post],
|
|
)
|
|
end
|
|
|
|
it "does not update existing unread notification" do
|
|
tag = Fabricate(:tag)
|
|
TagUser.change(user.id, tag.id, TagUser.notification_levels[:watching])
|
|
topic = Fabricate(:topic, tags: [tag])
|
|
|
|
post = Fabricate(:post, topic: topic)
|
|
PostAlerter.post_created(post)
|
|
notification = Notification.last
|
|
expect(notification.topic_id).to eq(topic.id)
|
|
expect(notification.post_number).to eq(1)
|
|
|
|
post = Fabricate(:post, topic: topic)
|
|
PostAlerter.post_created(post)
|
|
notification = Notification.last
|
|
expect(notification.topic_id).to eq(topic.id)
|
|
expect(notification.post_number).to eq(1)
|
|
notification_data = JSON.parse(notification.data)
|
|
expect(notification_data["display_username"]).to eq(I18n.t("embed.replies", count: 2))
|
|
end
|
|
|
|
it "does not add notification if user does not belong to tag group with permissions" do
|
|
tag = Fabricate(:tag)
|
|
topic = Fabricate(:topic, tags: [tag])
|
|
post = Fabricate(:post, topic: topic)
|
|
|
|
tag_group = TagGroup.new(name: "Only visible to group", tag_names: [tag.name])
|
|
tag_group.permissions = [[group.id, TagGroupPermission.permission_types[:full]]]
|
|
tag_group.save!
|
|
|
|
TagUser.change(user.id, tag.id, TagUser.notification_levels[:watching])
|
|
|
|
expect { PostAlerter.post_created(post) }.not_to change { Notification.count }
|
|
end
|
|
|
|
it "adds notification if user belongs to tag group with permissions" do
|
|
tag = Fabricate(:tag)
|
|
topic = Fabricate(:topic, tags: [tag])
|
|
post = Fabricate(:post, topic: topic)
|
|
tag_group = Fabricate(:tag_group, tags: [tag])
|
|
Fabricate(:group_user, group: group, user: user)
|
|
Fabricate(:tag_group_permission, tag_group: tag_group, group: group)
|
|
|
|
TagUser.change(user.id, tag.id, TagUser.notification_levels[:watching])
|
|
|
|
expect { PostAlerter.post_created(post) }.to change { Notification.count }.by(1)
|
|
end
|
|
end
|
|
|
|
context "with category and tags" do
|
|
fab!(:muted_category) do
|
|
Fabricate(:category).tap do |category|
|
|
CategoryUser.set_notification_level_for_category(
|
|
user,
|
|
CategoryUser.notification_levels[:muted],
|
|
category.id,
|
|
)
|
|
end
|
|
end
|
|
fab!(:muted_tag) do
|
|
Fabricate(:tag).tap do |tag|
|
|
TagUser.create!(
|
|
user: user,
|
|
tag: tag,
|
|
notification_level: TagUser.notification_levels[:muted],
|
|
)
|
|
end
|
|
end
|
|
fab!(:watched_tag) do
|
|
Fabricate(:tag).tap do |tag|
|
|
TagUser.create!(
|
|
user: user,
|
|
tag: tag,
|
|
notification_level: TagUser.notification_levels[:watching],
|
|
)
|
|
end
|
|
end
|
|
fab!(:topic_with_muted_tag_and_watched_category) do
|
|
Fabricate(:topic, category: category, tags: [muted_tag])
|
|
end
|
|
fab!(:topic_with_muted_category_and_watched_tag) do
|
|
Fabricate(:topic, category: muted_category, tags: [watched_tag])
|
|
end
|
|
fab!(:directly_watched_topic) do
|
|
Fabricate(:topic, category: muted_category, tags: [muted_tag])
|
|
end
|
|
fab!(:topic_user) do
|
|
Fabricate(
|
|
:topic_user,
|
|
topic: directly_watched_topic,
|
|
user: user,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
end
|
|
fab!(:topic_with_watched_category) { Fabricate(:topic, category: category) }
|
|
fab!(:post) { Fabricate(:post, topic: topic_with_muted_tag_and_watched_category) }
|
|
fab!(:post_2) { Fabricate(:post, topic: topic_with_muted_category_and_watched_tag) }
|
|
fab!(:post_3) { Fabricate(:post, topic: topic_with_watched_category) }
|
|
fab!(:post_4) { Fabricate(:post, topic: directly_watched_topic) }
|
|
|
|
before do
|
|
CategoryUser.set_notification_level_for_category(
|
|
user,
|
|
CategoryUser.notification_levels[:watching],
|
|
category.id,
|
|
)
|
|
end
|
|
|
|
it "adds notification when watched_precedence_over_muted setting is true" do
|
|
SiteSetting.watched_precedence_over_muted = true
|
|
expect {
|
|
PostAlerter.post_created(topic_with_muted_tag_and_watched_category.posts.first)
|
|
}.to change { Notification.count }.by(1)
|
|
expect {
|
|
PostAlerter.post_created(topic_with_muted_category_and_watched_tag.posts.first)
|
|
}.to change { Notification.count }.by(1)
|
|
expect { PostAlerter.post_created(directly_watched_topic.posts.first) }.to change {
|
|
Notification.count
|
|
}.by(1)
|
|
end
|
|
|
|
it "respects user option even if watched_precedence_over_muted site setting is true" do
|
|
SiteSetting.watched_precedence_over_muted = true
|
|
user.user_option.update!(watched_precedence_over_muted: false)
|
|
expect {
|
|
PostAlerter.post_created(topic_with_muted_tag_and_watched_category.posts.first)
|
|
}.not_to change { Notification.count }
|
|
expect {
|
|
PostAlerter.post_created(topic_with_muted_category_and_watched_tag.posts.first)
|
|
}.not_to change { Notification.count }
|
|
expect { PostAlerter.post_created(directly_watched_topic.posts.first) }.to change {
|
|
Notification.count
|
|
}.by(1)
|
|
end
|
|
|
|
it "does not add notification when watched_precedence_over_muted setting is false" do
|
|
SiteSetting.watched_precedence_over_muted = false
|
|
expect {
|
|
PostAlerter.post_created(topic_with_muted_tag_and_watched_category.posts.first)
|
|
}.not_to change { Notification.count }
|
|
expect {
|
|
PostAlerter.post_created(topic_with_muted_category_and_watched_tag.posts.first)
|
|
}.not_to change { Notification.count }
|
|
expect { PostAlerter.post_created(topic_with_watched_category.posts.first) }.to change {
|
|
Notification.count
|
|
}.by(1)
|
|
expect { PostAlerter.post_created(directly_watched_topic.posts.first) }.to change {
|
|
Notification.count
|
|
}.by(1)
|
|
end
|
|
|
|
it "respects user option even if watched_precedence_over_muted site setting is false" do
|
|
SiteSetting.watched_precedence_over_muted = false
|
|
user.user_option.update!(watched_precedence_over_muted: true)
|
|
expect {
|
|
PostAlerter.post_created(topic_with_muted_tag_and_watched_category.posts.first)
|
|
}.to change { Notification.count }.by(1)
|
|
expect {
|
|
PostAlerter.post_created(topic_with_muted_category_and_watched_tag.posts.first)
|
|
}.to change { Notification.count }.by(1)
|
|
expect { PostAlerter.post_created(directly_watched_topic.posts.first) }.to change {
|
|
Notification.count
|
|
}.by(1)
|
|
end
|
|
end
|
|
|
|
context "with on change" do
|
|
fab!(:user)
|
|
fab!(:other_tag) { Fabricate(:tag) }
|
|
fab!(:watched_tag) { Fabricate(:tag) }
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
SiteSetting.tag_topic_allowed_groups = Group::AUTO_GROUPS[:trust_level_0]
|
|
Jobs.run_immediately!
|
|
TagUser.change(user.id, watched_tag.id, TagUser.notification_levels[:watching_first_post])
|
|
TopicUser.change(
|
|
Fabricate(:user).id,
|
|
post.topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
end
|
|
|
|
it "triggers a notification" do
|
|
expect(
|
|
user
|
|
.notifications
|
|
.where(notification_type: Notification.types[:watching_first_post])
|
|
.count,
|
|
).to eq(0)
|
|
|
|
expect {
|
|
PostRevisor.new(post).revise!(
|
|
Fabricate(:user, refresh_auto_groups: true),
|
|
tags: [other_tag.name, watched_tag.name],
|
|
)
|
|
}.to change { Notification.where(user_id: user.id).count }.by(1)
|
|
expect(
|
|
user
|
|
.notifications
|
|
.where(notification_type: Notification.types[:watching_first_post])
|
|
.count,
|
|
).to eq(1)
|
|
|
|
expect {
|
|
PostRevisor.new(post).revise!(
|
|
Fabricate(:user, refresh_auto_groups: true),
|
|
tags: [watched_tag.name, other_tag.name],
|
|
)
|
|
}.not_to change { Notification.count }
|
|
expect(
|
|
user
|
|
.notifications
|
|
.where(notification_type: Notification.types[:watching_first_post])
|
|
.count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "doesn't trigger a notification if topic is unlisted" do
|
|
post.topic.update_column(:visible, false)
|
|
|
|
expect(
|
|
user
|
|
.notifications
|
|
.where(notification_type: Notification.types[:watching_first_post])
|
|
.count,
|
|
).to eq(0)
|
|
|
|
PostRevisor.new(post).revise!(Fabricate(:user), tags: [other_tag.name, watched_tag.name])
|
|
expect(
|
|
user
|
|
.notifications
|
|
.where(notification_type: Notification.types[:watching_first_post])
|
|
.count,
|
|
).to eq(0)
|
|
end
|
|
end
|
|
|
|
context "with private message" do
|
|
fab!(:post) { Fabricate(:private_message_post) }
|
|
fab!(:other_tag) { Fabricate(:tag) }
|
|
fab!(:other_tag2) { Fabricate(:tag) }
|
|
fab!(:other_tag3) { Fabricate(:tag) }
|
|
fab!(:user)
|
|
fab!(:staged)
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
SiteSetting.pm_tags_allowed_for_groups = "1|2|3"
|
|
Jobs.run_immediately!
|
|
TopicUser.change(
|
|
user.id,
|
|
post.topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
TopicUser.change(
|
|
staged.id,
|
|
post.topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
TopicUser.change(
|
|
admin.id,
|
|
post.topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
)
|
|
TagUser.change(staged.id, other_tag.id, TagUser.notification_levels[:watching])
|
|
TagUser.change(admin.id, other_tag3.id, TagUser.notification_levels[:watching])
|
|
post.topic.allowed_users << user
|
|
post.topic.allowed_users << staged
|
|
end
|
|
|
|
it "only notifies staff watching added tag" do
|
|
expect(
|
|
PostRevisor.new(post).revise!(
|
|
Fabricate(:admin, refresh_auto_groups: true),
|
|
tags: [other_tag.name],
|
|
),
|
|
).to be true
|
|
expect(Notification.where(user_id: staged.id).count).to eq(0)
|
|
expect(
|
|
PostRevisor.new(post).revise!(
|
|
Fabricate(:admin, refresh_auto_groups: true),
|
|
tags: [other_tag2.name],
|
|
),
|
|
).to be true
|
|
expect(Notification.where(user_id: admin.id).count).to eq(0)
|
|
expect(
|
|
PostRevisor.new(post).revise!(
|
|
Fabricate(:admin, refresh_auto_groups: true),
|
|
tags: [other_tag3.name],
|
|
),
|
|
).to be true
|
|
expect(Notification.where(user_id: admin.id).count).to eq(1)
|
|
end
|
|
end
|
|
|
|
context "with tag groups" do
|
|
fab!(:tag)
|
|
fab!(:user)
|
|
fab!(:topic) { Fabricate(:topic, tags: [tag]) }
|
|
fab!(:post) { Fabricate(:post, topic: topic) }
|
|
|
|
shared_examples "tag user with notification level" do |notification_level, notification_type|
|
|
it "notifies a user who is watching a tag that does not belong to a tag group" do
|
|
TagUser.change(user.id, tag.id, TagUser.notification_levels[notification_level])
|
|
PostAlerter.post_created(post)
|
|
expect(
|
|
user
|
|
.notifications
|
|
.where(notification_type: Notification.types[notification_type])
|
|
.count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "does not notify a user watching a tag with tag group permissions that he does not belong to" do
|
|
tag_group = Fabricate(:tag_group, tags: [tag], permissions: { group.name => 1 })
|
|
|
|
TagUser.change(user.id, tag.id, TagUser.notification_levels[notification_level])
|
|
|
|
PostAlerter.post_created(post)
|
|
|
|
expect(
|
|
user
|
|
.notifications
|
|
.where(notification_type: Notification.types[notification_type])
|
|
.count,
|
|
).to eq(0)
|
|
end
|
|
|
|
it "notifies a user watching a tag with tag group permissions that he belongs to" do
|
|
Fabricate(:group_user, group: group, user: user)
|
|
|
|
TagUser.change(user.id, tag.id, TagUser.notification_levels[notification_level])
|
|
|
|
PostAlerter.post_created(post)
|
|
|
|
expect(
|
|
user
|
|
.notifications
|
|
.where(notification_type: Notification.types[notification_type])
|
|
.count,
|
|
).to eq(1)
|
|
end
|
|
|
|
it "notifies a staff watching a tag with tag group permissions that he does not belong to" do
|
|
tag_group = Fabricate(:tag_group, tags: [tag])
|
|
Fabricate(:tag_group_permission, tag_group: tag_group, group: group)
|
|
staff_group = Group.find(Group::AUTO_GROUPS[:staff])
|
|
Fabricate(:group_user, group: staff_group, user: user)
|
|
|
|
TagUser.change(user.id, tag.id, TagUser.notification_levels[notification_level])
|
|
|
|
PostAlerter.post_created(post)
|
|
|
|
expect(
|
|
user
|
|
.notifications
|
|
.where(notification_type: Notification.types[notification_type])
|
|
.count,
|
|
).to eq(1)
|
|
end
|
|
end
|
|
|
|
context "with :watching notification level" do
|
|
include_examples "tag user with notification level", :watching, :watching_category_or_tag
|
|
end
|
|
|
|
context "with :watching_first_post notification level" do
|
|
include_examples "tag user with notification level",
|
|
:watching_first_post,
|
|
:watching_first_post
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#extract_linked_users" do
|
|
fab!(:post) { Fabricate(:post, topic: topic) }
|
|
fab!(:post2) { Fabricate(:post) }
|
|
|
|
describe "when linked post has been deleted" do
|
|
let(:topic_link) do
|
|
TopicLink.create!(
|
|
url: "/t/#{topic.id}",
|
|
topic_id: topic.id,
|
|
link_topic_id: post2.topic.id,
|
|
link_post_id: nil,
|
|
post_id: post.id,
|
|
user: user,
|
|
domain: "test.com",
|
|
)
|
|
end
|
|
|
|
it "should use the first post of the topic" do
|
|
topic_link
|
|
expect(PostAlerter.new.extract_linked_users(post.reload)).to eq([post2.user])
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#notify_post_users" do
|
|
fab!(:post) { Fabricate(:post, topic: topic) }
|
|
fab!(:last_editor) { Fabricate(:user) }
|
|
fab!(:tag)
|
|
fab!(:category)
|
|
|
|
it "creates single edit notification when post is modified" do
|
|
TopicUser.create!(
|
|
user_id: user.id,
|
|
topic_id: topic.id,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
last_read_post_number: post.post_number,
|
|
)
|
|
|
|
PostRevisor.new(post).revise!(last_editor, tags: [tag.name])
|
|
PostAlerter.new.notify_post_users(post, [])
|
|
expect(Notification.count).to eq(1)
|
|
expect(Notification.last.notification_type).to eq(Notification.types[:edited])
|
|
expect(JSON.parse(Notification.last.data)["display_username"]).to eq(last_editor.username)
|
|
|
|
PostAlerter.new.notify_post_users(post, [])
|
|
expect(Notification.count).to eq(1)
|
|
end
|
|
|
|
it "creates posted notification when Sidekiq is slow" do
|
|
CategoryUser.set_notification_level_for_category(
|
|
user,
|
|
CategoryUser.notification_levels[:watching],
|
|
category.id,
|
|
)
|
|
|
|
post =
|
|
PostCreator.create!(
|
|
Fabricate(:user, refresh_auto_groups: true),
|
|
title: "one of my first topics",
|
|
raw: "one of my first posts",
|
|
category: category.id,
|
|
)
|
|
|
|
TopicUser.change(user, post.topic_id, last_read_post_number: post.post_number)
|
|
|
|
# Manually run job after the user read the topic to simulate a slow
|
|
# Sidekiq.
|
|
job_args = Jobs::PostAlert.jobs[0]["args"][0]
|
|
expect { Jobs::PostAlert.new.execute(job_args.with_indifferent_access) }.to change {
|
|
Notification.count
|
|
}.by(1)
|
|
|
|
expect(Notification.last.notification_type).to eq(Notification.types[:posted])
|
|
end
|
|
end
|
|
|
|
context "with SMTP (group_smtp_email)" do
|
|
before do
|
|
SiteSetting.enable_smtp = true
|
|
SiteSetting.email_in = true
|
|
Jobs.run_immediately!
|
|
end
|
|
|
|
fab!(:group) do
|
|
Fabricate(
|
|
:group,
|
|
smtp_server: "smtp.gmail.com",
|
|
smtp_port: 587,
|
|
smtp_ssl_mode: Group.smtp_ssl_modes[:starttls],
|
|
imap_server: "imap.gmail.com",
|
|
imap_port: 993,
|
|
imap_ssl: true,
|
|
email_username: "discourse@example.com",
|
|
email_password: "password",
|
|
smtp_enabled: true,
|
|
imap_enabled: true,
|
|
)
|
|
end
|
|
|
|
def create_post_with_incoming
|
|
raw_mail = <<~EMAIL
|
|
From: Foo <foo@discourse.org>
|
|
To: discourse@example.com
|
|
Cc: bar@discourse.org, jim@othersite.com
|
|
Subject: Full email group username flow
|
|
Date: Fri, 15 Jan 2021 00:12:43 +0100
|
|
Message-ID: <u4w8c9r4y984yh98r3h69873@example.com.mail>
|
|
Mime-Version: 1.0
|
|
Content-Type: text/plain
|
|
Content-Transfer-Encoding: 7bit
|
|
|
|
This is the first email.
|
|
EMAIL
|
|
|
|
Email::Receiver.new(raw_mail, {}).process!
|
|
end
|
|
|
|
it "does not error if SMTP is enabled and the topic has no incoming email or allowed groups" do
|
|
expect { PostAlerter.new.after_save_post(post, true) }.not_to raise_error
|
|
end
|
|
|
|
it "does not error if SMTP is enabled and the topic has no incoming email but does have an allowed group" do
|
|
TopicAllowedGroup.create(topic: private_message_topic, group: group)
|
|
expect { PostAlerter.new.after_save_post(post, true) }.not_to raise_error
|
|
end
|
|
|
|
it "does not error if SMTP is enabled and the topic has no incoming email but has multiple allowed groups" do
|
|
TopicAllowedGroup.create(topic: private_message_topic, group: group)
|
|
TopicAllowedGroup.create(topic: private_message_topic, group: Fabricate(:group))
|
|
expect { PostAlerter.new.after_save_post(post, true) }.not_to raise_error
|
|
end
|
|
|
|
it "sends a group smtp email because SMTP is enabled for the site and the group" do
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
post = Fabricate(:post, topic: topic)
|
|
expect { PostAlerter.new.after_save_post(post, true) }.to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}.by(1)
|
|
email = ActionMailer::Base.deliveries.last
|
|
expect(email.from).to include(group.email_username)
|
|
expect(email.to).to contain_exactly(
|
|
topic.reload.topic_allowed_users.order(:created_at).first.user.email,
|
|
)
|
|
expect(email.cc).to match_array(%w[bar@discourse.org jim@othersite.com])
|
|
expect(email.subject).to eq("Re: #{topic.title}")
|
|
end
|
|
|
|
it "sends a group smtp email when the original group has had SMTP disabled and there is an additional topic allowed group" do
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
other_allowed_group = Fabricate(:smtp_group)
|
|
TopicAllowedGroup.create(group: other_allowed_group, topic: topic)
|
|
post = Fabricate(:post, topic: topic)
|
|
group.update!(smtp_enabled: false)
|
|
|
|
expect { PostAlerter.new.after_save_post(post, true) }.to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}.by(1)
|
|
|
|
email = ActionMailer::Base.deliveries.last
|
|
expect(email.from).to include(other_allowed_group.email_username)
|
|
expect(email.to).to contain_exactly(
|
|
topic.reload.topic_allowed_users.order(:created_at).first.user.email,
|
|
)
|
|
expect(email.cc).to match_array(%w[bar@discourse.org jim@othersite.com])
|
|
expect(email.subject).to eq("Re: #{topic.title}")
|
|
end
|
|
|
|
it "does not send a group smtp email if smtp is not enabled for the group" do
|
|
group.update!(smtp_enabled: false)
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
post = Fabricate(:post, topic: topic)
|
|
expect { PostAlerter.new.after_save_post(post, true) }.not_to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}
|
|
end
|
|
|
|
it "does not send a group smtp email if SiteSetting.enable_smtp is false" do
|
|
SiteSetting.enable_smtp = false
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
post = Fabricate(:post, topic: topic)
|
|
expect { PostAlerter.new.after_save_post(post, true) }.not_to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}
|
|
end
|
|
|
|
it "does not send group smtp emails for a whisper" do
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
post = Fabricate(:post, topic: topic, post_type: Post.types[:whisper])
|
|
expect { PostAlerter.new.after_save_post(post, true) }.not_to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}
|
|
end
|
|
|
|
it "sends the group smtp email job with a delay of personal_email_time_window_seconds" do
|
|
freeze_time
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
post = Fabricate(:post, topic: topic)
|
|
PostAlerter.new.after_save_post(post, true)
|
|
job_enqueued?(
|
|
job: :group_smtp_email,
|
|
args: {
|
|
group_id: group.id,
|
|
post_id: post.id,
|
|
email: topic.reload.topic_allowed_users.order(:created_at).first.user.email,
|
|
cc_emails: %w[bar@discourse.org jim@othersite.com],
|
|
},
|
|
at: Time.zone.now + SiteSetting.personal_email_time_window_seconds.seconds,
|
|
)
|
|
end
|
|
|
|
it "does not send a group smtp email for anyone if the reply post originates from an incoming email that is auto generated" do
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
post = Fabricate(:post, topic: topic)
|
|
Fabricate(:incoming_email, post: post, topic: topic, is_auto_generated: true)
|
|
expect_not_enqueued_with(job: :group_smtp_email) do
|
|
expect { PostAlerter.new.after_save_post(post, true) }.not_to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}
|
|
end
|
|
end
|
|
|
|
it "skips sending a notification email to the group and all other email addresses that are _not_ members of the group,
|
|
sends a group_smtp_email instead" do
|
|
NotificationEmailer.enable
|
|
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
|
|
group_user1 = Fabricate(:group_user, group: group)
|
|
group_user2 = Fabricate(:group_user, group: group)
|
|
TopicUser.create(
|
|
user: group_user1.user,
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
topic: topic,
|
|
)
|
|
post = Fabricate(:post, topic: topic.reload)
|
|
|
|
# Sends an email for:
|
|
#
|
|
# 1. the group user that is watching the post (but does not send this email with group SMTO)
|
|
# 2. the group smtp email to notify all topic_users not in the group
|
|
expect { PostAlerter.new.after_save_post(post, true) }.to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}.by(2).and change { Notification.count }.by(2)
|
|
|
|
# The group smtp email
|
|
email = ActionMailer::Base.deliveries.first
|
|
expect(email.from).to eq([group.email_username])
|
|
expect(email.to).to contain_exactly("foo@discourse.org")
|
|
expect(email.cc).to match_array(%w[bar@discourse.org jim@othersite.com])
|
|
expect(email.subject).to eq("Re: #{topic.title}")
|
|
|
|
# The watching group user notification email
|
|
email = ActionMailer::Base.deliveries.last
|
|
expect(email.from).to eq([SiteSetting.notification_email])
|
|
expect(email.to).to contain_exactly(group_user1.user.email)
|
|
expect(email.cc).to eq(nil)
|
|
expect(email.subject).to eq("[Discourse] [PM] #{topic.title}")
|
|
end
|
|
|
|
it "skips sending a notification email to the cc address that was added on the same post with an incoming email" do
|
|
NotificationEmailer.enable
|
|
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
|
|
post = Fabricate(:post, topic: topic.reload)
|
|
expect { PostAlerter.new.after_save_post(post, true) }.to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}.by(1).and change { Notification.count }.by(1)
|
|
email = ActionMailer::Base.deliveries.last
|
|
|
|
# the reply post from someone who was emailed
|
|
reply_raw_mail = <<~EMAIL
|
|
From: Bar <bar@discourse.org>
|
|
To: discourse@example.com
|
|
Cc: someothernewcc@baz.com, finalnewcc@doom.com
|
|
Subject: #{email.subject}
|
|
Date: Fri, 16 Jan 2021 00:12:43 +0100
|
|
Message-ID: <sdugj3o4iyu4832x3487@discourse.org.mail>
|
|
In-Reply-To: #{email.message_id}
|
|
Mime-Version: 1.0
|
|
Content-Type: text/plain
|
|
Content-Transfer-Encoding: 7bit
|
|
|
|
Hey here is my reply!
|
|
EMAIL
|
|
|
|
reply_post_from_email = nil
|
|
expect {
|
|
reply_post_from_email = Email::Receiver.new(reply_raw_mail, {}).process!
|
|
}.to change {
|
|
User.count # the two new cc addresses have users created
|
|
}.by(2).and change {
|
|
TopicAllowedUser.where(topic: topic).count # and they are added as topic allowed users
|
|
}.by(2).and change {
|
|
# but they are not sent emails because they were cc'd on an email, only jim@othersite.com
|
|
# is emailed because he is a topic allowed user cc'd on the _original_ email and he is not
|
|
# the one creating the post, and foo@discourse.org, who is the OP of the topic
|
|
ActionMailer::Base.deliveries.size
|
|
}.by(1).and change {
|
|
Notification.count # and they are still sent their normal discourse notification
|
|
}.by(2)
|
|
|
|
email = ActionMailer::Base.deliveries.last
|
|
|
|
expect(email.to).to eq(["foo@discourse.org"])
|
|
expect(email.cc).to eq(["jim@othersite.com"])
|
|
expect(email.from).to eq([group.email_username])
|
|
expect(email.subject).to eq("Re: #{topic.title}")
|
|
end
|
|
|
|
it "handles the OP of the topic replying by email and sends a group email to the other topic allowed users successfully" do
|
|
NotificationEmailer.enable
|
|
|
|
incoming_email_post = create_post_with_incoming
|
|
topic = incoming_email_post.topic
|
|
post = Fabricate(:post, topic: topic.reload)
|
|
expect { PostAlerter.new.after_save_post(post, true) }.to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}.by(1).and change { Notification.count }.by(1)
|
|
email = ActionMailer::Base.deliveries.last
|
|
|
|
# the reply post from someone who was emailed
|
|
reply_raw_mail = <<~EMAIL
|
|
From: Foo <foo@discourse.org>
|
|
To: discourse@example.com
|
|
Cc: someothernewcc@baz.com, finalnewcc@doom.com
|
|
Subject: #{email.subject}
|
|
Date: Fri, 16 Jan 2021 00:12:43 +0100
|
|
Message-ID: <sgk094238uc0348c334483@discourse.org.mail>
|
|
In-Reply-To: #{email.message_id}
|
|
Mime-Version: 1.0
|
|
Content-Type: text/plain
|
|
Content-Transfer-Encoding: 7bit
|
|
|
|
I am ~~Commander Shepherd~~ the OP and I approve of this message.
|
|
EMAIL
|
|
|
|
reply_post_from_email = nil
|
|
expect {
|
|
reply_post_from_email = Email::Receiver.new(reply_raw_mail, {}).process!
|
|
}.to change {
|
|
User.count # the two new cc addresses have users created
|
|
}.by(2).and change {
|
|
TopicAllowedUser.where(topic: topic).count # and they are added as topic allowed users
|
|
}.by(2).and change {
|
|
# but they are not sent emails because they were cc'd on an email, only jim@othersite.com
|
|
# is emailed because he is a topic allowed user cc'd on the _original_ email and he is not
|
|
# the one creating the post
|
|
ActionMailer::Base.deliveries.size
|
|
}.by(1).and change {
|
|
Notification.count # and they are still sent their normal discourse notification
|
|
}.by(2)
|
|
|
|
email = ActionMailer::Base.deliveries.last
|
|
|
|
expect(email.to).to eq(["bar@discourse.org"])
|
|
expect(email.cc).to eq(["jim@othersite.com"])
|
|
expect(email.from).to eq([group.email_username])
|
|
expect(email.subject).to eq("Re: #{topic.title}")
|
|
end
|
|
|
|
it "handles the OP of the topic replying by email and cc'ing new people, and does not send a group SMTP email to those newly cc'd users" do
|
|
NotificationEmailer.enable
|
|
|
|
# this is a special case where we are not CC'ing on the original email,
|
|
# only on the follow up email
|
|
raw_mail = <<~EMAIL
|
|
From: Foo <foo@discourse.org>
|
|
To: discourse@example.com
|
|
Subject: Full email group username flow
|
|
Date: Fri, 14 Jan 2021 00:12:43 +0100
|
|
Message-ID: <f4832ujfc3498u398i3@example.com.mail>
|
|
Mime-Version: 1.0
|
|
Content-Type: text/plain
|
|
Content-Transfer-Encoding: 7bit
|
|
|
|
This is the first email.
|
|
EMAIL
|
|
|
|
incoming_email_post = Email::Receiver.new(raw_mail, {}).process!
|
|
topic = incoming_email_post.topic
|
|
post = Fabricate(:post, topic: topic.reload)
|
|
expect { PostAlerter.new.after_save_post(post, true) }.to change {
|
|
ActionMailer::Base.deliveries.size
|
|
}.by(1).and change { Notification.count }.by(1)
|
|
email = ActionMailer::Base.deliveries.last
|
|
|
|
# the reply post from the OP, cc'ing new people in
|
|
reply_raw_mail = <<~EMAIL
|
|
From: Foo <foo@discourse.org>
|
|
To: discourse@example.com
|
|
Cc: someothernewcc@baz.com, finalnewcc@doom.com
|
|
Subject: #{email.subject}
|
|
Date: Fri, 16 Jan 2021 00:12:43 +0100
|
|
Message-ID: <3849cu9843yncr9834yr9348x934@discourse.org.mail>
|
|
In-Reply-To: #{email.message_id}
|
|
Mime-Version: 1.0
|
|
Content-Type: text/plain
|
|
Content-Transfer-Encoding: 7bit
|
|
|
|
I am inviting my mates to this email party.
|
|
EMAIL
|
|
|
|
reply_post_from_email = nil
|
|
expect {
|
|
reply_post_from_email = Email::Receiver.new(reply_raw_mail, {}).process!
|
|
}.to change {
|
|
User.count # the two new cc addresses have users created
|
|
}.by(2).and change {
|
|
TopicAllowedUser.where(topic: topic).count # and they are added as topic allowed users
|
|
}.by(2).and not_change {
|
|
# but they are not sent emails because they were cc'd on an email.
|
|
# no group smtp message is sent because the OP is not sent an email,
|
|
# they made this post.
|
|
ActionMailer::Base.deliveries.size
|
|
}.and change {
|
|
Notification.count # and they are still sent their normal discourse notification
|
|
}.by(2)
|
|
|
|
last_email = ActionMailer::Base.deliveries.last
|
|
expect(email).to eq(last_email)
|
|
end
|
|
end
|
|
|
|
describe "storing custom data" do
|
|
let(:custom_data) { "custom_string" }
|
|
|
|
it "stores custom data inside a notification" do
|
|
PostAlerter.new.create_notification(
|
|
admin,
|
|
Notification.types[:liked],
|
|
post,
|
|
custom_data: {
|
|
custom_key: custom_data,
|
|
},
|
|
)
|
|
|
|
liked_notification = Notification.where(notification_type: Notification.types[:liked]).last
|
|
|
|
expect(liked_notification.data_hash[:custom_key]).to eq(custom_data)
|
|
end
|
|
end
|
|
|
|
it "does not create notifications for PMs if not invited" do
|
|
SiteSetting.pm_tags_allowed_for_groups = "#{Group::AUTO_GROUPS[:everyone]}"
|
|
watching_first_post_tag = Fabricate(:tag)
|
|
TagUser.change(
|
|
admin.id,
|
|
watching_first_post_tag.id,
|
|
TagUser.notification_levels[:watching_first_post],
|
|
)
|
|
watching_tag = Fabricate(:tag)
|
|
TagUser.change(admin.id, watching_tag.id, TagUser.notification_levels[:watching])
|
|
|
|
post =
|
|
create_post(
|
|
tags: [watching_first_post_tag.name, watching_tag.name],
|
|
archetype: Archetype.private_message,
|
|
target_usernames: "#{evil_trout.username}",
|
|
)
|
|
expect { PostAlerter.new.after_save_post(post, true) }.to change { Notification.count }.by(1)
|
|
|
|
notification = Notification.last
|
|
expect(notification.user).to eq(evil_trout)
|
|
expect(notification.notification_type).to eq(Notification.types[:private_message])
|
|
expect(notification.topic).to eq(post.topic)
|
|
expect(notification.post_number).to eq(1)
|
|
end
|
|
|
|
it "does not create multiple notifications for same post" do
|
|
category = Fabricate(:category)
|
|
CategoryUser.set_notification_level_for_category(
|
|
user,
|
|
NotificationLevels.all[:tracking],
|
|
category.id,
|
|
)
|
|
watching_first_post_tag = Fabricate(:tag)
|
|
TagUser.change(
|
|
user.id,
|
|
watching_first_post_tag.id,
|
|
TagUser.notification_levels[:watching_first_post],
|
|
)
|
|
watching_tag = Fabricate(:tag)
|
|
TagUser.change(user.id, watching_tag.id, TagUser.notification_levels[:watching])
|
|
|
|
post = create_post(category: category, tags: [watching_first_post_tag.name, watching_tag.name])
|
|
expect { PostAlerter.new.after_save_post(post, true) }.to change { Notification.count }.by(1)
|
|
|
|
notification = Notification.last
|
|
expect(notification.user).to eq(user)
|
|
expect(notification.notification_type).to eq(Notification.types[:watching_category_or_tag])
|
|
expect(notification.topic).to eq(post.topic)
|
|
expect(notification.post_number).to eq(1)
|
|
end
|
|
|
|
it "triggers all discourse events" do
|
|
expected_events = %i[
|
|
post_alerter_before_mentions
|
|
post_alerter_before_replies
|
|
post_alerter_before_quotes
|
|
post_alerter_before_linked
|
|
post_alerter_before_post
|
|
post_alerter_before_first_post
|
|
post_alerter_after_save_post
|
|
]
|
|
|
|
events = DiscourseEvent.track_events { PostAlerter.new.after_save_post(post, true) }
|
|
|
|
# Expect all the notification events are called
|
|
# There are some other events triggered from outside after_save_post
|
|
expect(events.map { |e| e[:event_name] }).to include(*expected_events)
|
|
|
|
# Expect each notification event is called with the right parameters
|
|
events.each do |event|
|
|
if expected_events.include?(event[:event_name])
|
|
expect(event[:params]).to eq([post, true, [post.user]])
|
|
end
|
|
end
|
|
end
|
|
end
|