diaspora/spec/services/post_service_spec.rb
Benjamin Neff ff3bd1f59b
Fix post service spec
* load records to array to check them
* test that only 15 people are returned
2017-08-12 15:39:25 +02:00

414 lines
15 KiB
Ruby

describe PostService do
let(:post) { alice.post(:status_message, text: "ohai", to: alice.aspects.first) }
let(:public) { alice.post(:status_message, text: "hey", public: true) }
describe "#find" do
context "with user" do
it "returns the post, if it is the users post" do
expect(PostService.new(alice).find(post.id)).to eq(post)
end
it "returns the post, if the user can see the it" do
expect(PostService.new(bob).find(post.id)).to eq(post)
end
it "returns the post, if it is public" do
expect(PostService.new(eve).find(public.id)).to eq(public)
end
it "does not return the post, if the post cannot be found" do
expect(PostService.new(alice).find("unknown")).to be_nil
end
it "does not return the post, if user cannot see the post" do
expect(PostService.new(eve).find(post.id)).to be_nil
end
end
context "without user" do
it "returns the post, if it is public" do
expect(PostService.new.find(public.id)).to eq(public)
end
it "does not return the post, if the post is private" do
expect(PostService.new.find(post.id)).to be_nil
end
it "does not return the post, if the post cannot be found" do
expect(PostService.new.find("unknown")).to be_nil
end
end
end
describe "#find!" do
context "with user" do
it "returns the post, if it is the users post" do
expect(PostService.new(alice).find!(post.id)).to eq(post)
end
it "works with guid" do
expect(PostService.new(alice).find!(post.guid)).to eq(post)
end
it "returns the post, if the user can see the it" do
expect(PostService.new(bob).find!(post.id)).to eq(post)
end
it "returns the post, if it is public" do
expect(PostService.new(eve).find!(public.id)).to eq(public)
end
it "RecordNotFound if the post cannot be found" do
expect {
PostService.new(alice).find!("unknown")
}.to raise_error ActiveRecord::RecordNotFound, "could not find a post with id unknown for user #{alice.id}"
end
it "RecordNotFound if user cannot see the post" do
expect {
PostService.new(eve).find!(post.id)
}.to raise_error ActiveRecord::RecordNotFound, "could not find a post with id #{post.id} for user #{eve.id}"
end
end
context "without user" do
it "returns the post, if it is public" do
expect(PostService.new.find!(public.id)).to eq(public)
end
it "works with guid" do
expect(PostService.new.find!(public.guid)).to eq(public)
end
it "NonPublic if the post is private" do
expect {
PostService.new.find!(post.id)
}.to raise_error Diaspora::NonPublic
end
it "RecordNotFound if the post cannot be found" do
expect {
PostService.new.find!("unknown")
}.to raise_error ActiveRecord::RecordNotFound, "could not find a post with id unknown"
end
end
context "id/guid switch" do
let(:public) { alice.post(:status_message, text: "ohai", public: true) }
it "assumes ids less than 16 chars are ids and not guids" do
post = Post.where(id: public.id)
expect(Post).to receive(:where).with(hash_including(id: "123456789012345")).and_return(post).at_least(:once)
PostService.new(alice).find!("123456789012345")
end
it "assumes ids more than (or equal to) 16 chars are actually guids" do
post = Post.where(guid: public.guid)
expect(Post).to receive(:where).with(hash_including(guid: "1234567890123456")).and_return(post).at_least(:once)
PostService.new(alice).find!("1234567890123456")
end
end
end
describe "#mark_user_notifications" do
let(:status_text) { text_mentioning(alice) }
it "marks a corresponding notifications as read" do
FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
expect {
PostService.new(alice).mark_user_notifications(post.id)
}.to change(Notification.where(unread: true), :count).by(-2)
end
it "marks a corresponding mention notification as read" do
mention_post = bob.post(:status_message, text: status_text, public: true)
expect {
PostService.new(alice).mark_user_notifications(mention_post.id)
}.to change(Notification.where(unread: true), :count).by(-1)
end
it "marks a corresponding mention in comment notification as read" do
notification = FactoryGirl.create(:notification_mentioned_in_comment)
status_message = notification.target.mentions_container.parent
user = notification.recipient
expect {
PostService.new(user).mark_user_notifications(status_message.id)
}.to change(Notification.where(unread: true), :count).by(-1)
end
it "does not change the update_at date/time for post notifications" do
notification = Timecop.travel(1.minute.ago) do
FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
end
expect {
PostService.new(alice).mark_user_notifications(post.id)
}.not_to change { Notification.where(id: notification.id).pluck(:updated_at) }
end
it "does not change the update_at date/time for mention notifications" do
mention_post = Timecop.travel(1.minute.ago) do
bob.post(:status_message, text: status_text, public: true)
end
mention = mention_post.mentions.where(person_id: alice.person.id).first
expect {
PostService.new(alice).mark_user_notifications(post.id)
}.not_to change { Notification.where(target_type: "Mention", target_id: mention.id).pluck(:updated_at) }
end
it "does nothing without a user" do
expect_any_instance_of(PostService).not_to receive(:mark_comment_reshare_like_notifications_read).with(post.id)
expect_any_instance_of(PostService).not_to receive(:mark_mention_notifications_read).with(post.id)
PostService.new.mark_user_notifications(post.id)
end
end
describe "#destroy" do
it "let a user delete his message" do
PostService.new(alice).destroy(post.id)
expect(StatusMessage.find_by_id(post.id)).to be_nil
end
it "sends a retraction on delete" do
expect(alice).to receive(:retract).with(post)
PostService.new(alice).destroy(post.id)
end
it "will not let you destroy posts visible to you but that you do not own" do
expect {
PostService.new(bob).destroy(post.id)
}.to raise_error Diaspora::NotMine
expect(StatusMessage.find_by_id(post.id)).not_to be_nil
end
it "will not let you destroy posts that are not visible to you" do
expect {
PostService.new(eve).destroy(post.id)
}.to raise_error(ActiveRecord::RecordNotFound)
expect(StatusMessage.find_by_id(post.id)).not_to be_nil
end
end
describe "#mentionable_in_comment" do
describe "semi-integration test" do
let(:post_author_attributes) { {first_name: "Ro#{r_str}"} }
let(:post_author) { FactoryGirl.create(:person, post_author_attributes) }
let(:current_user) { FactoryGirl.create(:user_with_aspect) }
let(:post_service) { PostService.new(current_user) }
shared_context "with commenters and likers" do
# randomize ids of the created people so that the test doesn't pass just because of
# the id sequence matched against the expected ordering
let(:ids) { (1..4).map {|i| Person.maximum(:id) + i }.shuffle }
before do
# in case when post_author has't been instantiated before this context, specify id
# in order to avoid id conflict with the people generated here
post_author_attributes.merge!(id: ids.max + 1)
end
let!(:commenter1) {
FactoryGirl.create(:person, id: ids.shift, first_name: "Ro1#{r_str}").tap {|person|
FactoryGirl.create(:comment, author: person, post: post)
}
}
let!(:commenter2) {
FactoryGirl.create(:person, id: ids.shift, first_name: "Ro2#{r_str}").tap {|person|
FactoryGirl.create(:comment, author: person, post: post)
}
}
let!(:liker1) {
FactoryGirl.create(:person, id: ids.shift, first_name: "Ro1#{r_str}").tap {|person|
FactoryGirl.create(:like, author: person, target: post)
}
}
let!(:liker2) {
FactoryGirl.create(:person, id: ids.shift, first_name: "Ro2#{r_str}").tap {|person|
FactoryGirl.create(:like, author: person, target: post)
}
}
end
shared_context "with a current user's friend" do
let!(:current_users_friend) {
FactoryGirl.create(:person).tap {|friend|
current_user.contacts.create!(
person: friend,
aspects: [current_user.aspects.first],
sharing: true,
receiving: true
)
}
}
end
context "with private post" do
let(:post) { FactoryGirl.create(:status_message, text: "ohai", author: post_author) }
context "when the post doesn't have a visibility for the current user" do
it "doesn't find a post and raises an exception" do
expect {
post_service.mentionable_in_comment(post.id, "Ro")
}.to raise_error(ActiveRecord::RecordNotFound)
end
end
context "when the post has a visibility for the current user" do
before do
ShareVisibility.batch_import([current_user.id], post)
end
context "with commenters and likers" do
include_context "with commenters and likers"
it "returns mention suggestions in the correct order" do
expected_suggestions = [
post_author, commenter1, commenter2, liker1, liker2
]
expect(post_service.mentionable_in_comment(post.id, "Ro")).to eq(expected_suggestions)
end
end
context "with a current user's friend" do
include_context "with a current user's friend"
it "doesn't include a contact" do
expect(post_service.mentionable_in_comment(post.id, current_users_friend.first_name)).to be_empty
end
end
it "doesn't include a non contact" do
expect(post_service.mentionable_in_comment(post.id, eve.person.first_name)).to be_empty
end
end
end
context "with public post" do
let(:post) { FactoryGirl.create(:status_message, text: "ohai", public: true, author: post_author) }
context "with commenters and likers and with a current user's friend" do
include_context "with commenters and likers"
include_context "with a current user's friend"
it "returns mention suggestions in the correct order" do
result = post_service.mentionable_in_comment(post.id, "Ro").to_a
expect(result.size).to be > 7
# participants: post author, comments, likers
expect(result[0..4]).to eq([post_author, commenter1, commenter2, liker1, liker2])
# contacts
expect(result[5]).to eq(current_users_friend)
# non-contacts
result[6..-1].each {|person|
expect(person.contacts.where(user_id: current_user.id)).to be_empty
expect(person.profile.first_name).to include("Ro")
}
end
it "doesn't include people with non-matching names" do
commenter = FactoryGirl.create(:person, first_name: "RRR#{r_str}")
FactoryGirl.create(:comment, author: commenter)
liker = FactoryGirl.create(:person, first_name: "RRR#{r_str}")
FactoryGirl.create(:like, author: liker)
friend = FactoryGirl.create(:person, first_name: "RRR#{r_str}")
current_user.contacts.create!(
person: friend,
aspects: [current_user.aspects.first],
sharing: true,
receiving: true
)
result = post_service.mentionable_in_comment(post.id, "Ro")
expect(result).not_to include(commenter)
expect(result).not_to include(liker)
expect(result).not_to include(friend)
end
end
shared_examples "current user can't mention himself" do
before do
current_user.profile.update(first_name: "Ro#{r_str}")
end
it "doesn't include current user" do
expect(post_service.mentionable_in_comment(post.id, "Ro")).not_to include(current_user.person)
end
end
context "when current user is a post author" do
let(:post_author) { current_user.person }
include_examples "current user can't mention himself"
end
context "current user is a participant" do
before do
current_user.like!(post)
current_user.comment!(post, "hello")
end
include_examples "current user can't mention himself"
end
context "current user is a stranger matching a search pattern" do
include_examples "current user can't mention himself"
end
it "doesn't fail when the post author doesn't match the requested pattern" do
expect(post_service.mentionable_in_comment(post.id, "#{r_str}#{r_str}#{r_str}")).to be_empty
end
it "renders a commenter with multiple comments only once" do
person = FactoryGirl.create(:person, first_name: "Ro2#{r_str}")
2.times { FactoryGirl.create(:comment, author: person, post: post) }
expect(post_service.mentionable_in_comment(post.id, person.first_name).length).to eq(1)
end
end
end
describe "unit test" do
let(:post_service) { PostService.new(alice) }
before do
expect(post_service).to receive(:find!).and_return(post)
end
it "calls Person.allowed_to_be_mentioned_in_a_comment_to" do
expect(Person).to receive(:allowed_to_be_mentioned_in_a_comment_to).with(post).and_call_original
post_service.mentionable_in_comment(post.id, "whatever")
end
it "calls Person.find_by_substring" do
expect(Person).to receive(:find_by_substring).with("whatever").and_call_original
post_service.mentionable_in_comment(post.id, "whatever")
end
it "calls Person.sort_for_mention_suggestion" do
expect(Person).to receive(:sort_for_mention_suggestion).with(post, alice).and_call_original
post_service.mentionable_in_comment(post.id, "whatever")
end
it "calls Person.limit" do
16.times {
FactoryGirl.create(:comment, author: FactoryGirl.create(:person, first_name: "Ro#{r_str}"), post: post)
}
expect(post_service.mentionable_in_comment(post.id, "Ro").length).to eq(15)
end
it "contains a constraint on a current user" do
expect(Person).to receive(:allowed_to_be_mentioned_in_a_comment_to) { Person.all }
expect(Person).to receive(:find_by_substring) { Person.all }
expect(Person).to receive(:sort_for_mention_suggestion) { Person.all }
expect(post_service.mentionable_in_comment(post.id, alice.person.first_name))
.not_to include(alice.person)
end
end
end
end