diaspora/spec/integration/mentioning_spec.rb
2016-11-28 15:56:56 +02:00

381 lines
13 KiB
Ruby

module MentioningSpecHelpers
def notifications_about_mentioning(user, object)
table = object.class.table_name
if object.is_a?(StatusMessage)
klass = Notifications::MentionedInPost
elsif object.is_a?(Comment)
klass = Notifications::MentionedInComment
end
klass
.where(recipient_id: user.id)
.joins("LEFT OUTER JOIN mentions ON notifications.target_id = mentions.id AND "\
"notifications.target_type = 'Mention'")
.joins("LEFT OUTER JOIN #{table} ON mentions_container_id = #{table}.id AND "\
"mentions_container_type = '#{object.class.base_class}'").where(table.to_sym => {id: object.id})
end
def mention_container_path(object)
object.is_a?(Post) ? post_path(object) : post_path(object.parent, anchor: object.guid)
end
def mentioning_mail_notification(user, object)
ActionMailer::Base.deliveries.select {|delivery|
delivery.to.include?(user.email) &&
delivery.subject.include?(I18n.t("notifier.mentioned.subject", name: "")) &&
delivery.body.parts[0].body.include?(mention_container_path(object))
}
end
def also_commented_mail_notification(user, post)
ActionMailer::Base.deliveries.select {|delivery|
delivery.to.include?(user.email) &&
delivery.subject.include?(I18n.t("notifier.also_commented.limited_subject")) &&
delivery.body.parts[0].body.include?(post_path(post))
}
end
def stream_for(user)
stream = Stream::Multi.new(user)
stream.posts
end
def mention_stream_for(user)
stream = Stream::Mention.new(user)
stream.posts
end
def post_status_message(mentioned_user, aspects=nil)
aspects = user1.aspects.first.id.to_s if aspects.nil?
sign_in user1
status_msg = nil
inlined_jobs do
post "/status_messages.json", status_message: {text: text_mentioning(mentioned_user)}, aspect_ids: aspects
status_msg = StatusMessage.find(JSON.parse(response.body)["id"])
end
status_msg
end
def receive_each(entity, recipients)
inlined_jobs do
recipients.each do |recipient|
DiasporaFederation.callbacks.trigger(:receive_entity, entity, entity.author, recipient.id)
end
end
end
def find_private_message(guid)
StatusMessage.find_by(guid: guid).tap do |status_msg|
expect(status_msg).not_to be_nil
expect(status_msg.public?).to be false
end
end
def receive_status_message_via_federation(text, *recipients)
entity = FactoryGirl.build(
:status_message_entity,
author: remote_raphael.diaspora_handle,
text: text,
public: false
)
expect {
receive_each(entity, recipients)
}.to change(Post, :count).by(1).and change(ShareVisibility, :count).by(recipients.count)
find_private_message(entity.guid)
end
def receive_comment_via_federation(text, parent)
entity = build_relayable_federation_entity(
:comment,
parent_guid: parent.guid,
author: remote_raphael.diaspora_handle,
parent: Diaspora::Federation::Entities.related_entity(parent),
text: text
)
receive_each(entity, [parent.author.owner])
Comment.find_by(guid: entity.guid)
end
end
describe "mentioning", type: :request do
include MentioningSpecHelpers
RSpec::Matchers.define :be_mentioned_in do |object|
include Rails.application.routes.url_helpers
def user_notified?(user, object)
notifications_about_mentioning(user, object).any? && mentioning_mail_notification(user, object).any?
end
match do |user|
object.message.markdownified.include?(person_path(id: user.person.guid)) && user_notified?(user, object)
end
match_when_negated do |user|
!user_notified?(user, object)
end
end
RSpec::Matchers.define :be_in_streams_of do |user|
match do |status_message|
stream_for(user).map(&:id).include?(status_message.id) &&
mention_stream_for(user).map(&:id).include?(status_message.id)
end
match_when_negated do |status_message|
!stream_for(user).map(&:id).include?(status_message.id) &&
!mention_stream_for(user).map(&:id).include?(status_message.id)
end
end
let(:user1) { FactoryGirl.create(:user_with_aspect) }
let(:user2) { FactoryGirl.create(:user_with_aspect, friends: [user1, user3]) }
let(:user3) { FactoryGirl.create(:user_with_aspect) }
# see: https://github.com/diaspora/diaspora/issues/4160
it "only mentions people that are in the target aspect" do
status_msg = nil
expect {
status_msg = post_status_message(user3)
}.to change(Post, :count).by(1).and change(AspectVisibility, :count).by(1)
expect(status_msg).not_to be_nil
expect(status_msg.public?).to be false
expect(status_msg.text).to include(user3.name)
expect(user3).not_to be_mentioned_in(status_msg)
expect(status_msg).not_to be_in_streams_of(user3)
end
context "in private post via federation" do
let(:status_msg) {
receive_status_message_via_federation(text_mentioning(user2, user3), user3)
}
it "receiver is mentioned in status message" do
expect(user3).to be_mentioned_in(status_msg)
end
it "receiver can see status message in streams" do
expect(status_msg).to be_in_streams_of(user3)
end
it "non-receiver is not mentioned in status message" do
expect(user2).not_to be_mentioned_in(status_msg)
end
it "non-receiver can't see status message in streams" do
expect(status_msg).not_to be_in_streams_of(user2)
end
end
context "in private post via federation with multiple recipients" do
let(:status_msg) {
receive_status_message_via_federation(text_mentioning(user3, user2), user3, user2)
}
it "mentions all recipients in the status message" do
[user2, user3].each do |user|
expect(user).to be_mentioned_in(status_msg)
end
end
it "all recipients can see status message in streams" do
[user2, user3].each do |user|
expect(status_msg).to be_in_streams_of(user)
end
end
end
it "mentions people in public posts" do
status_msg = nil
expect {
status_msg = post_status_message(user3, "public")
}.to change(Post, :count).by(1)
expect(status_msg).not_to be_nil
expect(status_msg.public?).to be true
expect(status_msg.text).to include(user3.name)
expect(status_msg.text).to include(user3.diaspora_handle)
expect(user3).to be_mentioned_in(status_msg)
expect(status_msg).to be_in_streams_of(user3)
end
it "mentions people that are in the target aspect" do
status_msg = nil
expect {
status_msg = post_status_message(user2)
}.to change(Post, :count).by(1).and change(AspectVisibility, :count).by(1)
expect(status_msg).not_to be_nil
expect(status_msg.public?).to be false
expect(status_msg.text).to include(user2.name)
expect(status_msg.text).to include(user2.diaspora_handle)
expect(user2).to be_mentioned_in(status_msg)
expect(status_msg).to be_in_streams_of(user2)
end
context "in comments" do
let(:author) { FactoryGirl.create(:user_with_aspect) }
shared_context "commenter is author" do
let(:commenter) { author }
end
shared_context "commenter is author's friend" do
let(:commenter) { FactoryGirl.create(:user_with_aspect, friends: [author]) }
end
shared_context "commenter is not author's friend" do
let(:commenter) { FactoryGirl.create(:user) }
end
shared_context "mentioned user is author" do
let(:mentioned_user) { author }
end
shared_context "mentioned user is author's friend" do
let(:mentioned_user) { FactoryGirl.create(:user_with_aspect, friends: [author]) }
end
shared_context "mentioned user is not author's friend" do
let(:mentioned_user) { FactoryGirl.create(:user) }
end
context "with public post" do
let(:status_msg) { FactoryGirl.create(:status_message, author: author.person, public: true) }
[
["commenter is author's friend", "mentioned user is not author's friend"],
["commenter is author's friend", "mentioned user is author"],
["commenter is not author's friend", "mentioned user is author's friend"],
["commenter is not author's friend", "mentioned user is not author's friend"],
["commenter is author", "mentioned user is author's friend"],
["commenter is author", "mentioned user is not author's friend"]
].each do |commenters_context, mentioned_context|
context "when #{commenters_context} and #{mentioned_context}" do
include_context commenters_context
include_context mentioned_context
let(:comment) {
inlined_jobs do
commenter.comment!(status_msg, text_mentioning(mentioned_user))
end
}
subject { mentioned_user }
it { is_expected.to be_mentioned_in(comment) }
end
end
context "when comment is received via federation" do
context "when mentioned user is remote" do
it "relays the comment to the mentioned user" do
mentioned_person = FactoryGirl.create(:person)
expect_any_instance_of(Diaspora::Federation::Dispatcher::Public)
.to receive(:deliver_to_remote).with([mentioned_person])
receive_comment_via_federation(text_mentioning(mentioned_person), status_msg)
end
end
end
end
context "with private post" do
[
["commenter is author's friend", "mentioned user is author's friend"],
["commenter is author", "mentioned user is author's friend"],
["commenter is author's friend", "mentioned user is author"]
].each do |commenters_context, mentioned_context|
context "when #{commenters_context} and #{mentioned_context}" do
include_context commenters_context
include_context mentioned_context
let(:parent) { FactoryGirl.create(:status_message_in_aspect, author: author.person) }
let(:comment) {
inlined_jobs do
commenter.comment!(parent, text_mentioning(mentioned_user))
end
}
before do
mentioned_user.like!(parent)
end
subject { mentioned_user }
it { is_expected.to be_mentioned_in(comment) }
end
end
context "when comment is received via federation" do
let(:parent) { FactoryGirl.create(:status_message_in_aspect, author: user2.person) }
before do
user3.like!(parent)
user1.like!(parent)
end
let(:comment_text) { text_mentioning(user2, user3, user1) }
let(:comment) { receive_comment_via_federation(comment_text, parent) }
it "mentions all the recepients" do
[user1, user2, user3].each do |user|
expect(user).to be_mentioned_in(comment)
end
end
context "with only post author mentioned" do
let(:post_author) { parent.author.owner }
let(:comment_text) { text_mentioning(post_author) }
it "makes only one notification for each recipient" do
expect {
comment
}.to change { Notifications::MentionedInComment.for(post_author).count }.by(1)
.and change { Notifications::AlsoCommented.for(user1).count }.by(1)
.and change { Notifications::AlsoCommented.for(user3).count }.by(1)
expect(mentioning_mail_notification(post_author, comment).count).to eq(1)
[user1, user3].each do |user|
expect(also_commented_mail_notification(user, parent).count).to eq(1)
end
end
end
end
context "commenter can't mention a non-participant" do
let(:status_msg) { FactoryGirl.create(:status_message_in_aspect, author: author.person) }
[
["commenter is author's friend", "mentioned user is not author's friend"],
["commenter is not author's friend", "mentioned user is author's friend"],
["commenter is not author's friend", "mentioned user is not author's friend"],
["commenter is author", "mentioned user is author's friend"],
["commenter is author", "mentioned user is not author's friend"]
].each do |commenters_context, mentioned_context|
context "when #{commenters_context} and #{mentioned_context}" do
include_context commenters_context
include_context mentioned_context
let(:comment) {
inlined_jobs do
commenter.comment!(status_msg, text_mentioning(mentioned_user))
end
}
subject { mentioned_user }
it { is_expected.not_to be_mentioned_in(comment) }
end
end
end
end
end
end