414 lines
15 KiB
Ruby
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
|