Posts API Endpoint feature complete with full unit tests

This commit is contained in:
Hank Grabowski 2018-10-18 22:41:31 -04:00 committed by Frank Rousseau
parent f64a8e04ed
commit bb2261b47d
8 changed files with 695 additions and 64 deletions

View file

@ -13,6 +13,10 @@ module Api
require_access_token %w[read write] require_access_token %w[read write]
end end
rescue_from ActiveRecord::RecordNotFound do
render json: I18n.t("api.endpoint_errors.posts.post_not_found"), status: :not_found
end
def show def show
mark_notifications = mark_notifications =
params[:mark_notifications].present? && params[:mark_notifications] params[:mark_notifications].present? && params[:mark_notifications]
@ -23,37 +27,68 @@ module Api
def create def create
status_service = StatusMessageCreationService.new(current_user) status_service = StatusMessageCreationService.new(current_user)
@status_message = status_service.create(normalized_params) creation_params = normalized_create_params
render json: PostPresenter.new(@status_message, current_user) @status_message = status_service.create(creation_params)
render json: PostPresenter.new(@status_message, current_user).as_api_response
rescue StandardError
render json: I18n.t("api.endpoint_errors.posts.failed_create"), status: :unprocessable_entity
end end
def destroy def destroy
post_service.destroy(params[:id]) post_service.destroy(params[:id])
head :no_content head :no_content
rescue Diaspora::NotMine
render json: I18n.t("api.endpoint_errors.posts.failed_delete"), status: :forbidden
end end
def normalized_params def normalized_create_params
params.permit( mapped_parameters = {
:location_address, status_message: {
:location_coords, text: params.require(:body)
:poll_question, },
status_message: %i[text provider_display_name], public: params.require(:public),
poll_answers: [] aspect_ids: normalize_aspect_ids(params.permit(aspects: []))
).to_h.merge( }
services: [*params[:services]].compact, add_location_params(mapped_parameters)
aspect_ids: normalize_aspect_ids, add_poll_params(mapped_parameters)
public: [*params[:aspect_ids]].first == "public", add_photo_ids(mapped_parameters)
photos: [*params[:photos]].compact mapped_parameters
)
end end
def normalize_aspect_ids private
aspect_ids = [*params[:aspect_ids]]
if aspect_ids.first == "all_aspects" def add_location_params(mapped_parameters)
current_user.aspect_ids return unless params.has_key?(:location)
else location = params.require(:location)
aspect_ids mapped_parameters[:location_address] = location[:address]
mapped_parameters[:location_coords] = "#{location[:lat]},#{location[:lng]}"
end
def add_photo_ids(mapped_parameters)
return unless params.has_key?(:photos)
photo_guids = params[:photos]
return if photo_guids.empty?
photo_ids = photo_guids.map {|guid| Photo.find_by!(guid: guid) }
raise InvalidArgument if photo_ids.length != photo_guids.length
mapped_parameters[:photos] = photo_ids
end
def add_poll_params(mapped_parameters)
return unless params.has_key?(:poll)
poll_data = params.require(:poll)
question = poll_data[:question]
answers = poll_data[:poll_answers]
raise InvalidArgument if question.blank?
raise InvalidArgument if answers.empty?
answers.each do |a|
raise InvalidArgument if a.blank?
end end
mapped_parameters[:poll_question] = question
mapped_parameters[:poll_answers] = answers
end
def normalize_aspect_ids(aspects)
aspects.empty? ? [] : aspects[:aspects]
end end
def post_service def post_service

View file

@ -2,16 +2,31 @@
class PhotoPresenter < BasePresenter class PhotoPresenter < BasePresenter
def base_hash def base_hash
{ id: id, {
guid: guid, id: id,
guid: guid,
dimensions: { dimensions: {
height: height, height: height,
width: width width: width
}, },
sizes: { sizes: {
small: url(:thumb_small), small: url(:thumb_small),
medium: url(:thumb_medium), medium: url(:thumb_medium),
large: url(:scaled_full) large: url(:scaled_full)
}
}
end
def as_api_json
{
dimensions: {
height: height,
width: width
},
sizes: {
small: url(:thumb_small),
medium: url(:thumb_medium),
large: url(:scaled_full)
} }
} }
end end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
class PollPresenter < BasePresenter
def initialize(poll, participant_user=nil)
@poll = poll
@user = participant_user
end
def as_api_json
{
guid: @poll.guid,
participation_count: @poll.participation_count,
question: @poll.question,
already_participated: @user && @poll.participation_answer(@user) ? true : false,
poll_answers: answers_collection_as_api_json(@poll.poll_answers)
}
end
private
def answers_collection_as_api_json(answers_collection)
answers_collection.map {|answer| answer_as_api_json(answer) }
end
def answer_as_api_json(answer)
{
id: answer.id,
answer: answer.answer,
vote_count: answer.vote_count
}
end
end

View file

@ -20,9 +20,9 @@ class PostInteractionPresenter
def as_counters def as_counters
{ {
comments_count: @post.comments_count, comments: @post.comments_count,
likes_count: @post.likes_count, likes: @post.likes_count,
reshares_count: @post.reshares_count reshares: @post.reshares_count
} }
end end

View file

@ -26,11 +26,11 @@ class PostPresenter < BasePresenter
public: @post.public, public: @post.public,
created_at: @post.created_at, created_at: @post.created_at,
nsfw: @post.nsfw, nsfw: @post.nsfw,
author: @post.author.as_api_response(:backbone), author: PersonPresenter.new(@post.author).as_api_json,
provider_display_name: @post.provider_display_name, provider_display_name: @post.provider_display_name,
interactions: interactions.as_counters, interaction_counters: interactions.as_counters,
location: @post.post_location, location: @post.post_location,
poll: @post.poll, poll: PollPresenter.new(@post.poll, current_user).as_api_json,
mentioned_people: build_mentioned_people_json, mentioned_people: build_mentioned_people_json,
photos: build_photos_json, photos: build_photos_json,
root: root_api_response root: root_api_response
@ -113,11 +113,11 @@ class PostPresenter < BasePresenter
end end
def build_mentioned_people_json def build_mentioned_people_json
@post.mentioned_people.as_api_response(:backbone) @post.mentioned_people.map {|m| PersonPresenter.new(m).as_api_json }
end end
def build_photos_json def build_photos_json
@post.photos.map {|p| p.as_api_response(:backbone) } @post.photos.map {|p| PhotoPresenter.new(p).as_api_json }
end end
def root def root

View file

@ -962,6 +962,8 @@ en:
no_like: "Like doesnt exist" no_like: "Like doesnt exist"
posts: posts:
post_not_found: "Post with provided guid could not be found" post_not_found: "Post with provided guid could not be found"
failed_create: "Failed to create the post"
failed_delete: "Not allowed to delete the post"
error: error:
not_found: "No record found for given id." not_found: "No record found for given id."

View file

@ -13,14 +13,32 @@ describe Api::V1::PostsController do
auth_with_read_and_write.create_access_token.to_s auth_with_read_and_write.create_access_token.to_s
} }
let(:alice_aspect) { alice.aspects.first }
let(:alice_photo1) {
alice.build_post(:photo, pending: true, user_file: File.open(photo_fixture_name), to: alice_aspect.id).tap(&:save!)
}
let(:alice_photo2) {
alice.build_post(:photo, pending: true, user_file: File.open(photo_fixture_name), to: alice_aspect.id).tap(&:save!)
}
let(:alice_photo_ids) { [alice_photo1.id.to_s, alice_photo2.id.to_s] }
let(:alice_photo_guids) { [alice_photo1.guid, alice_photo2.guid] }
describe "#show" do describe "#show" do
before do before do
bob.email = "bob@example.com" @status = alice.post(
bob.save :status_message,
text: "hello @{#{bob.diaspora_handle}} and @{#{eve.diaspora_handle}}from Alice!",
public: true,
to: "all"
)
end end
context "when mark notifications is omitted" do context "when mark notifications is omitted" do
it "shows attempts to show the info and mark the user notifications" do # TODO: Determine if this needs to be added back to the spec or if this is just in the notifications services
xit "shows attempts to show the info and mark the user notifications" do
@status = auth_with_read.user.post( @status = auth_with_read.user.post(
:status_message, :status_message,
text: "hello @{bob Testing ; bob@example.com}", text: "hello @{bob Testing ; bob@example.com}",
@ -54,7 +72,8 @@ describe Api::V1::PostsController do
end end
context "when mark notifications is false" do context "when mark notifications is false" do
it "shows attempts to show the info" do # TODO: Determine if this needs to be added back to the spec or if this is just in the notifications services
xit "shows attempts to show the info" do
@status = auth_with_read.user.post( @status = auth_with_read.user.post(
:status_message, :status_message,
text: "hello @{bob ; bob@example.com}", text: "hello @{bob ; bob@example.com}",
@ -91,54 +110,432 @@ describe Api::V1::PostsController do
# expect(notifications.length).to eq(1) # expect(notifications.length).to eq(1)
end end
end end
context "access simple by post ID" do
it "gets post" do
get(
api_v1_post_path(@status.id),
params: {
access_token: access_token_with_read
}
)
expect(response.status).to eq(200)
post = response_body(response)
confirm_post_format(post, alice, @status, [bob, eve])
end
end
context "access full post by post ID" do
it "gets post" do
base_params = {status_message: {text: "myText"}, public: true}
poll_params = {poll_question: "something?", poll_answers: %w[yes no maybe]}
location_params = {location_address: "somewhere", location_coords: "1,2"}
merged_params = base_params.merge(location_params)
merged_params = merged_params.merge(poll_params)
merged_params = merged_params.merge(photos: alice_photo_ids)
status_message = StatusMessageCreationService.new(alice).create(merged_params)
get(
api_v1_post_path(status_message.id),
params: {
access_token: access_token_with_read
}
)
expect(response.status).to eq(200)
post = response_body(response)
confirm_post_format(post, alice, status_message)
end
end
context "access reshare style post by post ID" do
it "gets post" do
reshare_post = FactoryGirl.create(:reshare, root: @status, author: bob.person)
get(
api_v1_post_path(reshare_post.id),
params: {
access_token: access_token_with_read
}
)
expect(response.status).to eq(200)
post = response_body(response)
confirm_reshare_format(post, @status, alice)
end
end
context "access private post not to reader" do
it "fails to get post" do
private_post = alice.post(:status_message, text: "to aspect only", public: false, to: alice.aspects.first.id)
get(
api_v1_post_path(private_post.id),
params: {
access_token: access_token_with_read
}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
end
context "access post with invalid id" do
it "fails to get post" do
get(
api_v1_post_path("999_999_999"),
params: {
access_token: access_token_with_read
}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
end
end end
describe "#create" do describe "#create" do
let(:user_photo1) {
auth_with_read_and_write.user.build_post(:photo, pending: true,
user_file: File.open(photo_fixture_name), to: "all").tap(&:save!)
}
let(:user_photo2) {
auth_with_read_and_write.user.build_post(:photo, pending: true,
user_file: File.open(photo_fixture_name), to: "all").tap(&:save!)
}
let(:user_photo_ids) { [user_photo1.id.to_s, user_photo2.id.to_s] }
let(:user_photo_guids) { [user_photo1.guid, user_photo2.guid] }
context "when given read-write access token" do context "when given read-write access token" do
it "creates a public post" do it "creates a public post" do
post_for_ref_only = auth_with_read_and_write.user.post(
:status_message,
text: "Hello this is a public post!",
public: true,
to: "all"
)
post( post(
api_v1_posts_path, api_v1_posts_path,
params: { params: {
access_token: access_token_with_read_and_write, access_token: access_token_with_read_and_write,
status_message: {text: "Hello this is a public post!"}, body: "Hello this is a public post!",
aspect_ids: "public" public: true
} }
) )
expect( expect(response.status).to eq(200)
Post.find_by(text: "Hello this is a public post!").public post = response_body(response)
).to eq(true) confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
end end
it "or creates a private post" do it "or creates a private post" do
aspect = Aspect.find_by(user_id: auth_with_read_and_write.user.id) aspect = Aspect.find_by(user_id: auth_with_read_and_write.user.id)
post_for_ref_only = auth_with_read_and_write.user.post(
:status_message,
text: "Hello this is a private post!",
aspect_ids: [aspect[:id]]
)
post( post(
api_v1_posts_path, api_v1_posts_path,
params: { params: {
access_token: access_token_with_read_and_write, access_token: access_token_with_read_and_write,
status_message: {text: "Hello this is a post!"}, body: "Hello this is a private post!",
aspect_ids: [aspect.id] public: false,
aspects: [aspect[:id]]
} }
) )
posted_post = Post.find_by(text: "Hello this is a post!") post = response_body(response)
expect(posted_post.public).to eq(false) expect(response.status).to eq(200)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
end
end
context "with fully populated post" do
it "creates with photos" do
message_text = "Post with photos"
base_params = {status_message: {text: message_text}, public: true}
merged_params = base_params.merge(photos: user_photo_ids)
post_for_ref_only = StatusMessageCreationService.new(auth_with_read_and_write.user).create(merged_params)
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: true,
photos: user_photo_guids
}
)
expect(response.status).to eq(200)
post = response_body(response)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
end
it "fails to add other's photos" do
message_text = "Post with photos"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: true,
photos: alice_photo_guids
}
)
expect(response.status).to eq(200)
post = JSON.parse(response.body)
expect(post["photos"].empty?).to be_truthy
end
it "fails to add bad guid" do
message_text = "Post with photos"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: true,
photos: ["999_999_999"]
}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_create"))
end
it "creates with poll" do
message_text = "status with a poll"
poll_params = {poll_question: "something?", poll_answers: %w[yes no maybe]}
base_params = {status_message: {text: message_text}, public: true}
merged_params = base_params.merge(poll_params)
post_for_ref_only = StatusMessageCreationService.new(auth_with_read_and_write.user).create(merged_params)
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: true,
poll: {
question: "something?",
poll_answers: %w[yes no maybe]
}
}
)
post = response_body(response)
expect(response.status).to eq(200)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
end
it "fails poll with no answers" do
message_text = "status with a poll"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: true,
poll: {
question: "something?",
poll_answers: []
}
}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_create"))
end
it "fails poll with blank answer" do
message_text = "status with a poll"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: true,
poll: {
question: "question",
poll_answers: ["yes", ""]
}
}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_create"))
end
it "fails poll with blank question and message text" do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: "",
public: true,
poll: {
question: "question",
poll_answers: %w[yes no]
}
}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_create"))
end
it "creates with location" do
message_text = "status with location"
base_params = {status_message: {text: message_text}, public: true}
location_params = {location_address: "somewhere", location_coords: "1,2"}
merged_params = base_params.merge(location_params)
post_for_ref_only = StatusMessageCreationService.new(auth_with_read_and_write.user).create(merged_params)
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: true,
location: {
address: "somewhere",
lat: 1,
lng: 2
}
}
)
post = response_body(response)
expect(response.status).to eq(200)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only)
end
it "creates with mentions" do
message_text = "hello @{#{alice.diaspora_handle}} from Bob!"
post_for_ref_only = auth_with_read_and_write.user.post(
:status_message,
text: message_text,
public: true
)
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: true
}
)
post = response_body(response)
expect(response.status).to eq(200)
confirm_post_format(post, auth_with_read_and_write.user, post_for_ref_only, [alice])
end
end
context "when given NSFW hashtag" do
it "creates NSFW post" do
message_text = "hello @{#{alice.diaspora_handle}} from Bob but this is #nsfw!"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: true
}
)
expect(response.status).to eq(200)
post = response_body(response)
expect(post["nsfw"]).to be_truthy
end
end
# TODO: Add when doing reshares endpoint
xcontext "when reshared" do
it "creates post with identical fields plus root" do
raise NotImplementedError
end
it "fails to reshare post user can't see" do
raise NotImplementedError
end
end
context "when given missing format" do
it "fails when no body" do
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
public: true
}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_create"))
end
it "fails when no public field and no aspects" do
message_text = "hello @{#{alice.diaspora_handle}} from Bob!"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text
}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_create"))
end
it "fails when private no aspects" do
message_text = "hello @{#{alice.diaspora_handle}} from Bob!"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: false
}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_create"))
end
it "fails when unknown aspect IDs" do
message_text = "hello @{#{alice.diaspora_handle}} from Bob!"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
public: false,
aspects: ["-1"]
}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_create"))
end
it "fails when no public field but aspects" do
aspect = Aspect.find_by(user_id: auth_with_read_and_write.user.id)
message_text = "hello @{#{alice.diaspora_handle}} from Bob!"
post(
api_v1_posts_path,
params: {
access_token: access_token_with_read_and_write,
body: message_text,
aspects: [aspect[:id]]
}
)
expect(response.status).to eq(422)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_create"))
end end
end end
context "when given read only access token" do context "when given read only access token" do
before do it "doesn't create the post" do
post( post(
api_v1_posts_path, api_v1_posts_path,
params: { params: {
access_token: access_token_with_read, access_token: access_token_with_read,
status_message: {text: "Hello this is a post!"}, status_message: {text: "Hello this is a post!"},
aspect_ids: "public" public: true
} }
) )
end expect(response.status).to eq(403)
it "doesn't create the post" do
json_body = JSON.parse(response.body)
expect(json_body["code"]).to eq(403)
end end
end end
end end
@ -161,28 +558,138 @@ describe Api::V1::PostsController do
end end
context "when given read only access token" do context "when given read only access token" do
before do it "doesn't delete the post" do
@status = auth_with_read.user.post( @status = auth_with_read.user.post(
:status_message, :status_message,
text: "hello", text: "hello",
public: true, public: true
to: "all"
) )
delete( delete(
api_v1_post_path(@status.id), api_v1_post_path(@status.id),
params: {access_token: access_token_with_read} params: {access_token: access_token_with_read}
) )
end
it "doesn't delete the post" do
json_body = JSON.parse(response.body)
expect(json_body["code"]).to eq(403)
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
end end
context "when given invalid Post ID" do
it "doesn't delete a post" do
delete(
api_v1_post_path("999_999_999"),
params: {access_token: access_token_with_read_and_write}
)
expect(response.status).to eq(404)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found"))
end
end
context "when PostID refers to another user's post" do
it "fails to delete post" do
status = alice.post(
:status_message,
text: "hello",
public: true,
to: "all"
)
delete(
api_v1_post_path(status.guid),
params: {access_token: access_token_with_read_and_write}
)
expect(response.status).to eq(403)
expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.failed_delete"))
end
end
end end
def response_body(response) def response_body(response)
JSON.parse(response.body) JSON.parse(response.body)
end end
private
# rubocop:disable Metrics/AbcSize
def confirm_post_format(post, user, reference_post, mentions=[])
confirm_post_top_level(post, reference_post)
confirm_person_format(post["author"], user)
confirm_interactions(post["interaction_counters"], reference_post)
mentions.each do |mention|
post_mentions = post["mentioned_people"]
post_mention = post_mentions.find {|m| m["guid"] == mention.guid }
confirm_person_format(post_mention, mention)
end
confirm_poll(post["poll"], reference_post.poll, false) if reference_post.poll
confirm_location(post["location"], reference_post.location) if reference_post.location
confirm_photos(post["photos"], reference_post.photos) if reference_post.photos
end
def confirm_post_top_level(post, reference_post)
expect(post.has_key?("guid")).to be_truthy
expect(post.has_key?("created_at")).to be_truthy
expect(post["created_at"]).not_to be_nil
expect(post["title"]).to eq(reference_post.message.title)
expect(post["body"]).to eq(reference_post.message.plain_text_for_json)
expect(post["post_type"]).to eq(reference_post.post_type)
expect(post["provider_display_name"]).to eq(reference_post.provider_display_name)
expect(post["public"]).to eq(reference_post.public)
expect(post["nsfw"]).to eq(reference_post.nsfw)
end
def confirm_interactions(interactions, reference_post)
expect(interactions["comments"]).to eq(reference_post.comments_count)
expect(interactions["likes"]).to eq(reference_post.likes_count)
expect(interactions["reshares"]).to eq(reference_post.reshares_count)
end
def confirm_person_format(post_person, user)
expect(post_person["guid"]).to eq(user.guid)
expect(post_person["diaspora_id"]).to eq(user.diaspora_handle)
expect(post_person["name"]).to eq(user.name)
expect(post_person["avatar"]).to eq(user.profile.image_url)
end
def confirm_poll(post_poll, ref_poll, expected_participation)
return unless ref_poll
expect(post_poll.has_key?("guid")).to be_truthy
expect(post_poll["participation_count"]).to eq(ref_poll.participation_count)
expect(post_poll["already_participated"]).to eq(expected_participation)
expect(post_poll["question"]).to eq(ref_poll.question)
answers = post_poll["poll_answers"]
answers.each do |answer|
actual_answer = ref_poll.poll_answers.find {|a| a[:answer] == answer["answer"] }
expect(answer["answer"]).to eq(actual_answer[:answer])
expect(answer["vote_count"]).to eq(actual_answer[:vote_count])
end
end
def confirm_location(location, ref_location)
expect(location["address"]).to eq(ref_location[:address])
expect(location["lat"]).to eq(ref_location[:lat])
expect(location["lng"]).to eq(ref_location[:lng])
end
def confirm_photos(photos, ref_photos)
expect(photos.size).to eq(ref_photos.size)
photos.each do |photo|
expect(photo["dimensions"].has_key?("height")).to be_truthy
expect(photo["dimensions"].has_key?("height")).to be_truthy
expect(photo["sizes"]["small"]).to be_truthy
expect(photo["sizes"]["medium"]).to be_truthy
expect(photo["sizes"]["large"]).to be_truthy
end
end
def confirm_reshare_format(post, root_post, root_poster)
root = post["root"]
expect(root.has_key?("guid")).to be_truthy
expect(root["guid"]).to eq(root_post[:guid])
expect(root.has_key?("created_at")).to be_truthy
confirm_person_format(root["author"], root_poster)
end
# rubocop:enable Metrics/AbcSize
end end

View file

@ -0,0 +1,40 @@
# frozen_string_literal: true
describe PollPresenter do
let(:poll) { FactoryGirl.create(:status_message_with_poll, public: true).poll }
let(:poll_answer) { poll.poll_answers.first }
describe "#poll" do
it "works without a user" do
presenter = PollPresenter.new(poll)
confirm_poll_api_json_format(presenter.as_api_json, 0, false)
end
it "works with user" do
presenter = PollPresenter.new(poll, alice)
confirm_poll_api_json_format(presenter.as_api_json, 0, false)
poll.poll_participations.create(poll_answer: poll_answer, author: alice.person)
confirm_poll_api_json_format(presenter.as_api_json, 1, true)
presenter = PollPresenter.new(poll, eve)
confirm_poll_api_json_format(presenter.as_api_json, 1, false)
presenter = PollPresenter.new(poll)
confirm_poll_api_json_format(presenter.as_api_json, 1, false)
end
end
private
# rubocop:disable Metrics/AbcSize
def confirm_poll_api_json_format(response, expected_count, expected_participation)
expect(response).to include(guid: poll.guid)
expect(response).to include(participation_count: expected_count)
expect(response).to include(already_participated: expected_participation)
expect(response).to include(question: poll.question)
expect(response.has_key?(:poll_answers)).to be_truthy
answer = response[:poll_answers].find {|a| a[:id] == poll_answer.id }
expect(answer[:answer]).to eq(poll_answer.answer)
expect(answer[:vote_count]).to eq(poll_answer.vote_count)
end
# rubocop:enable Metrics/AbcSize
end