Merge pull request #6744 from SuperTux88/refactor-services

Refactor services
This commit is contained in:
Dennis Schubert 2016-03-08 23:35:43 +01:00
commit df1c1ab24e
23 changed files with 661 additions and 444 deletions

View file

@ -12,17 +12,16 @@ class CommentsController < ApplicationController
end end
def create def create
@comment = CommentService.new(post_id: params[:post_id], text: params[:text], user: current_user).create_comment comment = comment_service.create(params[:post_id], params[:text])
if @comment if comment
respond_create_success respond_create_success(comment)
else else
render nothing: true, status: 404 render nothing: true, status: 404
end end
end end
def destroy def destroy
service = CommentService.new(comment_id: params[:id], user: current_user) if comment_service.destroy(params[:id])
if service.destroy_comment
respond_destroy_success respond_destroy_success
else else
respond_destroy_error respond_destroy_error
@ -36,22 +35,24 @@ class CommentsController < ApplicationController
end end
def index def index
service = CommentService.new(post_id: params[:post_id], user: current_user) comments = comment_service.find_for_post(params[:post_id])
@post = service.post
@comments = service.comments
respond_with do |format| respond_with do |format|
format.json { render json: CommentPresenter.as_collection(@comments), status: 200 } format.json { render json: CommentPresenter.as_collection(comments), status: 200 }
format.mobile { render layout: false } format.mobile { render layout: false, locals: {comments: comments} }
end end
end end
private private
def respond_create_success def comment_service
@comment_service ||= CommentService.new(current_user)
end
def respond_create_success(comment)
respond_to do |format| respond_to do |format|
format.json { render json: CommentPresenter.new(@comment), status: 201 } format.json { render json: CommentPresenter.new(comment), status: 201 }
format.html { render nothing: true, status: 201 } format.html { render nothing: true, status: 201 }
format.mobile { render partial: "comment", locals: {post: @comment.post, comment: @comment} } format.mobile { render partial: "comment", locals: {comment: comment} }
end end
end end

View file

@ -3,61 +3,50 @@
# the COPYRIGHT file. # the COPYRIGHT file.
class PostsController < ApplicationController class PostsController < ApplicationController
include PostsHelper
before_action :authenticate_user!, only: :destroy before_action :authenticate_user!, only: :destroy
before_action :set_format_if_malformed_from_status_net, only: :show before_action :set_format_if_malformed_from_status_net, only: :show
respond_to :html, :mobile, :json, :xml respond_to :html, :mobile, :json, :xml
rescue_from Diaspora::NonPublic do rescue_from Diaspora::NonPublic do
if user_signed_in?
@code = "not-public"
respond_to do |format|
format.all { render template: "errors/not_public", status: 404, layout: "error_page" }
end
else
authenticate_user! authenticate_user!
end end
end
rescue_from Diaspora::NotMine do rescue_from Diaspora::NotMine do
render text: I18n.t("posts.show.forbidden"), status: 403 render text: I18n.t("posts.show.forbidden"), status: 403
end end
def show def show
post_service = PostService.new(id: params[:id], user: current_user) post = post_service.find!(params[:id])
post_service.mark_user_notifications post_service.mark_user_notifications(post.id)
@post = post_service.post
respond_to do |format| respond_to do |format|
format.html { gon.post = post_service.present_json } format.html {
format.xml { render xml: @post.to_diaspora_xml } gon.post = PostPresenter.new(post, current_user)
format.json { render json: post_service.present_json } render locals: {post: post}
}
format.mobile { render locals: {post: post} }
format.xml { render xml: post.to_diaspora_xml }
format.json { render json: PostPresenter.new(post, current_user) }
end end
end end
def iframe
render text: post_iframe_url(params[:id]), layout: false
end
def oembed def oembed
post_id = OEmbedPresenter.id_from_url(params.delete(:url)) post_id = OEmbedPresenter.id_from_url(params.delete(:url))
post_service = PostService.new(id: post_id, user: current_user, post = post_service.find!(post_id)
oembed: params.slice(:format, :maxheight, :minheight)) oembed = params.slice(:format, :maxheight, :minheight)
render json: post_service.present_oembed render json: OEmbedPresenter.new(post, oembed)
rescue
render nothing: true, status: 404
end end
def interactions def interactions
post_service = PostService.new(id: params[:id], user: current_user) post = post_service.find!(params[:id])
respond_with post_service.present_interactions_json respond_with PostInteractionPresenter.new(post, current_user)
end end
def destroy def destroy
post_service = PostService.new(id: params[:id], user: current_user) post_service.destroy(params[:id])
post_service.retract_post
@post = post_service.post
respond_to do |format| respond_to do |format|
format.js { render "destroy", layout: false, format: :js }
format.json { render nothing: true, status: 204 } format.json { render nothing: true, status: 204 }
format.any { redirect_to stream_path } format.any { redirect_to stream_path }
end end
@ -65,6 +54,10 @@ class PostsController < ApplicationController
private private
def post_service
@post_service ||= PostService.new(current_user)
end
def set_format_if_malformed_from_status_net def set_format_if_malformed_from_status_net
request.format = :html if request.format == "application/html+xml" request.format = :html if request.format == "application/html+xml"
end end

View file

@ -47,12 +47,17 @@ class StatusMessagesController < ApplicationController
end end
def create def create
@status_message = StatusMessageCreationService.new(params, current_user).status_message normalized_params = params.merge(
handle_mention_feedback services: normalize_services,
aspect_ids: normalize_aspect_ids,
public: normalize_public_flag
)
status_message = StatusMessageCreationService.new(current_user).create(normalized_params)
handle_mention_feedback(status_message)
respond_to do |format| respond_to do |format|
format.html { redirect_to :back } format.html { redirect_to :back }
format.mobile { redirect_to stream_path } format.mobile { redirect_to stream_path }
format.json { render json: PostPresenter.new(@status_message, current_user), status: 201 } format.json { render json: PostPresenter.new(status_message, current_user), status: 201 }
end end
rescue StandardError => error rescue StandardError => error
handle_create_error(error) handle_create_error(error)
@ -73,9 +78,9 @@ class StatusMessagesController < ApplicationController
end end
end end
def handle_mention_feedback def handle_mention_feedback(status_message)
return unless comes_from_others_profile_page? return unless comes_from_others_profile_page?
flash[:notice] = successful_mention_message flash[:notice] = t("status_messages.create.success", names: status_message.mentioned_people_names)
end end
def comes_from_others_profile_page? def comes_from_others_profile_page?
@ -87,11 +92,24 @@ class StatusMessagesController < ApplicationController
end end
def own_profile_page? def own_profile_page?
request.env["HTTP_REFERER"].include?("/people/" + params[:status_message][:author][:guid].to_s) request.env["HTTP_REFERER"].include?("/people/" + current_user.guid)
end end
def successful_mention_message def normalize_services
t("status_messages.create.success", names: @status_message.mentioned_people_names) [*params[:services]].compact
end
def normalize_aspect_ids
aspect_ids = [*params[:aspect_ids]]
if aspect_ids.first == "all_aspects"
current_user.aspect_ids
else
aspect_ids
end
end
def normalize_public_flag
[*params[:aspect_ids]].first == "public"
end end
def remove_getting_started def remove_getting_started

View file

@ -133,12 +133,10 @@ class Post < ActiveRecord::Base
############# #############
def self.diaspora_initialize(params) def self.diaspora_initialize(params)
new_post = self.new params.to_hash.stringify_keys.slice(*self.column_names) new(params.to_hash.stringify_keys.slice(*column_names)).tap do |new_post|
new_post.author = params[:author] new_post.author = params[:author]
new_post.public = params[:public] if params[:public]
new_post.pending = params[:pending] if params[:pending]
new_post.diaspora_handle = new_post.author.diaspora_handle new_post.diaspora_handle = new_post.author.diaspora_handle
new_post end
end end
# @return Returns true if this Post will accept updates (i.e. updates to the caption of a photo). # @return Returns true if this Post will accept updates (i.e. updates to the caption of a photo).
@ -157,21 +155,4 @@ class Post < ActiveRecord::Base
def nsfw def nsfw
self.author.profile.nsfw? self.author.profile.nsfw?
end end
def self.find_public(id)
where(post_key(id) => id).includes(:author, comments: :author).first.tap do |post|
raise ActiveRecord::RecordNotFound.new("could not find a post with id #{id}") unless post
raise Diaspora::NonPublic unless post.public?
end
end
def self.find_non_public_by_guid_or_id_with_user(id, user)
user.find_visible_shareable_by_id(Post, id, key: post_key(id)).tap do |post|
raise ActiveRecord::RecordNotFound.new("could not find a post with id #{id}") unless post
end
end
def self.post_key(id)
id.to_s.length <= 8 ? :id : :guid
end
end end

View file

@ -1,43 +1,32 @@
class CommentService class CommentService
attr_reader :post, :comments def initialize(user=nil)
@user = user
def initialize(params)
@user = params[:user]
@post_id = params[:post_id]
@comment_id = params[:comment_id]
@text = params[:text]
@post = find_post! if @post_id
@comments = @post.comments.for_a_stream if @post
end end
def create_comment def create(post_id, text)
@user.comment!(post, @text) if @post post = post_service.find!(post_id)
user.comment!(post, text)
end end
def destroy_comment def destroy(comment_id)
@comment = Comment.find(@comment_id) comment = Comment.find(comment_id)
if @user.owns?(@comment) || @user.owns?(@comment.parent) if user.owns?(comment) || user.owns?(comment.parent)
@user.retract(@comment) user.retract(comment)
true true
else else
false false
end end
end end
def find_for_post(post_id)
post_service.find!(post_id).comments.for_a_stream
end
private private
def find_post! attr_reader :user
find_post.tap do |post|
raise(ActiveRecord::RecordNotFound) unless post
end
end
def find_post def post_service
if @user @post_service ||= PostService.new(user)
@user.find_visible_shareable_by_id(Post, @post_id)
else
Post.find_by_id_and_public(@post_id, true)
end
end end
end end

View file

@ -1,65 +1,66 @@
class PostService class PostService
attr_reader :post def initialize(user=nil)
@user = user
def initialize(params)
@id = params[:id]
@user = params[:user]
@oembed = params[:oembed] || {}
assign_post
end end
def assign_post def find(id)
if user if user
@post = Post.find_non_public_by_guid_or_id_with_user(id, user) user.find_visible_shareable_by_id(Post, id)
else else
@post = Post.find_public(id) Post.find_by_id_and_public(id, true)
end end
end end
def present_json def find!(id_or_guid)
PostPresenter.new(post, user) if user
find_non_public_by_guid_or_id_with_user!(id_or_guid)
else
find_public!(id_or_guid)
end
end end
def present_interactions_json def mark_user_notifications(post_id)
PostInteractionPresenter.new(post, user) return unless user
mark_comment_reshare_like_notifications_read(post_id)
mark_mention_notifications_read(post_id)
end end
def present_oembed def destroy(post_id)
OEmbedPresenter.new(post, oembed) post = find!(post_id)
end raise Diaspora::NotMine unless post.author == user.person
user.retract(post)
def mark_user_notifications
mark_corresponding_notifications_read if user
end
def retract_post
raise Diaspora::NotMine unless user_owns_post?
user.retract(@post)
end end
private private
attr_reader :user, :id, :oembed attr_reader :user
def user_owns_post? def find_public!(id_or_guid)
post.author == user.person Post.where(post_key(id_or_guid) => id_or_guid).first.tap do |post|
end raise ActiveRecord::RecordNotFound, "could not find a post with id #{id_or_guid}" unless post
raise Diaspora::NonPublic unless post.public?
def mark_corresponding_notifications_read
mark_comment_reshare_like_notifications_read
mark_mention_notifications_read
end
def mark_comment_reshare_like_notifications_read
notification = Notification.where(recipient_id: user.id, target_type: "Post", target_id: post.id, unread: true)
notification.each do |notification|
notification.set_read_state(true)
end end
end end
def mark_mention_notifications_read def find_non_public_by_guid_or_id_with_user!(id_or_guid)
mention = post.mentions.where(person_id: user.person_id).first user.find_visible_shareable_by_id(Post, id_or_guid, key: post_key(id_or_guid)).tap do |post|
Notification.where(recipient_id: user.id, target_type: "Mention", target_id: mention.id, unread: true) raise ActiveRecord::RecordNotFound, "could not find a post with id #{id_or_guid} for user #{user.id}" unless post
.first.try(:set_read_state, true) if mention end
end
# We can assume a guid is at least 16 characters long as we have guids set to hex(8) since we started using them.
def post_key(id_or_guid)
id_or_guid.to_s.length < 16 ? :id : :guid
end
def mark_comment_reshare_like_notifications_read(post_id)
Notification.where(recipient_id: user.id, target_type: "Post", target_id: post_id, unread: true)
.update_all(unread: false)
end
def mark_mention_notifications_read(post_id)
mention_id = Mention.where(post_id: post_id, person_id: user.person_id).pluck(:id)
Notification.where(recipient_id: user.id, target_type: "Mention", target_id: mention_id, unread: true)
.update_all(unread: false) if mention_id
end end
end end

View file

@ -1,89 +1,66 @@
class StatusMessageCreationService class StatusMessageCreationService
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
attr_reader :status_message def initialize(user)
@user = user
end
def initialize(params, user) def create(params)
normalize_params(params, user) build_status_message(params).tap do |status_message|
status_message_initial = user.build_post(:status_message, params[:status_message]) add_attachments(status_message, params)
@status_message = add_attachments(params, status_message_initial) status_message.save
@status_message.save process(status_message, params[:aspect_ids], params[:services])
process_status_message(user) end
end end
private private
attr_reader :services, :destination_aspect_ids attr_reader :user
def normalize_params(params, user) def build_status_message(params)
normalize_aspect_ids(params) public = params[:public] || false
normalize_public_flag!(params) user.build_post(:status_message, params[:status_message].merge(public: public))
@services = [*params[:services]].compact
@destination_aspect_ids = destination_aspect_ids(params, user)
end end
def normalize_aspect_ids(params) def add_attachments(status_message, params)
params[:status_message][:aspect_ids] = [*params[:aspect_ids]] add_location(status_message, params[:location_address], params[:location_coords])
add_poll(status_message, params)
add_photos(status_message, params[:photos])
end end
def normalize_public_flag!(params) def add_location(status_message, address, coordinates)
sm = params[:status_message]
public_flag_string = (sm[:aspect_ids] && sm[:aspect_ids].first == "public") || sm[:public]
public_flag = public_flag_string.to_s.match(/(true)|(on)/) ? true : false
params[:status_message][:public] = public_flag
end
def destination_aspect_ids(params, user)
if params[:status_message][:aspect_ids].first == "all_aspects"
user.aspect_ids
elsif !params[:status_message][:public]
params[:aspect_ids]
end
end
def add_attachments(params, status_message_initial)
status_message_with_location = add_location(params, status_message_initial)
status_message_with_poll = add_poll(params, status_message_with_location)
add_photos(params, status_message_with_poll)
end
def add_location(params, status_message)
address = params[:location_address]
coordinates = params[:location_coords]
status_message.build_location(address: address, coordinates: coordinates) if address.present? status_message.build_location(address: address, coordinates: coordinates) if address.present?
status_message
end end
def add_poll(params, status_message) def add_poll(status_message, params)
if params[:poll_question].present? if params[:poll_question].present?
status_message.build_poll(question: params[:poll_question]) status_message.build_poll(question: params[:poll_question])
[*params[:poll_answers]].each do |poll_answer| [*params[:poll_answers]].each do |poll_answer|
status_message.poll.poll_answers.build(answer: poll_answer) status_message.poll.poll_answers.build(answer: poll_answer)
end end
end end
status_message
end end
def add_photos(params, status_message) def add_photos(status_message, photos)
status_message.attach_photos_by_ids(params[:photos]) status_message.attach_photos_by_ids(photos)
status_message status_message.photos.each {|photo| photo.public = status_message.public }
end end
def process_status_message(user) def process(status_message, aspect_ids, services)
add_status_message_to_streams(user) add_to_streams(status_message, aspect_ids) unless status_message.public
dispatch_status_message(user) dispatch(status_message, services)
user.participate!(@status_message) user.participate!(status_message)
end end
def add_status_message_to_streams(user) def add_to_streams(status_message, aspect_ids)
aspects = user.aspects_from_ids(@destination_aspect_ids) aspects = user.aspects_from_ids(aspect_ids)
user.add_to_streams(@status_message, aspects) user.add_to_streams(status_message, aspects)
end end
def dispatch_status_message(user) def dispatch(status_message, services)
receiving_services = Service.titles(@services) receiving_services = services ? Service.titles(services) : []
user.dispatch_post(@status_message, user.dispatch_post(status_message,
url: short_post_url(@status_message.guid, host: AppConfig.environment.url), url: short_post_url(status_message.guid, host: AppConfig.environment.url),
service_types: receiving_services) service_types: receiving_services)
end end
end end

View file

@ -1,3 +1,3 @@
.comment-container .comment-container
%ul.comments %ul.comments
= render partial: "comments/comment", collection: @post.comments.for_a_stream, locals: {post: @post} = render partial: "comments/comment", collection: comments

View file

@ -1,10 +0,0 @@
-# Copyright (c) 2010-2012, Diaspora Inc. This file is
-# licensed under the Affero General Public License version 3 or later. See
-# the COPYRIGHT file.
.transparent.big-number
404
%h3
= t("error_messages.post_not_public_or_not_exist")
%p
= t("error_messages.login_try_again", login_link: new_user_session_path).html_safe

View file

@ -1,2 +0,0 @@
var target = $("#<%= @post.guid %>")
target.hide('blind', { direction: 'vertical' }, 300, function(){ target.remove() });

View file

@ -3,7 +3,7 @@
-# the COPYRIGHT file. -# the COPYRIGHT file.
- content_for :page_title do - content_for :page_title do
= post_page_title @post = post_page_title post
- content_for :content do - content_for :content do
#container.container-fluid #container.container-fluid

View file

@ -3,6 +3,6 @@
-# the COPYRIGHT file. -# the COPYRIGHT file.
.stream .stream
= render :partial => 'shared/stream_element', = render partial: "shared/stream_element",
:locals => {:post => @post, :commenting_disabled => commenting_disabled?(@post), :expanded_info => true} locals: {post: post, commenting_disabled: commenting_disabled?(post), expanded_info: true}

View file

@ -26,7 +26,7 @@
!= reactions_link(post, "active") != reactions_link(post, "active")
.comment-container .comment-container
%ul.comments %ul.comments
= render partial: "comments/comment", collection: @post.comments.for_a_stream, locals: {post: @post} = render partial: "comments/comment", collection: post.comments.for_a_stream, locals: {post: post}
- else - else
!= reactions_link(post) != reactions_link(post)

View file

@ -91,9 +91,6 @@ en:
helper: helper:
invalid_fields: "Invalid fields" invalid_fields: "Invalid fields"
correct_the_following_errors_and_try_again: "Correct the following errors and try again." correct_the_following_errors_and_try_again: "Correct the following errors and try again."
post_not_public: "The post you are trying to view is not public!"
post_not_public_or_not_exist: "The post you are trying to view is not public, or does not exist!"
login_try_again: "Please <a href='%{login_link}'>login</a> and try again."
admins: admins:
admin_bar: admin_bar:

View file

@ -40,10 +40,7 @@ Diaspora::Application.routes.draw do
resources :comments, only: %i(new create destroy index) resources :comments, only: %i(new create destroy index)
end end
get 'p/:id' => 'posts#show', :as => 'short_post' get 'p/:id' => 'posts#show', :as => 'short_post'
get 'posts/:id/iframe' => 'posts#iframe', :as => 'iframe'
# roll up likes into a nested resource above # roll up likes into a nested resource above
resources :comments, :only => [:create, :destroy] do resources :comments, :only => [:create, :destroy] do

View file

@ -5,8 +5,6 @@ Then /^I should see the "(.*)" message$/ do |message|
I18n.translate('invitation_codes.excited', :name => @alice.name) I18n.translate('invitation_codes.excited', :name => @alice.name)
when "welcome to diaspora" when "welcome to diaspora"
I18n.translate('users.getting_started.well_hello_there') I18n.translate('users.getting_started.well_hello_there')
when 'post not public'
I18n.translate('error_messages.post_not_public_or_not_exist')
else else
raise "muriel, you don't have that message key, add one here" raise "muriel, you don't have that message key, add one here"
end end

View file

@ -140,7 +140,7 @@ describe CommentsController, :type => :controller do
comments = [alice, bob, eve].map{ |u| u.comment!(@message, "hey") } comments = [alice, bob, eve].map{ |u| u.comment!(@message, "hey") }
get :index, :post_id => @message.id, :format => :json get :index, :post_id => @message.id, :format => :json
expect(assigns[:comments].map(&:id)).to match_array(comments.map(&:id)) expect(JSON.parse(response.body).map {|comment| comment["id"] }).to match_array(comments.map(&:id))
end end
it 'returns a 404 on a nonexistent post' do it 'returns a 404 on a nonexistent post' do

View file

@ -5,104 +5,86 @@
require "spec_helper" require "spec_helper"
describe PostsController, type: :controller do describe PostsController, type: :controller do
let!(:post_service_double) { double("post_service") } let(:post) { alice.post(:status_message, text: "ohai", to: alice.aspects.first) }
before do
aspect = alice.aspects.first
@message = alice.build_post :status_message, text: "ohai", to: aspect.id
@message.save!
alice.add_to_streams(@message, [aspect])
alice.dispatch_post @message, to: aspect.id
allow(PostService).to receive(:new).and_return(post_service_double)
end
describe "#show" do describe "#show" do
before do
expect(post_service_double).to receive(:mark_user_notifications)
allow(post_service_double).to receive(:present_json)
end
context "user signed in" do context "user signed in" do
context "given a post that the user is allowed to see" do context "given a post that the user is allowed to see" do
before do before do
sign_in :user, alice sign_in :user, alice
expect(post_service_double).to receive(:post).and_return(@message)
end end
it "succeeds" do it "succeeds" do
get :show, id: @message.id expect_any_instance_of(PostService).to receive(:mark_user_notifications).with(post.id)
get :show, id: post.id
expect(response).to be_success expect(response).to be_success
end end
it 'succeeds after removing a mention when closing the mentioned user\'s account' do it "succeeds after removing a mention when closing the mentioned user's account" do
user = FactoryGirl.create(:user, username: "user") user = FactoryGirl.create(:user, username: "user")
alice.share_with(user.person, alice.aspects.first) alice.share_with(user.person, alice.aspects.first)
msg = alice.build_post :status_message,
text: "Mention @{User ; #{user.diaspora_handle}}", public: true, to: "all" msg = alice.post(:status_message, text: "Mention @{User ; #{user.diaspora_handle}}", public: true)
msg.save!
expect(msg.mentioned_people.count).to eq(1) expect(msg.mentioned_people.count).to eq(1)
user.destroy user.destroy
get :show, id: msg.id get :show, id: msg.id
expect(response).to be_success expect(response).to be_success
end end
it "renders the application layout on mobile" do it "renders the application layout on mobile" do
get :show, id: @message.id, format: :mobile get :show, id: post.id, format: :mobile
expect(response).to render_template("layouts/application") expect(response).to render_template("layouts/application")
end end
it "succeeds on mobile with a reshare" do it "succeeds on mobile with a reshare" do
get :show, id: FactoryGirl.create(:reshare, author: alice.person).id, format: :mobile reshare_id = FactoryGirl.create(:reshare, author: alice.person).id
expect_any_instance_of(PostService).to receive(:mark_user_notifications).with(reshare_id)
get :show, id: reshare_id, format: :mobile
expect(response).to be_success expect(response).to be_success
end end
end end
context "given a post that the user is not allowed to see" do context "given a post that the user is not allowed to see" do
before do before do
sign_in :user, alice sign_in :user, eve
expect(post_service_double).to receive(:post).and_raise(Diaspora::NonPublic)
end end
it "returns a 404" do it "returns a 404" do
get :show, id: @message.id expect {
expect(response.code).to eq("404") get :show, id: post.id
}.to raise_error ActiveRecord::RecordNotFound
end end
end end
end end
context "user not signed in" do context "user not signed in" do
context "given a public post" do context "given a public post" do
before :each do let(:public) { alice.post(:status_message, text: "hello", public: true) }
@status = alice.post(:status_message, text: "hello", public: true, to: "all")
expect(post_service_double).to receive(:post).and_return(@status)
end
it "shows a public post" do it "shows a public post" do
get :show, id: @status.id get :show, id: public.id
expect(response.body).to match "hello" expect(response.body).to match "hello"
end end
it "succeeds for statusnet" do it "succeeds for statusnet" do
@request.env["HTTP_ACCEPT"] = "application/html+xml,text/html" @request.env["HTTP_ACCEPT"] = "application/html+xml,text/html"
get :show, id: @status.id get :show, id: public.id
expect(response.body).to match "hello" expect(response.body).to match "hello"
end end
it "responds with diaspora xml if format is xml" do it "responds with diaspora xml if format is xml" do
get :show, id: @status.guid, format: :xml get :show, id: public.guid, format: :xml
expect(response.body).to eq(@status.to_diaspora_xml) expect(response.body).to eq(public.to_diaspora_xml)
end end
end end
context "given a limited post" do context "given a limited post" do
before do
expect(post_service_double).to receive(:post).and_raise(Diaspora::NonPublic)
end
it "forces the user to sign" do it "forces the user to sign" do
get :show, id: @message.id get :show, id: post.id
expect(response).to be_redirect expect(response).to be_redirect
expect(response).to redirect_to new_user_session_path expect(response).to redirect_to new_user_session_path
end end
@ -110,40 +92,55 @@ describe PostsController, type: :controller do
end end
end end
describe "iframe" do describe "oembed" do
it "contains an iframe" do it "works when you can see it" do
get :iframe, id: @message.id sign_in alice
get :oembed, url: "/posts/#{post.id}"
expect(response.body).to match /iframe/ expect(response.body).to match /iframe/
end end
end
describe "oembed" do it "returns a 404 response when the post is not found" do
it "receives a present oembed" do get :oembed, url: "/posts/#{post.id}"
expect(post_service_double).to receive(:present_oembed) expect(response.status).to eq(404)
get :oembed, url: "/posts/#{@message.id}"
end end
end end
describe "#destroy" do describe "#destroy" do
context "own post" do
before do before do
sign_in alice sign_in alice
end end
it "will receive a retract post" do it "works when it is your post" do
expect(post_service_double).to receive(:retract_post) expect_any_instance_of(PostService).to receive(:destroy).with(post.id.to_s)
expect(post_service_double).to receive(:post).and_return(@message)
message = alice.post(:status_message, text: "hey", to: alice.aspects.first.id) delete :destroy, format: :json, id: post.id
delete :destroy, format: :js, id: message.id expect(response.status).to eq(204)
end end
context "when Diaspora::NotMine is raised by retract post" do it "redirects to stream on mobile" do
delete :destroy, format: :mobile, id: post.id
expect(response).to be_redirect
expect(response).to redirect_to stream_path
end
end
context "post of another user" do
it "will respond with a 403" do it "will respond with a 403" do
expect(post_service_double).to receive(:retract_post).and_raise(Diaspora::NotMine) sign_in :user, bob
message = alice.post(:status_message, text: "hey", to: alice.aspects.first.id)
delete :destroy, format: :js, id: message.id delete :destroy, format: :json, id: post.id
expect(response.body).to eq("You are not allowed to do that") expect(response.body).to eq("You are not allowed to do that")
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
it "will respond with a 404 if the post is not visible" do
sign_in :user, eve
expect {
delete :destroy, format: :json, id: post.id
}.to raise_error ActiveRecord::RecordNotFound
end
end end
end end
end end

View file

@ -49,12 +49,12 @@ describe StatusMessagesController, :type => :controller do
end end
describe '#create' do describe '#create' do
let(:text) { "facebook, is that you?" }
let(:status_message_hash) { let(:status_message_hash) {
{ :status_message => { {
:public => "true", status_message: {text: text},
:text => "facebook, is that you?", aspect_ids: [@aspect1.id.to_s]
}, }
:aspect_ids => [@aspect1.id.to_s] }
} }
it 'creates with valid html' do it 'creates with valid html' do
@ -96,14 +96,52 @@ describe StatusMessagesController, :type => :controller do
post :create, status_message_hash post :create, status_message_hash
end end
it 'takes public in aspect ids' do context "with aspect_ids" do
post :create, status_message_hash.merge(:aspect_ids => ['public']) before do
expect(response.status).to eq(302) @aspect2 = alice.aspects.create(name: "another aspect")
end end
it 'takes all_aspects in aspect ids' do it "takes one aspect as array in aspect_ids" do
post :create, status_message_hash.merge(:aspect_ids => ['all_aspects']) post :create, status_message_hash
expect(response.status).to eq(302) expect(response.status).to eq(302)
status_message = StatusMessage.find_by_text(text)
expect(status_message.aspect_visibilities.map(&:aspect)).to eq([@aspect1])
end
it "takes one aspect as string in aspect_ids" do
post :create, status_message_hash.merge(aspect_ids: @aspect1.id.to_s)
expect(response.status).to eq(302)
status_message = StatusMessage.find_by_text(text)
expect(status_message.aspect_visibilities.map(&:aspect)).to eq([@aspect1])
end
it "takes public as array in aspect_ids" do
post :create, status_message_hash.merge(aspect_ids: ["public"])
expect(response.status).to eq(302)
status_message = StatusMessage.find_by_text(text)
expect(status_message.public).to be_truthy
end
it "takes public as string in aspect_ids" do
post :create, status_message_hash.merge(aspect_ids: "public")
expect(response.status).to eq(302)
status_message = StatusMessage.find_by_text(text)
expect(status_message.public).to be_truthy
end
it "takes all_aspects as array in aspect_ids" do
post :create, status_message_hash.merge(aspect_ids: ["all_aspects"])
expect(response.status).to eq(302)
status_message = StatusMessage.find_by_text(text)
expect(status_message.aspect_visibilities.map(&:aspect)).to match_array([@aspect1, @aspect2])
end
it "takes all_aspects as string in aspect_ids" do
post :create, status_message_hash.merge(aspect_ids: "all_aspects")
expect(response.status).to eq(302)
status_message = StatusMessage.find_by_text(text)
expect(status_message.aspect_visibilities.map(&:aspect)).to match_array([@aspect1, @aspect2])
end
end end
it "dispatches the post to the specified services" do it "dispatches the post to the specified services" do
@ -127,7 +165,7 @@ describe StatusMessagesController, :type => :controller do
it "doesn't overwrite author_id" do it "doesn't overwrite author_id" do
status_message_hash[:status_message][:author_id] = bob.person.id status_message_hash[:status_message][:author_id] = bob.person.id
post :create, status_message_hash post :create, status_message_hash
new_message = StatusMessage.find_by_text(status_message_hash[:status_message][:text]) new_message = StatusMessage.find_by_text(text)
expect(new_message.author_id).to eq(alice.person.id) expect(new_message.author_id).to eq(alice.person.id)
end end
@ -152,15 +190,9 @@ describe StatusMessagesController, :type => :controller do
expect(StatusMessage.first.provider_display_name).to eq('mobile') expect(StatusMessage.first.provider_display_name).to eq('mobile')
end end
# disabled to fix federation
# it 'sends the errors in the body on js' do
# post :create, status_message_hash.merge!(:format => 'js', :status_message => {:text => ''})
# response.body.should include('Status message requires a message or at least one photo')
# end
it "has one participation" do it "has one participation" do
post :create, status_message_hash post :create, status_message_hash
new_message = StatusMessage.find_by_text(status_message_hash[:status_message][:text]) new_message = StatusMessage.find_by_text(text)
expect(new_message.participations.count).to eq(1) expect(new_message.participations.count).to eq(1)
expect(new_message.participations.first.count).to eq(1) expect(new_message.participations.first.count).to eq(1)
end end
@ -185,7 +217,8 @@ describe StatusMessagesController, :type => :controller do
it "attaches all referenced photos" do it "attaches all referenced photos" do
post :create, @hash post :create, @hash
expect(assigns[:status_message].photos.map(&:id)).to match_array([@photo1, @photo2].map(&:id)) status_message = StatusMessage.find_by_text(text)
expect(status_message.photos.map(&:id)).to match_array([@photo1, @photo2].map(&:id))
end end
it "sets the pending bit of referenced photos" do it "sets the pending bit of referenced photos" do

View file

@ -415,56 +415,4 @@ describe Post, :type => :model do
expect(post.interacted_at).not_to be_blank expect(post.interacted_at).not_to be_blank
end end
end end
describe "#find_public" do
it "succeeds with an id" do
post = FactoryGirl.create :status_message, public: true
expect(Post.find_public post.id).to eq(post)
end
it "succeeds with an guid" do
post = FactoryGirl.create :status_message, public: true
expect(Post.find_public post.guid).to eq(post)
end
it "raises ActiveRecord::RecordNotFound for a non-existing id without a user" do
allow(Post).to receive_messages where: double(includes: double(first: nil))
expect {
Post.find_public 123
}.to raise_error ActiveRecord::RecordNotFound
end
it "raises Diaspora::NonPublic for a private post without a user" do
post = FactoryGirl.create :status_message
expect {
Post.find_public post.id
}.to raise_error Diaspora::NonPublic
end
end
describe "#find_non_public_by_guid_or_id_with_user" do
it "succeeds with an id" do
post = FactoryGirl.create :status_message_in_aspect
expect(Post.find_non_public_by_guid_or_id_with_user(post.id, post.author.owner)).to eq(post)
end
it "succeeds with an guid" do
post = FactoryGirl.create :status_message_in_aspect
expect(Post.find_non_public_by_guid_or_id_with_user(post.guid, post.author.owner)).to eq(post)
end
it "looks up on the passed user object if it's non-nil" do
post = FactoryGirl.create :status_message
user = double
expect(user).to receive(:find_visible_shareable_by_id).with(Post, post.id, key: :id).and_return(post)
Post.find_non_public_by_guid_or_id_with_user(post.id, user)
end
it "raises ActiveRecord::RecordNotFound with a non-existing id and a user" do
user = double(find_visible_shareable_by_id: nil)
expect {
Post.find_non_public_by_guid_or_id_with_user(123, user)
}.to raise_error ActiveRecord::RecordNotFound
end
end
end end

View file

@ -0,0 +1,102 @@
require "spec_helper"
describe CommentService do
let(:post) { alice.post(:status_message, text: "hello", to: alice.aspects.first) }
describe "#create" do
it "creates a comment on my own post" do
comment = CommentService.new(alice).create(post.id, "hi")
expect(comment.text).to eq("hi")
end
it "creates a comment on post of a contact" do
comment = CommentService.new(bob).create(post.id, "hi")
expect(comment.text).to eq("hi")
end
it "attaches the comment to the post" do
comment = CommentService.new(alice).create(post.id, "hi")
expect(post.comments.first.text).to eq("hi")
expect(post.comments.first.id).to eq(comment.id)
end
it "fail if the post does not exist" do
expect {
CommentService.new(alice).create("unknown id", "hi")
}.to raise_error ActiveRecord::RecordNotFound
end
it "fail if the user can not see the post" do
expect {
CommentService.new(eve).create("unknown id", "hi")
}.to raise_error ActiveRecord::RecordNotFound
end
end
describe "#destroy" do
let(:comment) { CommentService.new(bob).create(post.id, "hi") }
it "lets the user destroy his own comment" do
result = CommentService.new(bob).destroy(comment.id)
expect(result).to be_truthy
end
it "lets the parent author destroy others comment" do
result = CommentService.new(alice).destroy(comment.id)
expect(result).to be_truthy
end
it "does not let someone destroy others comment" do
result = CommentService.new(eve).destroy(comment.id)
expect(result).to be_falsey
end
it "fails if the comment does not exist" do
expect {
CommentService.new(bob).destroy("unknown id")
}.to raise_error ActiveRecord::RecordNotFound
end
end
describe "#find_for_post" do
context "with user" do
it "returns comments for a public post" do
post = alice.post(:status_message, text: "hello", public: true)
comment = CommentService.new(alice).create(post.id, "hi")
expect(CommentService.new(eve).find_for_post(post.id)).to include(comment)
end
it "returns comments for a visible private post" do
comment = CommentService.new(alice).create(post.id, "hi")
expect(CommentService.new(bob).find_for_post(post.id)).to include(comment)
end
it "does not return comments for private post the user can not see" do
expect {
CommentService.new(eve).find_for_post(post.id)
}.to raise_error ActiveRecord::RecordNotFound
end
end
context "without user" do
it "returns comments for a public post" do
post = alice.post(:status_message, text: "hello", public: true)
comment = CommentService.new(alice).create(post.id, "hi")
expect(CommentService.new.find_for_post(post.id)).to include(comment)
end
it "does not return comments for private post" do
expect {
CommentService.new.find_for_post(post.id)
}.to raise_error Diaspora::NonPublic
end
end
it "returns all comments of a post" do
post = alice.post(:status_message, text: "hello", public: true)
comments = [alice, bob, eve].map {|user| CommentService.new(user).create(post.id, "hi") }
expect(CommentService.new.find_for_post(post.id)).to eq(comments)
end
end
end

View file

@ -1,127 +1,188 @@
require "spec_helper" require "spec_helper"
describe PostService do describe PostService do
before do let(:post) { alice.post(:status_message, text: "ohai", to: alice.aspects.first) }
aspect = alice.aspects.first let(:public) { alice.post(:status_message, text: "hey", public: true) }
@message = alice.build_post :status_message, text: "ohai", to: aspect.id
@message.save!
alice.add_to_streams(@message, [aspect]) describe "#find" do
alice.dispatch_post @message, to: aspect.id 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 end
describe "#assign_post" do
context "when the post is private" do
it "RecordNotFound if the post cannot be found" do it "RecordNotFound if the post cannot be found" do
expect { PostService.new(id: 1_234_567, user: alice) }.to raise_error(ActiveRecord::RecordNotFound) expect {
PostService.new(alice).find!("unknown")
}.to raise_error ActiveRecord::RecordNotFound, "could not find a post with id unknown for user #{alice.id}"
end end
it "NonPublic if there is no user" do
expect { PostService.new(id: @message.id) }.to raise_error(Diaspora::NonPublic) it "RecordNotFound if user cannot see the post" do
end expect {
it "RecordNotFound if user cannot see post" do PostService.new(eve).find!(post.id)
expect { PostService.new(id: @message.id, user: eve) }.to raise_error(ActiveRecord::RecordNotFound) }.to raise_error ActiveRecord::RecordNotFound, "could not find a post with id #{post.id} for user #{eve.id}"
end end
end end
context "when the post is public" do 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 it "RecordNotFound if the post cannot be found" do
expect { PostService.new(id: 1_234_567) }.to raise_error(ActiveRecord::RecordNotFound) expect {
PostService.new.find!("unknown")
}.to raise_error ActiveRecord::RecordNotFound, "could not find a post with id unknown"
end end
end end
# We want to be using guids from now on for this post route, but do not want to break
# pre-exisiting permalinks. We can assume a guid is 8 characters long as we have
# guids set to hex(8) since we started using them.
context "id/guid switch" do context "id/guid switch" do
before do let(:public) { alice.post(:status_message, text: "ohai", public: true) }
@status = alice.post(:status_message, text: "hello", public: true, to: "all")
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 end
it "assumes guids less than 8 chars are ids and not guids" do it "assumes ids more than (or equal to) 16 chars are actually guids" do
post = Post.where(id: @status.id.to_s) post = Post.where(guid: public.guid)
expect(Post).to receive(:where).with(hash_including(id: @status.id)).and_return(post).at_least(:once) expect(Post).to receive(:where).with(hash_including(guid: "1234567890123456")).and_return(post).at_least(:once)
PostService.new(id: @status.id, user: alice) PostService.new(alice).find!("1234567890123456")
end
it "assumes guids more than (or equal to) 8 chars are actually guids" do
post = Post.where(guid: @status.guid)
expect(Post).to receive(:where).with(hash_including(guid: @status.guid)).and_return(post).at_least(:once)
PostService.new(id: @status.guid, user: alice)
end end
end end
end end
describe "#mark_user_notifications" do describe "#mark_user_notifications" do
it "marks a corresponding notifications as read" do it "marks a corresponding notifications as read" do
FactoryGirl.create(:notification, recipient: alice, target: @message, unread: true) FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
FactoryGirl.create(:notification, recipient: alice, target: @message, unread: true) FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
post_service = PostService.new(id: @message.id, user: alice)
expect { post_service.mark_user_notifications }.to change(Notification.where(unread: true), :count).by(-2) expect {
PostService.new(alice).mark_user_notifications(post.id)
}.to change(Notification.where(unread: true), :count).by(-2)
end end
it "marks a corresponding mention notification as read" do it "marks a corresponding mention notification as read" do
status_text = "this is a text mentioning @{Mention User ; #{alice.diaspora_handle}} ... have fun testing!" status_text = "this is a text mentioning @{Mention User ; #{alice.diaspora_handle}} ... have fun testing!"
status_msg = mention_post = bob.post(:status_message, text: status_text, public: true)
bob.post(:status_message, text: status_text, public: true, to: "all")
mention = status_msg.mentions.where(person_id: alice.person.id).first expect {
FactoryGirl.create(:notification, recipient: alice, target_type: "Mention", target_id: mention.id, unread: true) PostService.new(alice).mark_user_notifications(mention_post.id)
post_service = PostService.new(id: status_msg.id, user: alice) }.to change(Notification.where(unread: true), :count).by(-1)
expect { post_service.mark_user_notifications }.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
status_text = "this is a text mentioning @{Mention User ; #{alice.diaspora_handle}} ... have fun testing!"
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
end end
describe "#present_json" do describe "#destroy" do
it "works for a private post" do
post_service = PostService.new(id: @message.id, user: alice)
expect(post_service.present_json.to_json).to match(/\"text\"\:\"ohai\"/)
end
it "works for a public post " do
status = alice.post(:status_message, text: "hello", public: true, to: "all")
post_service = PostService.new(id: status.id)
expect(post_service.present_json.to_json).to match(/\"text\"\:\"hello\"/)
end
end
describe "#present_oembed" do
it "works for a private post" do
post_service = PostService.new(id: @message.id, user: alice)
expect(post_service.present_oembed.to_json).to match(/iframe/)
end
it "works for a public post" do
status = alice.post(:status_message, text: "hello", public: true, to: "all")
post_service = PostService.new(id: status.id)
expect(post_service.present_oembed.to_json).to match(/iframe/)
end
end
describe "#retract_post" do
it "let a user delete his message" do it "let a user delete his message" do
message = alice.post(:status_message, text: "hey", to: alice.aspects.first.id) PostService.new(alice).destroy(post.id)
post_service = PostService.new(id: message.id, user: alice) expect(StatusMessage.find_by_id(post.id)).to be_nil
post_service.retract_post
expect(StatusMessage.find_by_id(message.id)).to be_nil
end end
it "sends a retraction on delete" do it "sends a retraction on delete" do
message = alice.post(:status_message, text: "hey", to: alice.aspects.first.id) expect(alice).to receive(:retract).with(post)
post_service = PostService.new(id: message.id, user: alice) PostService.new(alice).destroy(post.id)
expect(alice).to receive(:retract).with(message)
post_service.retract_post
end end
it "will not let you destroy posts visible to you but that you do not own" do it "will not let you destroy posts visible to you but that you do not own" do
message = bob.post(:status_message, text: "hey", to: bob.aspects.first.id) expect {
post_service = PostService.new(id: message.id, user: alice) PostService.new(bob).destroy(post.id)
expect { post_service.retract_post }.to raise_error(Diaspora::NotMine) }.to raise_error Diaspora::NotMine
expect(StatusMessage.exists?(message.id)).to be true expect(StatusMessage.find_by_id(post.id)).not_to be_nil
end end
it "will not let you destroy posts that are not visible to you" do it "will not let you destroy posts that are not visible to you" do
message = eve.post(:status_message, text: "hey", to: eve.aspects.first.id) expect {
expect { PostService.new(id: message.id, user: alice) }.to raise_error(ActiveRecord::RecordNotFound) PostService.new(eve).destroy(post.id)
expect(StatusMessage.exists?(message.id)).to be true }.to raise_error(ActiveRecord::RecordNotFound)
expect(StatusMessage.find_by_id(post.id)).not_to be_nil
end end
end end
end end

View file

@ -0,0 +1,136 @@
require "spec_helper"
describe StatusMessageCreationService do
describe "#create" do
let(:aspect) { alice.aspects.first }
let(:text) { "I'm writing tests" }
let(:params) {
{
status_message: {text: text},
aspect_ids: [aspect.id.to_s]
}
}
it "returns the created StatusMessage" do
status_message = StatusMessageCreationService.new(alice).create(params)
expect(status_message).to_not be_nil
expect(status_message.text).to eq(text)
end
context "with aspect_ids" do
it "creates aspect_visibilities for the StatusMessages" do
alice.aspects.create(name: "another aspect")
status_message = StatusMessageCreationService.new(alice).create(params)
expect(status_message.aspect_visibilities.map(&:aspect)).to eq([aspect])
end
it "does not create aspect_visibilities if the post is public" do
status_message = StatusMessageCreationService.new(alice).create(params.merge(public: true))
expect(status_message.aspect_visibilities).to be_empty
end
end
context "with public" do
it "it creates a private StatusMessage by default" do
status_message = StatusMessageCreationService.new(alice).create(params)
expect(status_message.public).to be_falsey
end
it "it creates a private StatusMessage" do
status_message = StatusMessageCreationService.new(alice).create(params.merge(public: false))
expect(status_message.public).to be_falsey
end
it "it creates a public StatusMessage" do
status_message = StatusMessageCreationService.new(alice).create(params.merge(public: true))
expect(status_message.public).to be_truthy
end
end
context "with location" do
it "it creates a location" do
location_params = {location_address: "somewhere", location_coords: "1,2"}
status_message = StatusMessageCreationService.new(alice).create(params.merge(location_params))
location = status_message.location
expect(location.address).to eq("somewhere")
expect(location.lat).to eq("1")
expect(location.lng).to eq("2")
end
it "does not add a location without location params" do
status_message = StatusMessageCreationService.new(alice).create(params)
expect(status_message.location).to be_nil
end
end
context "with poll" do
it "it creates a poll" do
poll_params = {poll_question: "something?", poll_answers: %w(yes no maybe)}
status_message = StatusMessageCreationService.new(alice).create(params.merge(poll_params))
poll = status_message.poll
expect(poll.question).to eq("something?")
expect(poll.poll_answers.size).to eq(3)
poll_answers = poll.poll_answers.map(&:answer)
expect(poll_answers).to include("yes")
expect(poll_answers).to include("no")
expect(poll_answers).to include("maybe")
end
it "does not add a poll without poll params" do
status_message = StatusMessageCreationService.new(alice).create(params)
expect(status_message.poll).to be_nil
end
end
context "with photos" do
let(:photo1) {
alice.build_post(:photo, pending: true, user_file: File.open(photo_fixture_name), to: aspect.id).tap(&:save!)
}
let(:photo2) {
alice.build_post(:photo, pending: true, user_file: File.open(photo_fixture_name), to: aspect.id).tap(&:save!)
}
let(:photo_ids) { [photo1.id.to_s, photo2.id.to_s] }
it "it attaches all photos" do
status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids))
photos = status_message.photos
expect(photos.size).to eq(2)
expect(photos.map(&:id).map(&:to_s)).to eq(photo_ids)
end
it "it marks the photos as non-public if the post is non-public" do
status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids, public: false))
status_message.photos.each do |photo|
expect(photo.public).to be_falsey
end
end
it "it marks the photos as public if the post is public" do
status_message = StatusMessageCreationService.new(alice).create(params.merge(photos: photo_ids, public: true))
status_message.photos.each do |photo|
expect(photo.public).to be_truthy
end
end
it "does not attach photos without photos param" do
status_message = StatusMessageCreationService.new(alice).create(params)
expect(status_message.photos).to be_empty
end
end
context "dispatch" do
it "dispatches the StatusMessage" do
expect(alice).to receive(:dispatch_post).with(instance_of(StatusMessage), hash_including(service_types: []))
StatusMessageCreationService.new(alice).create(params)
end
it "dispatches the StatusMessage to services" do
expect(alice).to receive(:dispatch_post)
.with(instance_of(StatusMessage),
hash_including(service_types: array_including(%w(Services::Facebook Services::Twitter))))
StatusMessageCreationService.new(alice).create(params.merge(services: %w(twitter facebook)))
end
end
end
end