mirror of
https://github.com/discourse/discourse.git
synced 2024-11-30 11:15:14 +08:00
43ef44127c
Related toaeee7ed
. Before the change inaeee7ed
, notifications for direct replies to your posts and notifications for replies in watched topics looked the same in the notifications menu -- they both used the arrow icon. We decided inaeee7ed
to distinguish them by changing "watched topics" notifications to use the bell icon because it was confusing for users who watch topics to see the same icon for direct replies and "watched topics". However, that change also means that non-power/new users who receive replies to topics _they create_ will get notifications with the bell icon because technically they're watching the topic, but the arrow icon is more appropriate for this case because we use it throughout the app to indicate "replies". This commit adds a special-case so that if a user is watching a topic AND the topic is created by them, they receive notifications with the arrow icon (type `replied`) instead of the bell icon (type `posted`) for new posts in the topic. Internal topic: t/79051.
2095 lines
83 KiB
Ruby
2095 lines
83 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) { Fabricate(:category) }
|
|
|
|
fab!(:topic) { Fabricate(:topic) }
|
|
fab!(:post) { Fabricate(:post) }
|
|
|
|
fab!(:private_message_topic) { Fabricate(: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) { Fabricate(:group) }
|
|
|
|
fab!(:admin) { Fabricate(:admin) }
|
|
fab!(:evil_trout) { Fabricate(:evil_trout) }
|
|
fab!(:coding_horror) { Fabricate(:coding_horror) }
|
|
fab!(:walterwhite) { Fabricate(:walter_white) }
|
|
fab!(:user) { Fabricate(:user) }
|
|
fab!(:tl2_user) { Fabricate(:user, trust_level: TrustLevel[2]) }
|
|
|
|
def create_post_with_alerts(args = {})
|
|
post = Fabricate(:post, args)
|
|
PostAlerter.post_created(post)
|
|
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).pluck_first(: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
|
|
|
|
context "with group inboxes" do
|
|
fab!(:user1) { Fabricate(:user) }
|
|
fab!(:user2) { Fabricate(:user) }
|
|
fab!(:group) { Fabricate(:group, users: [user2], name: "TestGroup", default_notification_level: 2) }
|
|
fab!(:pm) { Fabricate(:topic, archetype: 'private_message', category_id: nil, allowed_groups: [group]) }
|
|
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 do
|
|
PostAlerter.post_created(op)
|
|
end
|
|
|
|
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}") do
|
|
PostAlerter.post_created(another_post)
|
|
end.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
|
|
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) { Fabricate(: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 do
|
|
PostAlerter.post_created(post)
|
|
end
|
|
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 do
|
|
Jobs.run_immediately!
|
|
end
|
|
|
|
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 do
|
|
linking_post
|
|
end
|
|
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)
|
|
group_member = Fabricate(:user)
|
|
group.add(group_member)
|
|
|
|
private_category = Fabricate(
|
|
:private_category, group: group,
|
|
email_in: 'test@test.com', email_in_allow_strangers: true
|
|
)
|
|
|
|
staged_user_post = create_post(user: staged_user, category: private_category)
|
|
|
|
linking = 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) { create_post_with_alerts(raw: "Hello @here how are you?", user: tl2_user, topic: topic) }
|
|
fab!(:other_post) { Fabricate(:post, topic: topic) }
|
|
|
|
before do
|
|
Jobs.run_immediately!
|
|
end
|
|
|
|
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) { Fabricate(:group, name: 'group', mentionable_level: Group::ALIAS_LEVELS[:everyone]) }
|
|
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 do
|
|
post
|
|
end
|
|
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 do
|
|
Jobs.run_immediately!
|
|
end
|
|
|
|
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 do
|
|
mention_post
|
|
end
|
|
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) { Fabricate(:group, name: 'group', mentionable_level: Group::ALIAS_LEVELS[:everyone]) }
|
|
|
|
before do
|
|
group.bulk_add([alice.id, eve.id])
|
|
end
|
|
|
|
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) }
|
|
|
|
[:watching, :tracking, :regular].each do |notification_level|
|
|
context "when notification level is '#{notification_level}'" do
|
|
before do
|
|
set_topic_notification_level(alice, topic, notification_level)
|
|
end
|
|
|
|
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 do
|
|
set_topic_notification_level(alice, topic, :muted)
|
|
end
|
|
|
|
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
|
|
[:watching, :tracking, :regular].each do |notification_level|
|
|
context "when notification level is '#{notification_level}'" do
|
|
before do
|
|
set_topic_notification_level(alice, pm_topic, notification_level)
|
|
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, :mentioned)
|
|
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, :mentioned)
|
|
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 do
|
|
set_topic_notification_level(alice, pm_topic, :muted)
|
|
end
|
|
|
|
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) { Fabricate(:group, name: 'some_group', mentionable_level: Group::ALIAS_LEVELS[:everyone]) }
|
|
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 do
|
|
some_group.bulk_add([alice.id, carol.id])
|
|
end
|
|
|
|
context "when group is part of conversation" do
|
|
[:watching, :tracking, :regular].each do |notification_level|
|
|
context "when notification level is '#{notification_level}'" do
|
|
before do
|
|
set_topic_notification_level(alice, pm_topic, notification_level)
|
|
end
|
|
|
|
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 do
|
|
set_topic_notification_level(alice, pm_topic, :muted)
|
|
end
|
|
|
|
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 do
|
|
PostAlerter.new.create_notification(user, type, 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).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, {})
|
|
end
|
|
expect(events).to include(event_name: :before_create_notification, params: [user, type, post, {}])
|
|
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"
|
|
2.times do |i|
|
|
UserApiKey.create!(user_id: evil_trout.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
|
|
|
|
describe "DiscoursePluginRegistry#push_notification_filters" do
|
|
it "sends push notifications when all filters pass" do
|
|
Plugin::Instance.new.register_push_notification_filter do |user, payload|
|
|
true
|
|
end
|
|
|
|
expect { mention_post }.to change { Jobs::PushNotification.jobs.count }.by(1)
|
|
DiscoursePluginRegistry.reset!
|
|
end
|
|
|
|
it "does not send push notifications when a filters returns false" do
|
|
Plugin::Instance.new.register_push_notification_filter do |user, payload|
|
|
false
|
|
end
|
|
expect { mention_post }.not_to change { Jobs::PushNotification.jobs.count }
|
|
DiscoursePluginRegistry.reset!
|
|
end
|
|
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 }
|
|
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 }
|
|
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
|
|
end
|
|
|
|
describe ".create_notification_alert" do
|
|
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.size).to eq(2)
|
|
expect(messages.size).to eq(0)
|
|
expect(Jobs::PushNotification.jobs.size).to eq(1)
|
|
end
|
|
end
|
|
|
|
describe "watching_first_post" do
|
|
fab!(:user) { Fabricate(:user) }
|
|
fab!(:category) { Fabricate(:category) }
|
|
fab!(:tag) { Fabricate(: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 do
|
|
PostAlerter.new.after_save_post(post, true)
|
|
end
|
|
expect(events).to include(event_name: :before_create_notifications_for_users, params: [[user], post])
|
|
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 do
|
|
PostAlerter.post_created(reply)
|
|
end
|
|
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 "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
|
|
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 do
|
|
PostAlerter.post_created(post)
|
|
end
|
|
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, :posted)
|
|
expect {
|
|
PostAlerter.post_created(whispered_post)
|
|
}.not_to add_notification(user, :posted)
|
|
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)
|
|
|
|
private_category = Fabricate(
|
|
:private_category, group: group,
|
|
email_in: 'test@test.com', email_in_allow_strangers: false
|
|
)
|
|
|
|
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, :posted)
|
|
.and not_add_notification(staged_non_member, :posted)
|
|
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
|
|
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 do
|
|
PostAlerter.post_created(post)
|
|
end
|
|
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 on change" do
|
|
fab!(:user) { Fabricate(:user) }
|
|
fab!(:other_tag) { Fabricate(:tag) }
|
|
fab!(:watched_tag) { Fabricate(:tag) }
|
|
|
|
before do
|
|
SiteSetting.tagging_enabled = true
|
|
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), 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), 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) { Fabricate(:user) }
|
|
fab!(:staged) { Fabricate(: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), 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), 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), 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) { Fabricate(:tag) }
|
|
fab!(:user) { Fabricate(: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, :posted
|
|
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) { Fabricate(:tag) }
|
|
fab!(:category) { Fabricate(: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),
|
|
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: true,
|
|
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(["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(["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: ["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(["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[:posted])
|
|
expect(notification.topic).to eq(post.topic)
|
|
expect(notification.post_number).to eq(1)
|
|
end
|
|
|
|
it 'triggers all discourse events' do
|
|
expected_events = [
|
|
: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 do
|
|
PostAlerter.new.after_save_post(post, true)
|
|
end
|
|
|
|
# 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
|