Browse Source

Merge branch 'split-masto-api/polls' into 'develop'

Extract poll actions from `MastodonAPIController` to `PollController`

See merge request pleroma/pleroma!1755
object-id-column
kaniini 4 years ago
parent
commit
f0b4ba1bf9
11 changed files with 454 additions and 419 deletions
  1. +12
    -0
      lib/pleroma/web/controller_helper.ex
  2. +0
    -64
      lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex
  3. +53
    -0
      lib/pleroma/web/mastodon_api/controllers/poll_controller.ex
  4. +1
    -1
      lib/pleroma/web/mastodon_api/controllers/status_controller.ex
  5. +74
    -0
      lib/pleroma/web/mastodon_api/views/poll_view.ex
  6. +2
    -70
      lib/pleroma/web/mastodon_api/views/status_view.ex
  7. +2
    -2
      lib/pleroma/web/router.ex
  8. +184
    -0
      test/web/mastodon_api/controllers/poll_controller_test.exs
  9. +0
    -172
      test/web/mastodon_api/mastodon_api_controller_test.exs
  10. +126
    -0
      test/web/mastodon_api/views/poll_view_test.exs
  11. +0
    -110
      test/web/mastodon_api/views/status_view_test.exs

+ 12
- 0
lib/pleroma/web/controller_helper.ex View File

@@ -75,4 +75,16 @@ defmodule Pleroma.Web.ControllerHelper do
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
end
end

def try_render(conn, target, params)
when is_binary(target) do
case render(conn, target, params) do
nil -> render_error(conn, :not_implemented, "Can't display this activity")
res -> res
end
end

def try_render(conn, _, _) do
render_error(conn, :not_implemented, "Can't display this activity")
end
end

+ 0
- 64
lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex View File

@@ -7,7 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do

import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]

alias Pleroma.Activity
alias Pleroma.Bookmark
alias Pleroma.Config
alias Pleroma.HTTP
@@ -19,7 +18,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
alias Pleroma.User
alias Pleroma.Web
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.AppView
@@ -117,56 +115,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
json(conn, mastodon_emoji)
end

def get_poll(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
true <- Visibility.visible_for_user?(activity, user) do
conn
|> put_view(StatusView)
|> try_render("poll.json", %{object: object, for: user})
else
error when is_nil(error) or error == false ->
render_error(conn, :not_found, "Record not found")
end
end

defp get_cached_vote_or_vote(user, object, choices) do
idempotency_key = "polls:#{user.id}:#{object.data["id"]}"

{_, res} =
Cachex.fetch(:idempotency_cache, idempotency_key, fn _ ->
case CommonAPI.vote(user, object, choices) do
{:error, _message} = res -> {:ignore, res}
res -> {:commit, res}
end
end)

res
end

def poll_vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choices}) do
with %Object{} = object <- Object.get_by_id(id),
true <- object.data["type"] == "Question",
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
true <- Visibility.visible_for_user?(activity, user),
{:ok, _activities, object} <- get_cached_vote_or_vote(user, object, choices) do
conn
|> put_view(StatusView)
|> try_render("poll.json", %{object: object, for: user})
else
nil ->
render_error(conn, :not_found, "Record not found")

false ->
render_error(conn, :not_found, "Record not found")

{:error, message} ->
conn
|> put_status(:unprocessable_entity)
|> json(%{error: message})
end
end

def update_media(
%{assigns: %{user: user}} = conn,
%{"id" => id, "description" => description} = _
@@ -511,18 +459,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end

def try_render(conn, target, params)
when is_binary(target) do
case render(conn, target, params) do
nil -> render_error(conn, :not_implemented, "Can't display this activity")
res -> res
end
end

def try_render(conn, _, _) do
render_error(conn, :not_implemented, "Can't display this activity")
end

defp present?(nil), do: false
defp present?(false), do: false
defp present?(_), do: true


+ 53
- 0
lib/pleroma/web/mastodon_api/controllers/poll_controller.ex View File

@@ -0,0 +1,53 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.PollController do
use Pleroma.Web, :controller

import Pleroma.Web.ControllerHelper, only: [try_render: 3, json_response: 3]

alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.CommonAPI

action_fallback(Pleroma.Web.MastodonAPI.FallbackController)

@doc "GET /api/v1/polls/:id"
def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
true <- Visibility.visible_for_user?(activity, user) do
try_render(conn, "show.json", %{object: object, for: user})
else
error when is_nil(error) or error == false ->
render_error(conn, :not_found, "Record not found")
end
end

@doc "POST /api/v1/polls/:id/votes"
def vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choices}) do
with %Object{data: %{"type" => "Question"}} = object <- Object.get_by_id(id),
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
true <- Visibility.visible_for_user?(activity, user),
{:ok, _activities, object} <- get_cached_vote_or_vote(user, object, choices) do
try_render(conn, "show.json", %{object: object, for: user})
else
nil -> render_error(conn, :not_found, "Record not found")
false -> render_error(conn, :not_found, "Record not found")
{:error, message} -> json_response(conn, :unprocessable_entity, %{error: message})
end
end

defp get_cached_vote_or_vote(user, object, choices) do
idempotency_key = "polls:#{user.id}:#{object.data["id"]}"

Cachex.fetch!(:idempotency_cache, idempotency_key, fn ->
case CommonAPI.vote(user, object, choices) do
{:error, _message} = res -> {:ignore, res}
res -> {:commit, res}
end
end)
end
end

+ 1
- 1
lib/pleroma/web/mastodon_api/controllers/status_controller.ex View File

@@ -5,7 +5,7 @@
defmodule Pleroma.Web.MastodonAPI.StatusController do
use Pleroma.Web, :controller

import Pleroma.Web.MastodonAPI.MastodonAPIController, only: [try_render: 3]
import Pleroma.Web.ControllerHelper, only: [try_render: 3]

require Ecto.Query



+ 74
- 0
lib/pleroma/web/mastodon_api/views/poll_view.ex View File

@@ -0,0 +1,74 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.PollView do
use Pleroma.Web, :view

alias Pleroma.HTML
alias Pleroma.Web.CommonAPI.Utils

def render("show.json", %{object: object, multiple: multiple, options: options} = params) do
{end_time, expired} = end_time_and_expired(object)
{options, votes_count} = options_and_votes_count(options)

%{
# Mastodon uses separate ids for polls, but an object can't have
# more than one poll embedded so object id is fine
id: to_string(object.id),
expires_at: end_time,
expired: expired,
multiple: multiple,
votes_count: votes_count,
options: options,
voted: voted?(params),
emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"])
}
end

def render("show.json", %{object: object} = params) do
case object.data do
%{"anyOf" => options} when is_list(options) ->
render(__MODULE__, "show.json", Map.merge(params, %{multiple: true, options: options}))

%{"oneOf" => options} when is_list(options) ->
render(__MODULE__, "show.json", Map.merge(params, %{multiple: false, options: options}))

_ ->
nil
end
end

defp end_time_and_expired(object) do
case object.data["closed"] || object.data["endTime"] do
end_time when is_binary(end_time) ->
end_time = NaiveDateTime.from_iso8601!(end_time)
expired = NaiveDateTime.compare(end_time, NaiveDateTime.utc_now()) == :lt

{Utils.to_masto_date(end_time), expired}

_ ->
{nil, false}
end
end

defp options_and_votes_count(options) do
Enum.map_reduce(options, 0, fn %{"name" => name} = option, count ->
current_count = option["replies"]["totalItems"] || 0

{%{
title: HTML.strip_tags(name),
votes_count: current_count
}, current_count + count}
end)
end

defp voted?(%{object: object} = opts) do
if opts[:for] do
existing_votes = Pleroma.Web.ActivityPub.Utils.get_existing_votes(opts[:for].ap_id, object)
existing_votes != [] or opts[:for].ap_id == object.data["actor"]
else
false
end
end
end

+ 2
- 70
lib/pleroma/web/mastodon_api/views/status_view.ex View File

@@ -18,6 +18,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.PollView
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.MediaProxy

@@ -277,7 +278,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
spoiler_text: summary_html,
visibility: get_visibility(object),
media_attachments: attachments,
poll: render("poll.json", %{object: object, for: opts[:for]}),
poll: render(PollView, "show.json", object: object, for: opts[:for]),
mentions: mentions,
tags: build_tags(tags),
application: %{
@@ -389,75 +390,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
safe_render_many(opts.activities, StatusView, "listen.json", opts)
end

def render("poll.json", %{object: object} = opts) do
{multiple, options} =
case object.data do
%{"anyOf" => options} when is_list(options) -> {true, options}
%{"oneOf" => options} when is_list(options) -> {false, options}
_ -> {nil, nil}
end

if options do
{end_time, expired} =
case object.data["closed"] || object.data["endTime"] do
end_time when is_binary(end_time) ->
end_time =
(object.data["closed"] || object.data["endTime"])
|> NaiveDateTime.from_iso8601!()

expired =
end_time
|> NaiveDateTime.compare(NaiveDateTime.utc_now())
|> case do
:lt -> true
_ -> false
end

end_time = Utils.to_masto_date(end_time)

{end_time, expired}

_ ->
{nil, false}
end

voted =
if opts[:for] do
existing_votes =
Pleroma.Web.ActivityPub.Utils.get_existing_votes(opts[:for].ap_id, object)

existing_votes != [] or opts[:for].ap_id == object.data["actor"]
else
false
end

{options, votes_count} =
Enum.map_reduce(options, 0, fn %{"name" => name} = option, count ->
current_count = option["replies"]["totalItems"] || 0

{%{
title: HTML.strip_tags(name),
votes_count: current_count
}, current_count + count}
end)

%{
# Mastodon uses separate ids for polls, but an object can't have
# more than one poll embedded so object id is fine
id: to_string(object.id),
expires_at: end_time,
expired: expired,
multiple: multiple,
votes_count: votes_count,
options: options,
voted: voted,
emojis: build_emojis(object.data["emoji"])
}
else
nil
end
end

def render("context.json", %{activity: activity, activities: activities, user: user}) do
%{ancestors: ancestors, descendants: descendants} =
activities


+ 2
- 2
lib/pleroma/web/router.ex View File

@@ -403,7 +403,7 @@ defmodule Pleroma.Web.Router do
put("/scheduled_statuses/:id", ScheduledActivityController, :update)
delete("/scheduled_statuses/:id", ScheduledActivityController, :delete)

post("/polls/:id/votes", MastodonAPIController, :poll_vote)
post("/polls/:id/votes", PollController, :vote)

post("/media", MastodonAPIController, :upload)
put("/media/:id", MastodonAPIController, :update_media)
@@ -488,7 +488,7 @@ defmodule Pleroma.Web.Router do
get("/statuses/:id", StatusController, :show)
get("/statuses/:id/context", StatusController, :context)

get("/polls/:id", MastodonAPIController, :get_poll)
get("/polls/:id", PollController, :show)

get("/accounts/:id/statuses", AccountController, :statuses)
get("/accounts/:id/followers", AccountController, :followers)


+ 184
- 0
test/web/mastodon_api/controllers/poll_controller_test.exs View File

@@ -0,0 +1,184 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
use Pleroma.Web.ConnCase

alias Pleroma.Object
alias Pleroma.Web.CommonAPI

import Pleroma.Factory

describe "GET /api/v1/polls/:id" do
test "returns poll entity for object id", %{conn: conn} do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Pleroma does",
"poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, user)
|> get("/api/v1/polls/#{object.id}")

response = json_response(conn, 200)
id = to_string(object.id)
assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
end

test "does not expose polls for private statuses", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Pleroma does",
"poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
"visibility" => "private"
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, other_user)
|> get("/api/v1/polls/#{object.id}")

assert json_response(conn, 404)
end
end

describe "POST /api/v1/polls/:id/votes" do
test "votes are added to the poll", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "A very delicious sandwich",
"poll" => %{
"options" => ["Lettuce", "Grilled Bacon", "Tomato"],
"expires_in" => 20,
"multiple" => true
}
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, other_user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})

assert json_response(conn, 200)
object = Object.get_by_id(object.id)

assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
total_items == 1
end)
end

test "author can't vote", %{conn: conn} do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Am I cute?",
"poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
})

object = Object.normalize(activity)

assert conn
|> assign(:user, user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
|> json_response(422) == %{"error" => "Poll's author can't vote"}

object = Object.get_by_id(object.id)

refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
end

test "does not allow multiple choices on a single-choice question", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "The glass is",
"poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
})

object = Object.normalize(activity)

assert conn
|> assign(:user, other_user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
|> json_response(422) == %{"error" => "Too many choices"}

object = Object.get_by_id(object.id)

refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
total_items == 1
end)
end

test "does not allow choice index to be greater than options count", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Am I cute?",
"poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, other_user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})

assert json_response(conn, 422) == %{"error" => "Invalid indices"}
end

test "returns 404 error when object is not exist", %{conn: conn} do
user = insert(:user)

conn =
conn
|> assign(:user, user)
|> post("/api/v1/polls/1/votes", %{"choices" => [0]})

assert json_response(conn, 404) == %{"error" => "Record not found"}
end

test "returns 404 when poll is private and not available for user", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Am I cute?",
"poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
"visibility" => "private"
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, other_user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})

assert json_response(conn, 404) == %{"error" => "Record not found"}
end
end
end

+ 0
- 172
test/web/mastodon_api/mastodon_api_controller_test.exs View File

@@ -417,178 +417,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
end
end

describe "GET /api/v1/polls/:id" do
test "returns poll entity for object id", %{conn: conn} do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Pleroma does",
"poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, user)
|> get("/api/v1/polls/#{object.id}")

response = json_response(conn, 200)
id = to_string(object.id)
assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
end

test "does not expose polls for private statuses", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Pleroma does",
"poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
"visibility" => "private"
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, other_user)
|> get("/api/v1/polls/#{object.id}")

assert json_response(conn, 404)
end
end

describe "POST /api/v1/polls/:id/votes" do
test "votes are added to the poll", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "A very delicious sandwich",
"poll" => %{
"options" => ["Lettuce", "Grilled Bacon", "Tomato"],
"expires_in" => 20,
"multiple" => true
}
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, other_user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})

assert json_response(conn, 200)
object = Object.get_by_id(object.id)

assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
total_items == 1
end)
end

test "author can't vote", %{conn: conn} do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Am I cute?",
"poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
})

object = Object.normalize(activity)

assert conn
|> assign(:user, user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
|> json_response(422) == %{"error" => "Poll's author can't vote"}

object = Object.get_by_id(object.id)

refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
end

test "does not allow multiple choices on a single-choice question", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "The glass is",
"poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
})

object = Object.normalize(activity)

assert conn
|> assign(:user, other_user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
|> json_response(422) == %{"error" => "Too many choices"}

object = Object.get_by_id(object.id)

refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
total_items == 1
end)
end

test "does not allow choice index to be greater than options count", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Am I cute?",
"poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, other_user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})

assert json_response(conn, 422) == %{"error" => "Invalid indices"}
end

test "returns 404 error when object is not exist", %{conn: conn} do
user = insert(:user)

conn =
conn
|> assign(:user, user)
|> post("/api/v1/polls/1/votes", %{"choices" => [0]})

assert json_response(conn, 404) == %{"error" => "Record not found"}
end

test "returns 404 when poll is private and not available for user", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Am I cute?",
"poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
"visibility" => "private"
})

object = Object.normalize(activity)

conn =
conn
|> assign(:user, other_user)
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})

assert json_response(conn, 404) == %{"error" => "Record not found"}
end
end

describe "POST /auth/password, with valid parameters" do
setup %{conn: conn} do
user = insert(:user)


+ 126
- 0
test/web/mastodon_api/views/poll_view_test.exs View File

@@ -0,0 +1,126 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.MastodonAPI.PollViewTest do
use Pleroma.DataCase

alias Pleroma.Object
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.PollView

import Pleroma.Factory
import Tesla.Mock

setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end

test "renders a poll" do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Is Tenshi eating a corndog cute?",
"poll" => %{
"options" => ["absolutely!", "sure", "yes", "why are you even asking?"],
"expires_in" => 20
}
})

object = Object.normalize(activity)

expected = %{
emojis: [],
expired: false,
id: to_string(object.id),
multiple: false,
options: [
%{title: "absolutely!", votes_count: 0},
%{title: "sure", votes_count: 0},
%{title: "yes", votes_count: 0},
%{title: "why are you even asking?", votes_count: 0}
],
voted: false,
votes_count: 0
}

result = PollView.render("show.json", %{object: object})
expires_at = result.expires_at
result = Map.delete(result, :expires_at)

assert result == expected

expires_at = NaiveDateTime.from_iso8601!(expires_at)
assert NaiveDateTime.diff(expires_at, NaiveDateTime.utc_now()) in 15..20
end

test "detects if it is multiple choice" do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Which Mastodon developer is your favourite?",
"poll" => %{
"options" => ["Gargron", "Eugen"],
"expires_in" => 20,
"multiple" => true
}
})

object = Object.normalize(activity)

assert %{multiple: true} = PollView.render("show.json", %{object: object})
end

test "detects emoji" do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "What's with the smug face?",
"poll" => %{
"options" => [":blank: sip", ":blank::blank: sip", ":blank::blank::blank: sip"],
"expires_in" => 20
}
})

object = Object.normalize(activity)

assert %{emojis: [%{shortcode: "blank"}]} = PollView.render("show.json", %{object: object})
end

test "detects vote status" do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Which input devices do you use?",
"poll" => %{
"options" => ["mouse", "trackball", "trackpoint"],
"multiple" => true,
"expires_in" => 20
}
})

object = Object.normalize(activity)

{:ok, _, object} = CommonAPI.vote(other_user, object, [1, 2])

result = PollView.render("show.json", %{object: object, for: other_user})

assert result[:voted] == true
assert Enum.at(result[:options], 1)[:votes_count] == 1
assert Enum.at(result[:options], 2)[:votes_count] == 1
end

test "does not crash on polls with no end date" do
object = Object.normalize("https://skippers-bin.com/notes/7x9tmrp97i")
result = PollView.render("show.json", %{object: object})

assert result[:expires_at] == nil
assert result[:expired] == false
end
end

+ 0
- 110
test/web/mastodon_api/views/status_view_test.exs View File

@@ -451,116 +451,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
end
end

describe "poll view" do
test "renders a poll" do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Is Tenshi eating a corndog cute?",
"poll" => %{
"options" => ["absolutely!", "sure", "yes", "why are you even asking?"],
"expires_in" => 20
}
})

object = Object.normalize(activity)

expected = %{
emojis: [],
expired: false,
id: to_string(object.id),
multiple: false,
options: [
%{title: "absolutely!", votes_count: 0},
%{title: "sure", votes_count: 0},
%{title: "yes", votes_count: 0},
%{title: "why are you even asking?", votes_count: 0}
],
voted: false,
votes_count: 0
}

result = StatusView.render("poll.json", %{object: object})
expires_at = result.expires_at
result = Map.delete(result, :expires_at)

assert result == expected

expires_at = NaiveDateTime.from_iso8601!(expires_at)
assert NaiveDateTime.diff(expires_at, NaiveDateTime.utc_now()) in 15..20
end

test "detects if it is multiple choice" do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Which Mastodon developer is your favourite?",
"poll" => %{
"options" => ["Gargron", "Eugen"],
"expires_in" => 20,
"multiple" => true
}
})

object = Object.normalize(activity)

assert %{multiple: true} = StatusView.render("poll.json", %{object: object})
end

test "detects emoji" do
user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "What's with the smug face?",
"poll" => %{
"options" => [":blank: sip", ":blank::blank: sip", ":blank::blank::blank: sip"],
"expires_in" => 20
}
})

object = Object.normalize(activity)

assert %{emojis: [%{shortcode: "blank"}]} =
StatusView.render("poll.json", %{object: object})
end

test "detects vote status" do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} =
CommonAPI.post(user, %{
"status" => "Which input devices do you use?",
"poll" => %{
"options" => ["mouse", "trackball", "trackpoint"],
"multiple" => true,
"expires_in" => 20
}
})

object = Object.normalize(activity)

{:ok, _, object} = CommonAPI.vote(other_user, object, [1, 2])

result = StatusView.render("poll.json", %{object: object, for: other_user})

assert result[:voted] == true
assert Enum.at(result[:options], 1)[:votes_count] == 1
assert Enum.at(result[:options], 2)[:votes_count] == 1
end

test "does not crash on polls with no end date" do
object = Object.normalize("https://skippers-bin.com/notes/7x9tmrp97i")
result = StatusView.render("poll.json", %{object: object})

assert result[:expires_at] == nil
assert result[:expired] == false
end
end

test "embeds a relationship in the account" do
user = insert(:user)
other_user = insert(:user)


Loading…
Cancel
Save