diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index b8bdb3a4c..9abc7503b 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -12,17 +12,16 @@ class CommentsController < ApplicationController end def create - @comment = CommentService.new(post_id: params[:post_id], text: params[:text], user: current_user).create_comment - if @comment - respond_create_success + comment = comment_service.create(params[:post_id], params[:text]) + if comment + respond_create_success(comment) else render nothing: true, status: 404 end end def destroy - service = CommentService.new(comment_id: params[:id], user: current_user) - if service.destroy_comment + if comment_service.destroy(params[:id]) respond_destroy_success else respond_destroy_error @@ -36,22 +35,24 @@ class CommentsController < ApplicationController end def index - service = CommentService.new(post_id: params[:post_id], user: current_user) - @post = service.post - @comments = service.comments + comments = comment_service.find_for_post(params[:post_id]) respond_with do |format| - format.json { render json: CommentPresenter.as_collection(@comments), status: 200 } - format.mobile { render layout: false } + format.json { render json: CommentPresenter.as_collection(comments), status: 200 } + format.mobile { render layout: false, locals: {comments: comments} } end end private - def respond_create_success + def comment_service + @comment_service ||= CommentService.new(current_user) + end + + def respond_create_success(comment) 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.mobile { render partial: "comment", locals: {post: @comment.post, comment: @comment} } + format.mobile { render partial: "comment", locals: {comment: comment} } end end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 87fef27a6..8417dc172 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -3,22 +3,13 @@ # the COPYRIGHT file. class PostsController < ApplicationController - include PostsHelper - before_action :authenticate_user!, only: :destroy before_action :set_format_if_malformed_from_status_net, only: :show respond_to :html, :mobile, :json, :xml 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! - end + authenticate_user! end rescue_from Diaspora::NotMine do @@ -26,38 +17,36 @@ class PostsController < ApplicationController end def show - post_service = PostService.new(id: params[:id], user: current_user) - post_service.mark_user_notifications - @post = post_service.post + post = post_service.find!(params[:id]) + post_service.mark_user_notifications(post.id) respond_to do |format| - format.html { gon.post = post_service.present_json } - format.xml { render xml: @post.to_diaspora_xml } - format.json { render json: post_service.present_json } + format.html { + gon.post = PostPresenter.new(post, current_user) + 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 - def iframe - render text: post_iframe_url(params[:id]), layout: false - end - def oembed post_id = OEmbedPresenter.id_from_url(params.delete(:url)) - post_service = PostService.new(id: post_id, user: current_user, - oembed: params.slice(:format, :maxheight, :minheight)) - render json: post_service.present_oembed + post = post_service.find!(post_id) + oembed = params.slice(:format, :maxheight, :minheight) + render json: OEmbedPresenter.new(post, oembed) + rescue + render nothing: true, status: 404 end def interactions - post_service = PostService.new(id: params[:id], user: current_user) - respond_with post_service.present_interactions_json + post = post_service.find!(params[:id]) + respond_with PostInteractionPresenter.new(post, current_user) end def destroy - post_service = PostService.new(id: params[:id], user: current_user) - post_service.retract_post - @post = post_service.post + post_service.destroy(params[:id]) respond_to do |format| - format.js { render "destroy", layout: false, format: :js } format.json { render nothing: true, status: 204 } format.any { redirect_to stream_path } end @@ -65,6 +54,10 @@ class PostsController < ApplicationController private + def post_service + @post_service ||= PostService.new(current_user) + end + def set_format_if_malformed_from_status_net request.format = :html if request.format == "application/html+xml" end diff --git a/app/controllers/status_messages_controller.rb b/app/controllers/status_messages_controller.rb index a7a4ebea0..87db1237f 100644 --- a/app/controllers/status_messages_controller.rb +++ b/app/controllers/status_messages_controller.rb @@ -47,12 +47,17 @@ class StatusMessagesController < ApplicationController end def create - @status_message = StatusMessageCreationService.new(params, current_user).status_message - handle_mention_feedback + normalized_params = params.merge( + 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| format.html { redirect_to :back } 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 rescue StandardError => error handle_create_error(error) @@ -73,9 +78,9 @@ class StatusMessagesController < ApplicationController end end - def handle_mention_feedback + def handle_mention_feedback(status_message) 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 def comes_from_others_profile_page? @@ -87,11 +92,24 @@ class StatusMessagesController < ApplicationController end 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 - def successful_mention_message - t("status_messages.create.success", names: @status_message.mentioned_people_names) + def normalize_services + [*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 def remove_getting_started diff --git a/app/models/post.rb b/app/models/post.rb index b7b794447..77681c070 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -133,12 +133,10 @@ class Post < ActiveRecord::Base ############# def self.diaspora_initialize(params) - new_post = self.new params.to_hash.stringify_keys.slice(*self.column_names) - 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 + new(params.to_hash.stringify_keys.slice(*column_names)).tap do |new_post| + new_post.author = params[:author] + new_post.diaspora_handle = new_post.author.diaspora_handle + end end # @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 self.author.profile.nsfw? 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 diff --git a/app/services/comment_service.rb b/app/services/comment_service.rb index a571db375..4ec3b8835 100644 --- a/app/services/comment_service.rb +++ b/app/services/comment_service.rb @@ -1,43 +1,32 @@ class CommentService - attr_reader :post, :comments - - 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 + def initialize(user=nil) + @user = user end - def create_comment - @user.comment!(post, @text) if @post + def create(post_id, text) + post = post_service.find!(post_id) + user.comment!(post, text) end - def destroy_comment - @comment = Comment.find(@comment_id) - if @user.owns?(@comment) || @user.owns?(@comment.parent) - @user.retract(@comment) + def destroy(comment_id) + comment = Comment.find(comment_id) + if user.owns?(comment) || user.owns?(comment.parent) + user.retract(comment) true else false end end + def find_for_post(post_id) + post_service.find!(post_id).comments.for_a_stream + end + private - def find_post! - find_post.tap do |post| - raise(ActiveRecord::RecordNotFound) unless post - end - end + attr_reader :user - def find_post - if @user - @user.find_visible_shareable_by_id(Post, @post_id) - else - Post.find_by_id_and_public(@post_id, true) - end + def post_service + @post_service ||= PostService.new(user) end end diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 1408d0642..140c33a0d 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -1,65 +1,66 @@ class PostService - attr_reader :post - - def initialize(params) - @id = params[:id] - @user = params[:user] - @oembed = params[:oembed] || {} - assign_post + def initialize(user=nil) + @user = user end - def assign_post + def find(id) if user - @post = Post.find_non_public_by_guid_or_id_with_user(id, user) + user.find_visible_shareable_by_id(Post, id) else - @post = Post.find_public(id) + Post.find_by_id_and_public(id, true) end end - def present_json - PostPresenter.new(post, user) + def find!(id_or_guid) + if user + find_non_public_by_guid_or_id_with_user!(id_or_guid) + else + find_public!(id_or_guid) + end end - def present_interactions_json - PostInteractionPresenter.new(post, user) + def mark_user_notifications(post_id) + return unless user + mark_comment_reshare_like_notifications_read(post_id) + mark_mention_notifications_read(post_id) end - def present_oembed - OEmbedPresenter.new(post, oembed) - end - - def mark_user_notifications - mark_corresponding_notifications_read if user - end - - def retract_post - raise Diaspora::NotMine unless user_owns_post? - user.retract(@post) + def destroy(post_id) + post = find!(post_id) + raise Diaspora::NotMine unless post.author == user.person + user.retract(post) end private - attr_reader :user, :id, :oembed + attr_reader :user - def user_owns_post? - post.author == user.person - end - - 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) + def find_public!(id_or_guid) + Post.where(post_key(id_or_guid) => id_or_guid).first.tap do |post| + raise ActiveRecord::RecordNotFound, "could not find a post with id #{id_or_guid}" unless post + raise Diaspora::NonPublic unless post.public? end end - def mark_mention_notifications_read - mention = post.mentions.where(person_id: user.person_id).first - Notification.where(recipient_id: user.id, target_type: "Mention", target_id: mention.id, unread: true) - .first.try(:set_read_state, true) if mention + def find_non_public_by_guid_or_id_with_user!(id_or_guid) + user.find_visible_shareable_by_id(Post, id_or_guid, key: post_key(id_or_guid)).tap do |post| + raise ActiveRecord::RecordNotFound, "could not find a post with id #{id_or_guid} for user #{user.id}" unless post + 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 diff --git a/app/services/status_message_creation_service.rb b/app/services/status_message_creation_service.rb index d1de5c555..9c06d2687 100644 --- a/app/services/status_message_creation_service.rb +++ b/app/services/status_message_creation_service.rb @@ -1,89 +1,66 @@ class StatusMessageCreationService include Rails.application.routes.url_helpers - attr_reader :status_message + def initialize(user) + @user = user + end - def initialize(params, user) - normalize_params(params, user) - status_message_initial = user.build_post(:status_message, params[:status_message]) - @status_message = add_attachments(params, status_message_initial) - @status_message.save - process_status_message(user) + def create(params) + build_status_message(params).tap do |status_message| + add_attachments(status_message, params) + status_message.save + process(status_message, params[:aspect_ids], params[:services]) + end end private - attr_reader :services, :destination_aspect_ids + attr_reader :user - def normalize_params(params, user) - normalize_aspect_ids(params) - normalize_public_flag!(params) - @services = [*params[:services]].compact - @destination_aspect_ids = destination_aspect_ids(params, user) + def build_status_message(params) + public = params[:public] || false + user.build_post(:status_message, params[:status_message].merge(public: public)) end - def normalize_aspect_ids(params) - params[:status_message][:aspect_ids] = [*params[:aspect_ids]] + def add_attachments(status_message, params) + add_location(status_message, params[:location_address], params[:location_coords]) + add_poll(status_message, params) + add_photos(status_message, params[:photos]) end - def normalize_public_flag!(params) - 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] + def add_location(status_message, address, coordinates) status_message.build_location(address: address, coordinates: coordinates) if address.present? - status_message end - def add_poll(params, status_message) + def add_poll(status_message, params) if params[:poll_question].present? status_message.build_poll(question: params[:poll_question]) [*params[:poll_answers]].each do |poll_answer| status_message.poll.poll_answers.build(answer: poll_answer) end end - status_message end - def add_photos(params, status_message) - status_message.attach_photos_by_ids(params[:photos]) - status_message + def add_photos(status_message, photos) + status_message.attach_photos_by_ids(photos) + status_message.photos.each {|photo| photo.public = status_message.public } end - def process_status_message(user) - add_status_message_to_streams(user) - dispatch_status_message(user) - user.participate!(@status_message) + def process(status_message, aspect_ids, services) + add_to_streams(status_message, aspect_ids) unless status_message.public + dispatch(status_message, services) + user.participate!(status_message) end - def add_status_message_to_streams(user) - aspects = user.aspects_from_ids(@destination_aspect_ids) - user.add_to_streams(@status_message, aspects) + def add_to_streams(status_message, aspect_ids) + aspects = user.aspects_from_ids(aspect_ids) + user.add_to_streams(status_message, aspects) end - def dispatch_status_message(user) - receiving_services = Service.titles(@services) - user.dispatch_post(@status_message, - url: short_post_url(@status_message.guid, host: AppConfig.environment.url), + def dispatch(status_message, services) + receiving_services = services ? Service.titles(services) : [] + user.dispatch_post(status_message, + url: short_post_url(status_message.guid, host: AppConfig.environment.url), service_types: receiving_services) end end diff --git a/app/views/comments/index.mobile.haml b/app/views/comments/index.mobile.haml index 6f0d545e2..bbb26073a 100644 --- a/app/views/comments/index.mobile.haml +++ b/app/views/comments/index.mobile.haml @@ -1,3 +1,3 @@ .comment-container %ul.comments - = render partial: "comments/comment", collection: @post.comments.for_a_stream, locals: {post: @post} + = render partial: "comments/comment", collection: comments diff --git a/app/views/errors/not_public.haml b/app/views/errors/not_public.haml deleted file mode 100644 index bcd908b26..000000000 --- a/app/views/errors/not_public.haml +++ /dev/null @@ -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 diff --git a/app/views/posts/destroy.js.erb b/app/views/posts/destroy.js.erb deleted file mode 100644 index 5a0c3dd83..000000000 --- a/app/views/posts/destroy.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -var target = $("#<%= @post.guid %>") -target.hide('blind', { direction: 'vertical' }, 300, function(){ target.remove() }); diff --git a/app/views/posts/show.html.haml b/app/views/posts/show.html.haml index 6b09af4df..6798f31ce 100644 --- a/app/views/posts/show.html.haml +++ b/app/views/posts/show.html.haml @@ -3,7 +3,7 @@ -# the COPYRIGHT file. - content_for :page_title do - = post_page_title @post + = post_page_title post - content_for :content do #container.container-fluid diff --git a/app/views/posts/show.mobile.haml b/app/views/posts/show.mobile.haml index 63bf09376..61d8556e8 100644 --- a/app/views/posts/show.mobile.haml +++ b/app/views/posts/show.mobile.haml @@ -3,6 +3,6 @@ -# the COPYRIGHT file. .stream - = render :partial => 'shared/stream_element', - :locals => {:post => @post, :commenting_disabled => commenting_disabled?(@post), :expanded_info => true} + = render partial: "shared/stream_element", + locals: {post: post, commenting_disabled: commenting_disabled?(post), expanded_info: true} diff --git a/app/views/shared/_stream_element.mobile.haml b/app/views/shared/_stream_element.mobile.haml index bb6869503..237a36af6 100644 --- a/app/views/shared/_stream_element.mobile.haml +++ b/app/views/shared/_stream_element.mobile.haml @@ -26,7 +26,7 @@ != reactions_link(post, "active") .comment-container %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 != reactions_link(post) diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index 938196af9..45a531106 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -91,9 +91,6 @@ en: helper: invalid_fields: "Invalid fields" 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 login and try again." admins: admin_bar: diff --git a/config/routes.rb b/config/routes.rb index e06d98c84..292cc1fb8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,10 +40,7 @@ Diaspora::Application.routes.draw do resources :comments, only: %i(new create destroy index) end - - get 'p/:id' => 'posts#show', :as => 'short_post' - get 'posts/:id/iframe' => 'posts#iframe', :as => 'iframe' # roll up likes into a nested resource above resources :comments, :only => [:create, :destroy] do diff --git a/features/step_definitions/message_steps.rb b/features/step_definitions/message_steps.rb index 43399d007..a85c1bd9d 100644 --- a/features/step_definitions/message_steps.rb +++ b/features/step_definitions/message_steps.rb @@ -5,8 +5,6 @@ Then /^I should see the "(.*)" message$/ do |message| I18n.translate('invitation_codes.excited', :name => @alice.name) when "welcome to diaspora" I18n.translate('users.getting_started.well_hello_there') - when 'post not public' - I18n.translate('error_messages.post_not_public_or_not_exist') else raise "muriel, you don't have that message key, add one here" end diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index adb2ec1ec..bd9bb899f 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -140,7 +140,7 @@ describe CommentsController, :type => :controller do comments = [alice, bob, eve].map{ |u| u.comment!(@message, "hey") } 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 it 'returns a 404 on a nonexistent post' do diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index 8da4502f3..c8d2d215d 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -5,104 +5,86 @@ require "spec_helper" describe PostsController, type: :controller do - let!(:post_service_double) { double("post_service") } - - 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 + let(:post) { alice.post(:status_message, text: "ohai", to: alice.aspects.first) } 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 "given a post that the user is allowed to see" do before do sign_in :user, alice - expect(post_service_double).to receive(:post).and_return(@message) end 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 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") 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.save! + + msg = alice.post(:status_message, text: "Mention @{User ; #{user.diaspora_handle}}", public: true) + expect(msg.mentioned_people.count).to eq(1) user.destroy + get :show, id: msg.id expect(response).to be_success end 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") end 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 end end context "given a post that the user is not allowed to see" do before do - sign_in :user, alice - expect(post_service_double).to receive(:post).and_raise(Diaspora::NonPublic) + sign_in :user, eve end it "returns a 404" do - get :show, id: @message.id - expect(response.code).to eq("404") + expect { + get :show, id: post.id + }.to raise_error ActiveRecord::RecordNotFound end end end context "user not signed in" do context "given a public post" do - before :each do - @status = alice.post(:status_message, text: "hello", public: true, to: "all") - expect(post_service_double).to receive(:post).and_return(@status) - end + let(:public) { alice.post(:status_message, text: "hello", public: true) } it "shows a public post" do - get :show, id: @status.id + get :show, id: public.id expect(response.body).to match "hello" end it "succeeds for statusnet" do @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" end it "responds with diaspora xml if format is xml" do - get :show, id: @status.guid, format: :xml - expect(response.body).to eq(@status.to_diaspora_xml) + get :show, id: public.guid, format: :xml + expect(response.body).to eq(public.to_diaspora_xml) end end 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 - get :show, id: @message.id + get :show, id: post.id expect(response).to be_redirect expect(response).to redirect_to new_user_session_path end @@ -110,40 +92,55 @@ describe PostsController, type: :controller do end end - describe "iframe" do - it "contains an iframe" do - get :iframe, id: @message.id + describe "oembed" do + it "works when you can see it" do + sign_in alice + get :oembed, url: "/posts/#{post.id}" expect(response.body).to match /iframe/ end - end - describe "oembed" do - it "receives a present oembed" do - expect(post_service_double).to receive(:present_oembed) - get :oembed, url: "/posts/#{@message.id}" + it "returns a 404 response when the post is not found" do + get :oembed, url: "/posts/#{post.id}" + expect(response.status).to eq(404) end end describe "#destroy" do - before do - sign_in alice + context "own post" do + before do + sign_in alice + end + + it "works when it is your post" do + expect_any_instance_of(PostService).to receive(:destroy).with(post.id.to_s) + + delete :destroy, format: :json, id: post.id + expect(response.status).to eq(204) + end + + 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 - it "will receive a retract post" do - expect(post_service_double).to receive(:retract_post) - 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: :js, id: message.id - end - - context "when Diaspora::NotMine is raised by retract post" do + context "post of another user" do it "will respond with a 403" do - expect(post_service_double).to receive(:retract_post).and_raise(Diaspora::NotMine) - message = alice.post(:status_message, text: "hey", to: alice.aspects.first.id) - delete :destroy, format: :js, id: message.id + sign_in :user, bob + + delete :destroy, format: :json, id: post.id expect(response.body).to eq("You are not allowed to do that") expect(response.status).to eq(403) 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 diff --git a/spec/controllers/status_messages_controller_spec.rb b/spec/controllers/status_messages_controller_spec.rb index 5fc8ea7ea..120b6d8be 100644 --- a/spec/controllers/status_messages_controller_spec.rb +++ b/spec/controllers/status_messages_controller_spec.rb @@ -49,12 +49,12 @@ describe StatusMessagesController, :type => :controller do end describe '#create' do + let(:text) { "facebook, is that you?" } let(:status_message_hash) { - { :status_message => { - :public => "true", - :text => "facebook, is that you?", - }, - :aspect_ids => [@aspect1.id.to_s] } + { + status_message: {text: text}, + aspect_ids: [@aspect1.id.to_s] + } } it 'creates with valid html' do @@ -96,14 +96,52 @@ describe StatusMessagesController, :type => :controller do post :create, status_message_hash end - it 'takes public in aspect ids' do - post :create, status_message_hash.merge(:aspect_ids => ['public']) - expect(response.status).to eq(302) - end + context "with aspect_ids" do + before do + @aspect2 = alice.aspects.create(name: "another aspect") + end - it 'takes all_aspects in aspect ids' do - post :create, status_message_hash.merge(:aspect_ids => ['all_aspects']) - expect(response.status).to eq(302) + it "takes one aspect as array in aspect_ids" do + post :create, status_message_hash + 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 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 status_message_hash[:status_message][:author_id] = bob.person.id 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) end @@ -152,15 +190,9 @@ describe StatusMessagesController, :type => :controller do expect(StatusMessage.first.provider_display_name).to eq('mobile') 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 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.first.count).to eq(1) end @@ -185,7 +217,8 @@ describe StatusMessagesController, :type => :controller do it "attaches all referenced photos" do 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 it "sets the pending bit of referenced photos" do diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 3a988f2a2..c6f33d6c1 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -415,56 +415,4 @@ describe Post, :type => :model do expect(post.interacted_at).not_to be_blank 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 diff --git a/spec/services/comment_service_spec.rb b/spec/services/comment_service_spec.rb new file mode 100644 index 000000000..d6b24cfb7 --- /dev/null +++ b/spec/services/comment_service_spec.rb @@ -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 diff --git a/spec/services/post_service_spec.rb b/spec/services/post_service_spec.rb index 47f57d750..2f2d4764f 100644 --- a/spec/services/post_service_spec.rb +++ b/spec/services/post_service_spec.rb @@ -1,127 +1,188 @@ require "spec_helper" describe PostService do - before do - aspect = alice.aspects.first - @message = alice.build_post :status_message, text: "ohai", to: aspect.id - @message.save! + let(:post) { alice.post(:status_message, text: "ohai", to: alice.aspects.first) } + let(:public) { alice.post(:status_message, text: "hey", public: true) } - alice.add_to_streams(@message, [aspect]) - alice.dispatch_post @message, to: aspect.id + 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 "#assign_post" do - context "when the post is private" do + 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(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 - it "NonPublic if there is no user" do - expect { PostService.new(id: @message.id) }.to raise_error(Diaspora::NonPublic) - end - it "RecordNotFound if user cannot see post" do - expect { PostService.new(id: @message.id, user: eve) }.to raise_error(ActiveRecord::RecordNotFound) + + 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 "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 - 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 - # 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 - before do - @status = alice.post(:status_message, text: "hello", public: true, to: "all") + 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 guids less than 8 chars are ids and not guids" do - post = Post.where(id: @status.id.to_s) - expect(Post).to receive(:where).with(hash_including(id: @status.id)).and_return(post).at_least(:once) - PostService.new(id: @status.id, user: alice) - 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) + 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 it "marks a corresponding notifications as read" do - FactoryGirl.create(:notification, recipient: alice, target: @message, unread: true) - FactoryGirl.create(:notification, recipient: alice, target: @message, 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) + 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 status_text = "this is a text mentioning @{Mention User ; #{alice.diaspora_handle}} ... have fun testing!" - status_msg = - bob.post(:status_message, text: status_text, public: true, to: "all") - mention = status_msg.mentions.where(person_id: alice.person.id).first - FactoryGirl.create(:notification, recipient: alice, target_type: "Mention", target_id: mention.id, unread: true) - post_service = PostService.new(id: status_msg.id, user: alice) - expect { post_service.mark_user_notifications }.to change(Notification.where(unread: true), :count).by(-1) + 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 "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 - describe "#present_json" 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 + describe "#destroy" do it "let a user delete his message" do - message = alice.post(:status_message, text: "hey", to: alice.aspects.first.id) - post_service = PostService.new(id: message.id, user: alice) - post_service.retract_post - expect(StatusMessage.find_by_id(message.id)).to be_nil + PostService.new(alice).destroy(post.id) + expect(StatusMessage.find_by_id(post.id)).to be_nil end it "sends a retraction on delete" do - message = alice.post(:status_message, text: "hey", to: alice.aspects.first.id) - post_service = PostService.new(id: message.id, user: alice) - expect(alice).to receive(:retract).with(message) - post_service.retract_post + 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 - message = bob.post(:status_message, text: "hey", to: bob.aspects.first.id) - post_service = PostService.new(id: message.id, user: alice) - expect { post_service.retract_post }.to raise_error(Diaspora::NotMine) - expect(StatusMessage.exists?(message.id)).to be true + 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 - message = eve.post(:status_message, text: "hey", to: eve.aspects.first.id) - expect { PostService.new(id: message.id, user: alice) }.to raise_error(ActiveRecord::RecordNotFound) - expect(StatusMessage.exists?(message.id)).to be true + 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 end diff --git a/spec/services/status_message_creation_service_spec.rb b/spec/services/status_message_creation_service_spec.rb new file mode 100644 index 000000000..706f44ebb --- /dev/null +++ b/spec/services/status_message_creation_service_spec.rb @@ -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