1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.Notification
13 alias Pleroma.ScheduledActivity
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.MastodonAPI.FilterView
18 alias Pleroma.Web.OAuth.App
19 alias Pleroma.Web.OAuth.Token
20 alias Pleroma.Web.OStatus
21 alias Pleroma.Web.Push
22 alias Pleroma.Web.TwitterAPI.TwitterAPI
23 import Pleroma.Factory
24 import ExUnit.CaptureLog
28 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
32 test "the home timeline", %{conn: conn} do
34 following = insert(:user)
36 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
40 |> assign(:user, user)
41 |> get("/api/v1/timelines/home")
43 assert Enum.empty?(json_response(conn, 200))
45 {:ok, user} = User.follow(user, following)
49 |> assign(:user, user)
50 |> get("/api/v1/timelines/home")
52 assert [%{"content" => "test"}] = json_response(conn, 200)
55 test "the public timeline", %{conn: conn} do
56 following = insert(:user)
59 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
62 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
66 |> get("/api/v1/timelines/public", %{"local" => "False"})
68 assert length(json_response(conn, 200)) == 2
72 |> get("/api/v1/timelines/public", %{"local" => "True"})
74 assert [%{"content" => "test"}] = json_response(conn, 200)
78 |> get("/api/v1/timelines/public", %{"local" => "1"})
80 assert [%{"content" => "test"}] = json_response(conn, 200)
84 test "the public timeline when public is set to false", %{conn: conn} do
85 public = Pleroma.Config.get([:instance, :public])
86 Pleroma.Config.put([:instance, :public], false)
89 Pleroma.Config.put([:instance, :public], public)
93 |> get("/api/v1/timelines/public", %{"local" => "False"})
94 |> json_response(403) == %{"error" => "This resource requires authentication."}
97 test "posting a status", %{conn: conn} do
100 idempotency_key = "Pikachu rocks!"
104 |> assign(:user, user)
105 |> put_req_header("idempotency-key", idempotency_key)
106 |> post("/api/v1/statuses", %{
108 "spoiler_text" => "2hu",
109 "sensitive" => "false"
112 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
114 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
116 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
117 json_response(conn_one, 200)
119 assert Activity.get_by_id(id)
123 |> assign(:user, user)
124 |> put_req_header("idempotency-key", idempotency_key)
125 |> post("/api/v1/statuses", %{
127 "spoiler_text" => "2hu",
128 "sensitive" => "false"
131 assert %{"id" => second_id} = json_response(conn_two, 200)
133 assert id == second_id
137 |> assign(:user, user)
138 |> post("/api/v1/statuses", %{
140 "spoiler_text" => "2hu",
141 "sensitive" => "false"
144 assert %{"id" => third_id} = json_response(conn_three, 200)
146 refute id == third_id
149 describe "posting polls" do
150 test "posting a poll", %{conn: conn} do
152 time = NaiveDateTime.utc_now()
156 |> assign(:user, user)
157 |> post("/api/v1/statuses", %{
158 "status" => "Who is the #bestgrill?",
159 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
162 response = json_response(conn, 200)
164 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
165 title in ["Rei", "Asuka", "Misato"]
168 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
169 refute response["poll"]["expred"]
172 test "option limit is enforced", %{conn: conn} do
174 limit = Pleroma.Config.get([:instance, :poll_limits, :max_options])
178 |> assign(:user, user)
179 |> post("/api/v1/statuses", %{
181 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
184 %{"error" => error} = json_response(conn, 422)
185 assert error == "Poll can't contain more than #{limit} options"
188 test "option character limit is enforced", %{conn: conn} do
190 limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars])
194 |> assign(:user, user)
195 |> post("/api/v1/statuses", %{
198 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
203 %{"error" => error} = json_response(conn, 422)
204 assert error == "Poll options cannot be longer than #{limit} characters each"
207 test "minimal date limit is enforced", %{conn: conn} do
209 limit = Pleroma.Config.get([:instance, :poll_limits, :min_expiration])
213 |> assign(:user, user)
214 |> post("/api/v1/statuses", %{
215 "status" => "imagine arbitrary limits",
217 "options" => ["this post was made by pleroma gang"],
218 "expires_in" => limit - 1
222 %{"error" => error} = json_response(conn, 422)
223 assert error == "Expiration date is too soon"
226 test "maximum date limit is enforced", %{conn: conn} do
228 limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration])
232 |> assign(:user, user)
233 |> post("/api/v1/statuses", %{
234 "status" => "imagine arbitrary limits",
236 "options" => ["this post was made by pleroma gang"],
237 "expires_in" => limit + 1
241 %{"error" => error} = json_response(conn, 422)
242 assert error == "Expiration date is too far in the future"
246 test "posting a sensitive status", %{conn: conn} do
251 |> assign(:user, user)
252 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
254 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
255 assert Activity.get_by_id(id)
258 test "posting a fake status", %{conn: conn} do
263 |> assign(:user, user)
264 |> post("/api/v1/statuses", %{
266 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
269 real_status = json_response(real_conn, 200)
272 assert Object.get_by_ap_id(real_status["uri"])
276 |> Map.put("id", nil)
277 |> Map.put("url", nil)
278 |> Map.put("uri", nil)
279 |> Map.put("created_at", nil)
280 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
284 |> assign(:user, user)
285 |> post("/api/v1/statuses", %{
287 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
291 fake_status = json_response(fake_conn, 200)
294 refute Object.get_by_ap_id(fake_status["uri"])
298 |> Map.put("id", nil)
299 |> Map.put("url", nil)
300 |> Map.put("uri", nil)
301 |> Map.put("created_at", nil)
302 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
304 assert real_status == fake_status
307 test "posting a status with OGP link preview", %{conn: conn} do
308 Pleroma.Config.put([:rich_media, :enabled], true)
313 |> assign(:user, user)
314 |> post("/api/v1/statuses", %{
315 "status" => "http://example.com/ogp"
318 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
319 assert Activity.get_by_id(id)
320 Pleroma.Config.put([:rich_media, :enabled], false)
323 test "posting a direct status", %{conn: conn} do
324 user1 = insert(:user)
325 user2 = insert(:user)
326 content = "direct cofe @#{user2.nickname}"
330 |> assign(:user, user1)
331 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
333 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
334 assert activity = Activity.get_by_id(id)
335 assert activity.recipients == [user2.ap_id, user1.ap_id]
336 assert activity.data["to"] == [user2.ap_id]
337 assert activity.data["cc"] == []
340 test "direct timeline", %{conn: conn} do
341 user_one = insert(:user)
342 user_two = insert(:user)
344 {:ok, user_two} = User.follow(user_two, user_one)
347 CommonAPI.post(user_one, %{
348 "status" => "Hi @#{user_two.nickname}!",
349 "visibility" => "direct"
352 {:ok, _follower_only} =
353 CommonAPI.post(user_one, %{
354 "status" => "Hi @#{user_two.nickname}!",
355 "visibility" => "private"
358 # Only direct should be visible here
361 |> assign(:user, user_two)
362 |> get("api/v1/timelines/direct")
364 [status] = json_response(res_conn, 200)
366 assert %{"visibility" => "direct"} = status
367 assert status["url"] != direct.data["id"]
369 # User should be able to see his own direct message
372 |> assign(:user, user_one)
373 |> get("api/v1/timelines/direct")
375 [status] = json_response(res_conn, 200)
377 assert %{"visibility" => "direct"} = status
379 # Both should be visible here
382 |> assign(:user, user_two)
383 |> get("api/v1/timelines/home")
385 [_s1, _s2] = json_response(res_conn, 200)
388 Enum.each(1..20, fn _ ->
390 CommonAPI.post(user_one, %{
391 "status" => "Hi @#{user_two.nickname}!",
392 "visibility" => "direct"
398 |> assign(:user, user_two)
399 |> get("api/v1/timelines/direct")
401 statuses = json_response(res_conn, 200)
402 assert length(statuses) == 20
406 |> assign(:user, user_two)
407 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
409 [status] = json_response(res_conn, 200)
411 assert status["url"] != direct.data["id"]
414 test "Conversations", %{conn: conn} do
415 user_one = insert(:user)
416 user_two = insert(:user)
418 {:ok, user_two} = User.follow(user_two, user_one)
421 CommonAPI.post(user_one, %{
422 "status" => "Hi @#{user_two.nickname}!",
423 "visibility" => "direct"
426 {:ok, _follower_only} =
427 CommonAPI.post(user_one, %{
428 "status" => "Hi @#{user_two.nickname}!",
429 "visibility" => "private"
434 |> assign(:user, user_one)
435 |> get("/api/v1/conversations")
437 assert response = json_response(res_conn, 200)
442 "accounts" => res_accounts,
443 "last_status" => res_last_status,
448 assert length(res_accounts) == 2
449 assert is_binary(res_id)
450 assert unread == true
451 assert res_last_status["id"] == direct.id
453 # Apparently undocumented API endpoint
456 |> assign(:user, user_one)
457 |> post("/api/v1/conversations/#{res_id}/read")
459 assert response = json_response(res_conn, 200)
460 assert length(response["accounts"]) == 2
461 assert response["last_status"]["id"] == direct.id
462 assert response["unread"] == false
464 # (vanilla) Mastodon frontend behaviour
467 |> assign(:user, user_one)
468 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
470 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
473 test "doesn't include DMs from blocked users", %{conn: conn} do
474 blocker = insert(:user)
475 blocked = insert(:user)
477 {:ok, blocker} = User.block(blocker, blocked)
479 {:ok, _blocked_direct} =
480 CommonAPI.post(blocked, %{
481 "status" => "Hi @#{blocker.nickname}!",
482 "visibility" => "direct"
486 CommonAPI.post(user, %{
487 "status" => "Hi @#{blocker.nickname}!",
488 "visibility" => "direct"
493 |> assign(:user, user)
494 |> get("api/v1/timelines/direct")
496 [status] = json_response(res_conn, 200)
497 assert status["id"] == direct.id
500 test "replying to a status", %{conn: conn} do
503 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
507 |> assign(:user, user)
508 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
510 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
512 activity = Activity.get_by_id(id)
514 assert activity.data["context"] == replied_to.data["context"]
515 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
518 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
523 |> assign(:user, user)
524 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
526 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
528 activity = Activity.get_by_id(id)
533 test "verify_credentials", %{conn: conn} do
538 |> assign(:user, user)
539 |> get("/api/v1/accounts/verify_credentials")
541 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
542 assert id == to_string(user.id)
545 test "verify_credentials default scope unlisted", %{conn: conn} do
546 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
550 |> assign(:user, user)
551 |> get("/api/v1/accounts/verify_credentials")
553 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
554 assert id == to_string(user.id)
557 test "apps/verify_credentials", %{conn: conn} do
558 token = insert(:oauth_token)
562 |> assign(:user, token.user)
563 |> assign(:token, token)
564 |> get("/api/v1/apps/verify_credentials")
566 app = Repo.preload(token, :app).app
569 "name" => app.client_name,
570 "website" => app.website,
571 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
574 assert expected == json_response(conn, 200)
577 test "creates an oauth app", %{conn: conn} do
579 app_attrs = build(:oauth_app)
583 |> assign(:user, user)
584 |> post("/api/v1/apps", %{
585 client_name: app_attrs.client_name,
586 redirect_uris: app_attrs.redirect_uris
589 [app] = Repo.all(App)
592 "name" => app.client_name,
593 "website" => app.website,
594 "client_id" => app.client_id,
595 "client_secret" => app.client_secret,
596 "id" => app.id |> to_string(),
597 "redirect_uri" => app.redirect_uris,
598 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
601 assert expected == json_response(conn, 200)
604 test "get a status", %{conn: conn} do
605 activity = insert(:note_activity)
609 |> get("/api/v1/statuses/#{activity.id}")
611 assert %{"id" => id} = json_response(conn, 200)
612 assert id == to_string(activity.id)
615 describe "deleting a status" do
616 test "when you created it", %{conn: conn} do
617 activity = insert(:note_activity)
618 author = User.get_cached_by_ap_id(activity.data["actor"])
622 |> assign(:user, author)
623 |> delete("/api/v1/statuses/#{activity.id}")
625 assert %{} = json_response(conn, 200)
627 refute Activity.get_by_id(activity.id)
630 test "when you didn't create it", %{conn: conn} do
631 activity = insert(:note_activity)
636 |> assign(:user, user)
637 |> delete("/api/v1/statuses/#{activity.id}")
639 assert %{"error" => _} = json_response(conn, 403)
641 assert Activity.get_by_id(activity.id) == activity
644 test "when you're an admin or moderator", %{conn: conn} do
645 activity1 = insert(:note_activity)
646 activity2 = insert(:note_activity)
647 admin = insert(:user, info: %{is_admin: true})
648 moderator = insert(:user, info: %{is_moderator: true})
652 |> assign(:user, admin)
653 |> delete("/api/v1/statuses/#{activity1.id}")
655 assert %{} = json_response(res_conn, 200)
659 |> assign(:user, moderator)
660 |> delete("/api/v1/statuses/#{activity2.id}")
662 assert %{} = json_response(res_conn, 200)
664 refute Activity.get_by_id(activity1.id)
665 refute Activity.get_by_id(activity2.id)
669 describe "filters" do
670 test "creating a filter", %{conn: conn} do
673 filter = %Pleroma.Filter{
680 |> assign(:user, user)
681 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
683 assert response = json_response(conn, 200)
684 assert response["phrase"] == filter.phrase
685 assert response["context"] == filter.context
686 assert response["irreversible"] == false
687 assert response["id"] != nil
688 assert response["id"] != ""
691 test "fetching a list of filters", %{conn: conn} do
694 query_one = %Pleroma.Filter{
701 query_two = %Pleroma.Filter{
708 {:ok, filter_one} = Pleroma.Filter.create(query_one)
709 {:ok, filter_two} = Pleroma.Filter.create(query_two)
713 |> assign(:user, user)
714 |> get("/api/v1/filters")
715 |> json_response(200)
721 filters: [filter_two, filter_one]
725 test "get a filter", %{conn: conn} do
728 query = %Pleroma.Filter{
735 {:ok, filter} = Pleroma.Filter.create(query)
739 |> assign(:user, user)
740 |> get("/api/v1/filters/#{filter.filter_id}")
742 assert _response = json_response(conn, 200)
745 test "update a filter", %{conn: conn} do
748 query = %Pleroma.Filter{
755 {:ok, _filter} = Pleroma.Filter.create(query)
757 new = %Pleroma.Filter{
764 |> assign(:user, user)
765 |> put("/api/v1/filters/#{query.filter_id}", %{
770 assert response = json_response(conn, 200)
771 assert response["phrase"] == new.phrase
772 assert response["context"] == new.context
775 test "delete a filter", %{conn: conn} do
778 query = %Pleroma.Filter{
785 {:ok, filter} = Pleroma.Filter.create(query)
789 |> assign(:user, user)
790 |> delete("/api/v1/filters/#{filter.filter_id}")
792 assert response = json_response(conn, 200)
793 assert response == %{}
798 test "creating a list", %{conn: conn} do
803 |> assign(:user, user)
804 |> post("/api/v1/lists", %{"title" => "cuties"})
806 assert %{"title" => title} = json_response(conn, 200)
807 assert title == "cuties"
810 test "adding users to a list", %{conn: conn} do
812 other_user = insert(:user)
813 {:ok, list} = Pleroma.List.create("name", user)
817 |> assign(:user, user)
818 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
820 assert %{} == json_response(conn, 200)
821 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
822 assert following == [other_user.follower_address]
825 test "removing users from a list", %{conn: conn} do
827 other_user = insert(:user)
828 third_user = insert(:user)
829 {:ok, list} = Pleroma.List.create("name", user)
830 {:ok, list} = Pleroma.List.follow(list, other_user)
831 {:ok, list} = Pleroma.List.follow(list, third_user)
835 |> assign(:user, user)
836 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
838 assert %{} == json_response(conn, 200)
839 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
840 assert following == [third_user.follower_address]
843 test "listing users in a list", %{conn: conn} do
845 other_user = insert(:user)
846 {:ok, list} = Pleroma.List.create("name", user)
847 {:ok, list} = Pleroma.List.follow(list, other_user)
851 |> assign(:user, user)
852 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
854 assert [%{"id" => id}] = json_response(conn, 200)
855 assert id == to_string(other_user.id)
858 test "retrieving a list", %{conn: conn} do
860 {:ok, list} = Pleroma.List.create("name", user)
864 |> assign(:user, user)
865 |> get("/api/v1/lists/#{list.id}")
867 assert %{"id" => id} = json_response(conn, 200)
868 assert id == to_string(list.id)
871 test "renaming a list", %{conn: conn} do
873 {:ok, list} = Pleroma.List.create("name", user)
877 |> assign(:user, user)
878 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
880 assert %{"title" => name} = json_response(conn, 200)
881 assert name == "newname"
884 test "deleting a list", %{conn: conn} do
886 {:ok, list} = Pleroma.List.create("name", user)
890 |> assign(:user, user)
891 |> delete("/api/v1/lists/#{list.id}")
893 assert %{} = json_response(conn, 200)
894 assert is_nil(Repo.get(Pleroma.List, list.id))
897 test "list timeline", %{conn: conn} do
899 other_user = insert(:user)
900 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
901 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
902 {:ok, list} = Pleroma.List.create("name", user)
903 {:ok, list} = Pleroma.List.follow(list, other_user)
907 |> assign(:user, user)
908 |> get("/api/v1/timelines/list/#{list.id}")
910 assert [%{"id" => id}] = json_response(conn, 200)
912 assert id == to_string(activity_two.id)
915 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
917 other_user = insert(:user)
918 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
920 {:ok, _activity_two} =
921 TwitterAPI.create_status(other_user, %{
922 "status" => "Marisa is cute.",
923 "visibility" => "private"
926 {:ok, list} = Pleroma.List.create("name", user)
927 {:ok, list} = Pleroma.List.follow(list, other_user)
931 |> assign(:user, user)
932 |> get("/api/v1/timelines/list/#{list.id}")
934 assert [%{"id" => id}] = json_response(conn, 200)
936 assert id == to_string(activity_one.id)
940 describe "notifications" do
941 test "list of notifications", %{conn: conn} do
943 other_user = insert(:user)
946 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
948 {:ok, [_notification]} = Notification.create_notifications(activity)
952 |> assign(:user, user)
953 |> get("/api/v1/notifications")
956 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
958 }\">@<span>#{user.nickname}</span></a></span>"
960 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
961 assert response == expected_response
964 test "getting a single notification", %{conn: conn} do
966 other_user = insert(:user)
969 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
971 {:ok, [notification]} = Notification.create_notifications(activity)
975 |> assign(:user, user)
976 |> get("/api/v1/notifications/#{notification.id}")
979 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
981 }\">@<span>#{user.nickname}</span></a></span>"
983 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
984 assert response == expected_response
987 test "dismissing a single notification", %{conn: conn} do
989 other_user = insert(:user)
992 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
994 {:ok, [notification]} = Notification.create_notifications(activity)
998 |> assign(:user, user)
999 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1001 assert %{} = json_response(conn, 200)
1004 test "clearing all notifications", %{conn: conn} do
1005 user = insert(:user)
1006 other_user = insert(:user)
1009 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
1011 {:ok, [_notification]} = Notification.create_notifications(activity)
1015 |> assign(:user, user)
1016 |> post("/api/v1/notifications/clear")
1018 assert %{} = json_response(conn, 200)
1022 |> assign(:user, user)
1023 |> get("/api/v1/notifications")
1025 assert all = json_response(conn, 200)
1029 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1030 user = insert(:user)
1031 other_user = insert(:user)
1033 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1034 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1035 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1036 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1038 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1039 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1040 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1041 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1045 |> assign(:user, user)
1050 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1052 result = json_response(conn_res, 200)
1053 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1058 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1060 result = json_response(conn_res, 200)
1061 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1066 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1068 result = json_response(conn_res, 200)
1069 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1072 test "filters notifications using exclude_types", %{conn: conn} do
1073 user = insert(:user)
1074 other_user = insert(:user)
1076 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1077 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1078 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1079 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1080 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1082 mention_notification_id =
1083 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1085 favorite_notification_id =
1086 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1088 reblog_notification_id =
1089 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1091 follow_notification_id =
1092 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1096 |> assign(:user, user)
1099 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1101 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1104 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1106 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1109 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1111 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1114 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1116 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1119 test "destroy multiple", %{conn: conn} do
1120 user = insert(:user)
1121 other_user = insert(:user)
1123 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1124 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1125 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1126 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1128 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1129 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1130 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1131 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1135 |> assign(:user, user)
1139 |> get("/api/v1/notifications")
1141 result = json_response(conn_res, 200)
1142 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1146 |> assign(:user, other_user)
1150 |> get("/api/v1/notifications")
1152 result = json_response(conn_res, 200)
1153 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1157 |> delete("/api/v1/notifications/destroy_multiple", %{
1158 "ids" => [notification1_id, notification2_id]
1161 assert json_response(conn_destroy, 200) == %{}
1165 |> get("/api/v1/notifications")
1167 result = json_response(conn_res, 200)
1168 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1172 describe "reblogging" do
1173 test "reblogs and returns the reblogged status", %{conn: conn} do
1174 activity = insert(:note_activity)
1175 user = insert(:user)
1179 |> assign(:user, user)
1180 |> post("/api/v1/statuses/#{activity.id}/reblog")
1183 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1185 } = json_response(conn, 200)
1187 assert to_string(activity.id) == id
1190 test "reblogged status for another user", %{conn: conn} do
1191 activity = insert(:note_activity)
1192 user1 = insert(:user)
1193 user2 = insert(:user)
1194 user3 = insert(:user)
1195 CommonAPI.favorite(activity.id, user2)
1196 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1197 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1198 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1202 |> assign(:user, user3)
1203 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1206 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1207 "reblogged" => false,
1208 "favourited" => false,
1209 "bookmarked" => false
1210 } = json_response(conn_res, 200)
1214 |> assign(:user, user2)
1215 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1218 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1219 "reblogged" => true,
1220 "favourited" => true,
1221 "bookmarked" => true
1222 } = json_response(conn_res, 200)
1224 assert to_string(activity.id) == id
1228 describe "unreblogging" do
1229 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1230 activity = insert(:note_activity)
1231 user = insert(:user)
1233 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1237 |> assign(:user, user)
1238 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1240 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1242 assert to_string(activity.id) == id
1246 describe "favoriting" do
1247 test "favs a status and returns it", %{conn: conn} do
1248 activity = insert(:note_activity)
1249 user = insert(:user)
1253 |> assign(:user, user)
1254 |> post("/api/v1/statuses/#{activity.id}/favourite")
1256 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1257 json_response(conn, 200)
1259 assert to_string(activity.id) == id
1262 test "returns 500 for a wrong id", %{conn: conn} do
1263 user = insert(:user)
1267 |> assign(:user, user)
1268 |> post("/api/v1/statuses/1/favourite")
1269 |> json_response(500)
1271 assert resp == "Something went wrong"
1275 describe "unfavoriting" do
1276 test "unfavorites a status and returns it", %{conn: conn} do
1277 activity = insert(:note_activity)
1278 user = insert(:user)
1280 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1284 |> assign(:user, user)
1285 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1287 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1288 json_response(conn, 200)
1290 assert to_string(activity.id) == id
1294 describe "user timelines" do
1295 test "gets a users statuses", %{conn: conn} do
1296 user_one = insert(:user)
1297 user_two = insert(:user)
1298 user_three = insert(:user)
1300 {:ok, user_three} = User.follow(user_three, user_one)
1302 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1304 {:ok, direct_activity} =
1305 CommonAPI.post(user_one, %{
1306 "status" => "Hi, @#{user_two.nickname}.",
1307 "visibility" => "direct"
1310 {:ok, private_activity} =
1311 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1315 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1317 assert [%{"id" => id}] = json_response(resp, 200)
1318 assert id == to_string(activity.id)
1322 |> assign(:user, user_two)
1323 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1325 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1326 assert id_one == to_string(direct_activity.id)
1327 assert id_two == to_string(activity.id)
1331 |> assign(:user, user_three)
1332 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1334 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1335 assert id_one == to_string(private_activity.id)
1336 assert id_two == to_string(activity.id)
1339 test "unimplemented pinned statuses feature", %{conn: conn} do
1340 note = insert(:note_activity)
1341 user = User.get_cached_by_ap_id(note.data["actor"])
1345 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1347 assert json_response(conn, 200) == []
1350 test "gets an users media", %{conn: conn} do
1351 note = insert(:note_activity)
1352 user = User.get_cached_by_ap_id(note.data["actor"])
1354 file = %Plug.Upload{
1355 content_type: "image/jpg",
1356 path: Path.absname("test/fixtures/image.jpg"),
1357 filename: "an_image.jpg"
1361 TwitterAPI.upload(file, user, "json")
1365 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1369 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1371 assert [%{"id" => id}] = json_response(conn, 200)
1372 assert id == to_string(image_post.id)
1376 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1378 assert [%{"id" => id}] = json_response(conn, 200)
1379 assert id == to_string(image_post.id)
1382 test "gets a user's statuses without reblogs", %{conn: conn} do
1383 user = insert(:user)
1384 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1385 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1389 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1391 assert [%{"id" => id}] = json_response(conn, 200)
1392 assert id == to_string(post.id)
1396 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1398 assert [%{"id" => id}] = json_response(conn, 200)
1399 assert id == to_string(post.id)
1403 describe "user relationships" do
1404 test "returns the relationships for the current user", %{conn: conn} do
1405 user = insert(:user)
1406 other_user = insert(:user)
1407 {:ok, user} = User.follow(user, other_user)
1411 |> assign(:user, user)
1412 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1414 assert [relationship] = json_response(conn, 200)
1416 assert to_string(other_user.id) == relationship["id"]
1420 describe "locked accounts" do
1421 test "/api/v1/follow_requests works" do
1422 user = insert(:user, %{info: %User.Info{locked: true}})
1423 other_user = insert(:user)
1425 {:ok, _activity} = ActivityPub.follow(other_user, user)
1427 user = User.get_cached_by_id(user.id)
1428 other_user = User.get_cached_by_id(other_user.id)
1430 assert User.following?(other_user, user) == false
1434 |> assign(:user, user)
1435 |> get("/api/v1/follow_requests")
1437 assert [relationship] = json_response(conn, 200)
1438 assert to_string(other_user.id) == relationship["id"]
1441 test "/api/v1/follow_requests/:id/authorize works" do
1442 user = insert(:user, %{info: %User.Info{locked: true}})
1443 other_user = insert(:user)
1445 {:ok, _activity} = ActivityPub.follow(other_user, user)
1447 user = User.get_cached_by_id(user.id)
1448 other_user = User.get_cached_by_id(other_user.id)
1450 assert User.following?(other_user, user) == false
1454 |> assign(:user, user)
1455 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1457 assert relationship = json_response(conn, 200)
1458 assert to_string(other_user.id) == relationship["id"]
1460 user = User.get_cached_by_id(user.id)
1461 other_user = User.get_cached_by_id(other_user.id)
1463 assert User.following?(other_user, user) == true
1466 test "verify_credentials", %{conn: conn} do
1467 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1471 |> assign(:user, user)
1472 |> get("/api/v1/accounts/verify_credentials")
1474 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1475 assert id == to_string(user.id)
1478 test "/api/v1/follow_requests/:id/reject works" do
1479 user = insert(:user, %{info: %User.Info{locked: true}})
1480 other_user = insert(:user)
1482 {:ok, _activity} = ActivityPub.follow(other_user, user)
1484 user = User.get_cached_by_id(user.id)
1488 |> assign(:user, user)
1489 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1491 assert relationship = json_response(conn, 200)
1492 assert to_string(other_user.id) == relationship["id"]
1494 user = User.get_cached_by_id(user.id)
1495 other_user = User.get_cached_by_id(other_user.id)
1497 assert User.following?(other_user, user) == false
1501 test "account fetching", %{conn: conn} do
1502 user = insert(:user)
1506 |> get("/api/v1/accounts/#{user.id}")
1508 assert %{"id" => id} = json_response(conn, 200)
1509 assert id == to_string(user.id)
1513 |> get("/api/v1/accounts/-1")
1515 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1518 test "account fetching also works nickname", %{conn: conn} do
1519 user = insert(:user)
1523 |> get("/api/v1/accounts/#{user.nickname}")
1525 assert %{"id" => id} = json_response(conn, 200)
1526 assert id == user.id
1529 test "media upload", %{conn: conn} do
1530 file = %Plug.Upload{
1531 content_type: "image/jpg",
1532 path: Path.absname("test/fixtures/image.jpg"),
1533 filename: "an_image.jpg"
1536 desc = "Description of the image"
1538 user = insert(:user)
1542 |> assign(:user, user)
1543 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1545 assert media = json_response(conn, 200)
1547 assert media["type"] == "image"
1548 assert media["description"] == desc
1551 object = Repo.get(Object, media["id"])
1552 assert object.data["actor"] == User.ap_id(user)
1555 test "mascot upload", %{conn: conn} do
1556 user = insert(:user)
1558 non_image_file = %Plug.Upload{
1559 content_type: "audio/mpeg",
1560 path: Path.absname("test/fixtures/sound.mp3"),
1561 filename: "sound.mp3"
1566 |> assign(:user, user)
1567 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1569 assert json_response(conn, 415)
1571 file = %Plug.Upload{
1572 content_type: "image/jpg",
1573 path: Path.absname("test/fixtures/image.jpg"),
1574 filename: "an_image.jpg"
1579 |> assign(:user, user)
1580 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1582 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1585 test "mascot retrieving", %{conn: conn} do
1586 user = insert(:user)
1587 # When user hasn't set a mascot, we should just get pleroma tan back
1590 |> assign(:user, user)
1591 |> get("/api/v1/pleroma/mascot")
1593 assert %{"url" => url} = json_response(conn, 200)
1594 assert url =~ "pleroma-fox-tan-smol"
1596 # When a user sets their mascot, we should get that back
1597 file = %Plug.Upload{
1598 content_type: "image/jpg",
1599 path: Path.absname("test/fixtures/image.jpg"),
1600 filename: "an_image.jpg"
1605 |> assign(:user, user)
1606 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1608 assert json_response(conn, 200)
1610 user = User.get_cached_by_id(user.id)
1614 |> assign(:user, user)
1615 |> get("/api/v1/pleroma/mascot")
1617 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1618 assert url =~ "an_image"
1621 test "hashtag timeline", %{conn: conn} do
1622 following = insert(:user)
1625 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1627 {:ok, [_activity]} =
1628 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1632 |> get("/api/v1/timelines/tag/2hu")
1634 assert [%{"id" => id}] = json_response(nconn, 200)
1636 assert id == to_string(activity.id)
1638 # works for different capitalization too
1641 |> get("/api/v1/timelines/tag/2HU")
1643 assert [%{"id" => id}] = json_response(nconn, 200)
1645 assert id == to_string(activity.id)
1649 test "multi-hashtag timeline", %{conn: conn} do
1650 user = insert(:user)
1652 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1653 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1654 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1658 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1660 [status_none, status_test1, status_test] = json_response(any_test, 200)
1662 assert to_string(activity_test.id) == status_test["id"]
1663 assert to_string(activity_test1.id) == status_test1["id"]
1664 assert to_string(activity_none.id) == status_none["id"]
1668 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1670 assert [status_test1] == json_response(restricted_test, 200)
1672 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1674 assert [status_none] == json_response(all_test, 200)
1677 test "getting followers", %{conn: conn} do
1678 user = insert(:user)
1679 other_user = insert(:user)
1680 {:ok, user} = User.follow(user, other_user)
1684 |> get("/api/v1/accounts/#{other_user.id}/followers")
1686 assert [%{"id" => id}] = json_response(conn, 200)
1687 assert id == to_string(user.id)
1690 test "getting followers, hide_followers", %{conn: conn} do
1691 user = insert(:user)
1692 other_user = insert(:user, %{info: %{hide_followers: true}})
1693 {:ok, _user} = User.follow(user, other_user)
1697 |> get("/api/v1/accounts/#{other_user.id}/followers")
1699 assert [] == json_response(conn, 200)
1702 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1703 user = insert(:user)
1704 other_user = insert(:user, %{info: %{hide_followers: true}})
1705 {:ok, _user} = User.follow(user, other_user)
1709 |> assign(:user, other_user)
1710 |> get("/api/v1/accounts/#{other_user.id}/followers")
1712 refute [] == json_response(conn, 200)
1715 test "getting followers, pagination", %{conn: conn} do
1716 user = insert(:user)
1717 follower1 = insert(:user)
1718 follower2 = insert(:user)
1719 follower3 = insert(:user)
1720 {:ok, _} = User.follow(follower1, user)
1721 {:ok, _} = User.follow(follower2, user)
1722 {:ok, _} = User.follow(follower3, user)
1726 |> assign(:user, user)
1730 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1732 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1733 assert id3 == follower3.id
1734 assert id2 == follower2.id
1738 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1740 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1741 assert id2 == follower2.id
1742 assert id1 == follower1.id
1746 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1748 assert [%{"id" => id2}] = json_response(res_conn, 200)
1749 assert id2 == follower2.id
1751 assert [link_header] = get_resp_header(res_conn, "link")
1752 assert link_header =~ ~r/min_id=#{follower2.id}/
1753 assert link_header =~ ~r/max_id=#{follower2.id}/
1756 test "getting following", %{conn: conn} do
1757 user = insert(:user)
1758 other_user = insert(:user)
1759 {:ok, user} = User.follow(user, other_user)
1763 |> get("/api/v1/accounts/#{user.id}/following")
1765 assert [%{"id" => id}] = json_response(conn, 200)
1766 assert id == to_string(other_user.id)
1769 test "getting following, hide_follows", %{conn: conn} do
1770 user = insert(:user, %{info: %{hide_follows: true}})
1771 other_user = insert(:user)
1772 {:ok, user} = User.follow(user, other_user)
1776 |> get("/api/v1/accounts/#{user.id}/following")
1778 assert [] == json_response(conn, 200)
1781 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1782 user = insert(:user, %{info: %{hide_follows: true}})
1783 other_user = insert(:user)
1784 {:ok, user} = User.follow(user, other_user)
1788 |> assign(:user, user)
1789 |> get("/api/v1/accounts/#{user.id}/following")
1791 refute [] == json_response(conn, 200)
1794 test "getting following, pagination", %{conn: conn} do
1795 user = insert(:user)
1796 following1 = insert(:user)
1797 following2 = insert(:user)
1798 following3 = insert(:user)
1799 {:ok, _} = User.follow(user, following1)
1800 {:ok, _} = User.follow(user, following2)
1801 {:ok, _} = User.follow(user, following3)
1805 |> assign(:user, user)
1809 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1811 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1812 assert id3 == following3.id
1813 assert id2 == following2.id
1817 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1819 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1820 assert id2 == following2.id
1821 assert id1 == following1.id
1825 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1827 assert [%{"id" => id2}] = json_response(res_conn, 200)
1828 assert id2 == following2.id
1830 assert [link_header] = get_resp_header(res_conn, "link")
1831 assert link_header =~ ~r/min_id=#{following2.id}/
1832 assert link_header =~ ~r/max_id=#{following2.id}/
1835 test "following / unfollowing a user", %{conn: conn} do
1836 user = insert(:user)
1837 other_user = insert(:user)
1841 |> assign(:user, user)
1842 |> post("/api/v1/accounts/#{other_user.id}/follow")
1844 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1846 user = User.get_cached_by_id(user.id)
1850 |> assign(:user, user)
1851 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1853 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1855 user = User.get_cached_by_id(user.id)
1859 |> assign(:user, user)
1860 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1862 assert %{"id" => id} = json_response(conn, 200)
1863 assert id == to_string(other_user.id)
1866 test "following without reblogs" do
1867 follower = insert(:user)
1868 followed = insert(:user)
1869 other_user = insert(:user)
1873 |> assign(:user, follower)
1874 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1876 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1878 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1879 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1883 |> assign(:user, User.get_cached_by_id(follower.id))
1884 |> get("/api/v1/timelines/home")
1886 assert [] == json_response(conn, 200)
1890 |> assign(:user, follower)
1891 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1893 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1897 |> assign(:user, User.get_cached_by_id(follower.id))
1898 |> get("/api/v1/timelines/home")
1900 expected_activity_id = reblog.id
1901 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1904 test "following / unfollowing errors" do
1905 user = insert(:user)
1909 |> assign(:user, user)
1912 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1913 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1916 user = User.get_cached_by_id(user.id)
1917 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1918 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1920 # self follow via uri
1921 user = User.get_cached_by_id(user.id)
1922 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1923 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1925 # follow non existing user
1926 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1927 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1929 # follow non existing user via uri
1930 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1931 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1933 # unfollow non existing user
1934 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1935 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1938 test "muting / unmuting a user", %{conn: conn} do
1939 user = insert(:user)
1940 other_user = insert(:user)
1944 |> assign(:user, user)
1945 |> post("/api/v1/accounts/#{other_user.id}/mute")
1947 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1949 user = User.get_cached_by_id(user.id)
1953 |> assign(:user, user)
1954 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1956 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1959 test "subscribing / unsubscribing to a user", %{conn: conn} do
1960 user = insert(:user)
1961 subscription_target = insert(:user)
1965 |> assign(:user, user)
1966 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1968 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1972 |> assign(:user, user)
1973 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1975 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1978 test "getting a list of mutes", %{conn: conn} do
1979 user = insert(:user)
1980 other_user = insert(:user)
1982 {:ok, user} = User.mute(user, other_user)
1986 |> assign(:user, user)
1987 |> get("/api/v1/mutes")
1989 other_user_id = to_string(other_user.id)
1990 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1993 test "blocking / unblocking a user", %{conn: conn} do
1994 user = insert(:user)
1995 other_user = insert(:user)
1999 |> assign(:user, user)
2000 |> post("/api/v1/accounts/#{other_user.id}/block")
2002 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2004 user = User.get_cached_by_id(user.id)
2008 |> assign(:user, user)
2009 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2011 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2014 test "getting a list of blocks", %{conn: conn} do
2015 user = insert(:user)
2016 other_user = insert(:user)
2018 {:ok, user} = User.block(user, other_user)
2022 |> assign(:user, user)
2023 |> get("/api/v1/blocks")
2025 other_user_id = to_string(other_user.id)
2026 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2029 test "blocking / unblocking a domain", %{conn: conn} do
2030 user = insert(:user)
2031 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2035 |> assign(:user, user)
2036 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2038 assert %{} = json_response(conn, 200)
2039 user = User.get_cached_by_ap_id(user.ap_id)
2040 assert User.blocks?(user, other_user)
2044 |> assign(:user, user)
2045 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2047 assert %{} = json_response(conn, 200)
2048 user = User.get_cached_by_ap_id(user.ap_id)
2049 refute User.blocks?(user, other_user)
2052 test "getting a list of domain blocks", %{conn: conn} do
2053 user = insert(:user)
2055 {:ok, user} = User.block_domain(user, "bad.site")
2056 {:ok, user} = User.block_domain(user, "even.worse.site")
2060 |> assign(:user, user)
2061 |> get("/api/v1/domain_blocks")
2063 domain_blocks = json_response(conn, 200)
2065 assert "bad.site" in domain_blocks
2066 assert "even.worse.site" in domain_blocks
2069 test "unimplemented follow_requests, blocks, domain blocks" do
2070 user = insert(:user)
2072 ["blocks", "domain_blocks", "follow_requests"]
2073 |> Enum.each(fn endpoint ->
2076 |> assign(:user, user)
2077 |> get("/api/v1/#{endpoint}")
2079 assert [] = json_response(conn, 200)
2083 test "account search", %{conn: conn} do
2084 user = insert(:user)
2085 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2086 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2090 |> assign(:user, user)
2091 |> get("/api/v1/accounts/search", %{"q" => "shp"})
2092 |> json_response(200)
2094 result_ids = for result <- results, do: result["acct"]
2096 assert user_two.nickname in result_ids
2097 assert user_three.nickname in result_ids
2101 |> assign(:user, user)
2102 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
2103 |> json_response(200)
2105 result_ids = for result <- results, do: result["acct"]
2107 assert user_three.nickname in result_ids
2110 test "search", %{conn: conn} do
2111 user = insert(:user)
2112 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2113 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2115 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
2118 CommonAPI.post(user, %{
2119 "status" => "This is about 2hu, but private",
2120 "visibility" => "private"
2123 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
2127 |> get("/api/v1/search", %{"q" => "2hu"})
2129 assert results = json_response(conn, 200)
2131 [account | _] = results["accounts"]
2132 assert account["id"] == to_string(user_three.id)
2134 assert results["hashtags"] == []
2136 [status] = results["statuses"]
2137 assert status["id"] == to_string(activity.id)
2140 test "search fetches remote statuses", %{conn: conn} do
2144 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
2146 assert results = json_response(conn, 200)
2148 [status] = results["statuses"]
2149 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
2153 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
2155 CommonAPI.post(insert(:user), %{
2156 "status" => "This is about 2hu, but private",
2157 "visibility" => "private"
2163 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2165 assert results = json_response(conn, 200)
2167 [] = results["statuses"]
2171 test "search fetches remote accounts", %{conn: conn} do
2174 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2176 assert results = json_response(conn, 200)
2177 [account] = results["accounts"]
2178 assert account["acct"] == "shp@social.heldscal.la"
2181 test "returns the favorites of a user", %{conn: conn} do
2182 user = insert(:user)
2183 other_user = insert(:user)
2185 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2186 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2188 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2192 |> assign(:user, user)
2193 |> get("/api/v1/favourites")
2195 assert [status] = json_response(first_conn, 200)
2196 assert status["id"] == to_string(activity.id)
2198 assert [{"link", _link_header}] =
2199 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2201 # Honours query params
2202 {:ok, second_activity} =
2203 CommonAPI.post(other_user, %{
2205 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2208 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2210 last_like = status["id"]
2214 |> assign(:user, user)
2215 |> get("/api/v1/favourites?since_id=#{last_like}")
2217 assert [second_status] = json_response(second_conn, 200)
2218 assert second_status["id"] == to_string(second_activity.id)
2222 |> assign(:user, user)
2223 |> get("/api/v1/favourites?limit=0")
2225 assert [] = json_response(third_conn, 200)
2228 describe "getting favorites timeline of specified user" do
2230 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2231 [current_user: current_user, user: user]
2234 test "returns list of statuses favorited by specified user", %{
2236 current_user: current_user,
2239 [activity | _] = insert_pair(:note_activity)
2240 CommonAPI.favorite(activity.id, user)
2244 |> assign(:user, current_user)
2245 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2246 |> json_response(:ok)
2250 assert length(response) == 1
2251 assert like["id"] == activity.id
2254 test "returns favorites for specified user_id when user is not logged in", %{
2258 activity = insert(:note_activity)
2259 CommonAPI.favorite(activity.id, user)
2263 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2264 |> json_response(:ok)
2266 assert length(response) == 1
2269 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2271 current_user: current_user,
2275 CommonAPI.post(current_user, %{
2276 "status" => "Hi @#{user.nickname}!",
2277 "visibility" => "direct"
2280 CommonAPI.favorite(direct.id, user)
2284 |> assign(:user, current_user)
2285 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2286 |> json_response(:ok)
2288 assert length(response) == 1
2290 anonymous_response =
2292 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2293 |> json_response(:ok)
2295 assert Enum.empty?(anonymous_response)
2298 test "does not return others' favorited DM when user is not one of recipients", %{
2300 current_user: current_user,
2303 user_two = insert(:user)
2306 CommonAPI.post(user_two, %{
2307 "status" => "Hi @#{user.nickname}!",
2308 "visibility" => "direct"
2311 CommonAPI.favorite(direct.id, user)
2315 |> assign(:user, current_user)
2316 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2317 |> json_response(:ok)
2319 assert Enum.empty?(response)
2322 test "paginates favorites using since_id and max_id", %{
2324 current_user: current_user,
2327 activities = insert_list(10, :note_activity)
2329 Enum.each(activities, fn activity ->
2330 CommonAPI.favorite(activity.id, user)
2333 third_activity = Enum.at(activities, 2)
2334 seventh_activity = Enum.at(activities, 6)
2338 |> assign(:user, current_user)
2339 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2340 since_id: third_activity.id,
2341 max_id: seventh_activity.id
2343 |> json_response(:ok)
2345 assert length(response) == 3
2346 refute third_activity in response
2347 refute seventh_activity in response
2350 test "limits favorites using limit parameter", %{
2352 current_user: current_user,
2356 |> insert_list(:note_activity)
2357 |> Enum.each(fn activity ->
2358 CommonAPI.favorite(activity.id, user)
2363 |> assign(:user, current_user)
2364 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2365 |> json_response(:ok)
2367 assert length(response) == 3
2370 test "returns empty response when user does not have any favorited statuses", %{
2372 current_user: current_user,
2377 |> assign(:user, current_user)
2378 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2379 |> json_response(:ok)
2381 assert Enum.empty?(response)
2384 test "returns 404 error when specified user is not exist", %{conn: conn} do
2385 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2387 assert json_response(conn, 404) == %{"error" => "Record not found"}
2390 test "returns 403 error when user has hidden own favorites", %{
2392 current_user: current_user
2394 user = insert(:user, %{info: %{hide_favorites: true}})
2395 activity = insert(:note_activity)
2396 CommonAPI.favorite(activity.id, user)
2400 |> assign(:user, current_user)
2401 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2403 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2406 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2407 user = insert(:user)
2408 activity = insert(:note_activity)
2409 CommonAPI.favorite(activity.id, user)
2413 |> assign(:user, current_user)
2414 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2416 assert user.info.hide_favorites
2417 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2421 describe "updating credentials" do
2422 test "updates the user's bio", %{conn: conn} do
2423 user = insert(:user)
2424 user2 = insert(:user)
2428 |> assign(:user, user)
2429 |> patch("/api/v1/accounts/update_credentials", %{
2430 "note" => "I drink #cofe with @#{user2.nickname}"
2433 assert user = json_response(conn, 200)
2435 assert user["note"] ==
2436 ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe" rel="tag">#cofe</a> with <span class="h-card"><a data-user=") <>
2438 ~s(" class="u-url mention" href=") <>
2439 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2442 test "updates the user's locking status", %{conn: conn} do
2443 user = insert(:user)
2447 |> assign(:user, user)
2448 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2450 assert user = json_response(conn, 200)
2451 assert user["locked"] == true
2454 test "updates the user's default scope", %{conn: conn} do
2455 user = insert(:user)
2459 |> assign(:user, user)
2460 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2462 assert user = json_response(conn, 200)
2463 assert user["source"]["privacy"] == "cofe"
2466 test "updates the user's hide_followers status", %{conn: conn} do
2467 user = insert(:user)
2471 |> assign(:user, user)
2472 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2474 assert user = json_response(conn, 200)
2475 assert user["pleroma"]["hide_followers"] == true
2478 test "updates the user's hide_follows status", %{conn: conn} do
2479 user = insert(:user)
2483 |> assign(:user, user)
2484 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2486 assert user = json_response(conn, 200)
2487 assert user["pleroma"]["hide_follows"] == true
2490 test "updates the user's hide_favorites status", %{conn: conn} do
2491 user = insert(:user)
2495 |> assign(:user, user)
2496 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2498 assert user = json_response(conn, 200)
2499 assert user["pleroma"]["hide_favorites"] == true
2502 test "updates the user's show_role status", %{conn: conn} do
2503 user = insert(:user)
2507 |> assign(:user, user)
2508 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2510 assert user = json_response(conn, 200)
2511 assert user["source"]["pleroma"]["show_role"] == false
2514 test "updates the user's no_rich_text status", %{conn: conn} do
2515 user = insert(:user)
2519 |> assign(:user, user)
2520 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2522 assert user = json_response(conn, 200)
2523 assert user["source"]["pleroma"]["no_rich_text"] == true
2526 test "updates the user's name", %{conn: conn} do
2527 user = insert(:user)
2531 |> assign(:user, user)
2532 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2534 assert user = json_response(conn, 200)
2535 assert user["display_name"] == "markorepairs"
2538 test "updates the user's avatar", %{conn: conn} do
2539 user = insert(:user)
2541 new_avatar = %Plug.Upload{
2542 content_type: "image/jpg",
2543 path: Path.absname("test/fixtures/image.jpg"),
2544 filename: "an_image.jpg"
2549 |> assign(:user, user)
2550 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2552 assert user_response = json_response(conn, 200)
2553 assert user_response["avatar"] != User.avatar_url(user)
2556 test "updates the user's banner", %{conn: conn} do
2557 user = insert(:user)
2559 new_header = %Plug.Upload{
2560 content_type: "image/jpg",
2561 path: Path.absname("test/fixtures/image.jpg"),
2562 filename: "an_image.jpg"
2567 |> assign(:user, user)
2568 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2570 assert user_response = json_response(conn, 200)
2571 assert user_response["header"] != User.banner_url(user)
2574 test "requires 'write' permission", %{conn: conn} do
2575 token1 = insert(:oauth_token, scopes: ["read"])
2576 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2578 for token <- [token1, token2] do
2581 |> put_req_header("authorization", "Bearer #{token.token}")
2582 |> patch("/api/v1/accounts/update_credentials", %{})
2584 if token == token1 do
2585 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2587 assert json_response(conn, 200)
2592 test "updates profile emojos", %{conn: conn} do
2593 user = insert(:user)
2595 note = "*sips :blank:*"
2596 name = "I am :firefox:"
2600 |> assign(:user, user)
2601 |> patch("/api/v1/accounts/update_credentials", %{
2603 "display_name" => name
2606 assert json_response(conn, 200)
2610 |> get("/api/v1/accounts/#{user.id}")
2612 assert user = json_response(conn, 200)
2614 assert user["note"] == note
2615 assert user["display_name"] == name
2616 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2620 test "get instance information", %{conn: conn} do
2621 conn = get(conn, "/api/v1/instance")
2622 assert result = json_response(conn, 200)
2624 email = Pleroma.Config.get([:instance, :email])
2625 # Note: not checking for "max_toot_chars" since it's optional
2631 "email" => from_config_email,
2633 "streaming_api" => _
2638 "registrations" => _,
2642 assert email == from_config_email
2645 test "get instance stats", %{conn: conn} do
2646 user = insert(:user, %{local: true})
2648 user2 = insert(:user, %{local: true})
2649 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2651 insert(:user, %{local: false, nickname: "u@peer1.com"})
2652 insert(:user, %{local: false, nickname: "u@peer2.com"})
2654 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2656 # Stats should count users with missing or nil `info.deactivated` value
2657 user = User.get_cached_by_id(user.id)
2658 info_change = Changeset.change(user.info, %{deactivated: nil})
2662 |> Changeset.change()
2663 |> Changeset.put_embed(:info, info_change)
2664 |> User.update_and_set_cache()
2666 Pleroma.Stats.update_stats()
2668 conn = get(conn, "/api/v1/instance")
2670 assert result = json_response(conn, 200)
2672 stats = result["stats"]
2675 assert stats["user_count"] == 1
2676 assert stats["status_count"] == 1
2677 assert stats["domain_count"] == 2
2680 test "get peers", %{conn: conn} do
2681 insert(:user, %{local: false, nickname: "u@peer1.com"})
2682 insert(:user, %{local: false, nickname: "u@peer2.com"})
2684 Pleroma.Stats.update_stats()
2686 conn = get(conn, "/api/v1/instance/peers")
2688 assert result = json_response(conn, 200)
2690 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2693 test "put settings", %{conn: conn} do
2694 user = insert(:user)
2698 |> assign(:user, user)
2699 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2701 assert _result = json_response(conn, 200)
2703 user = User.get_cached_by_ap_id(user.ap_id)
2704 assert user.info.settings == %{"programming" => "socks"}
2707 describe "pinned statuses" do
2709 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2711 user = insert(:user)
2712 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2714 [user: user, activity: activity]
2717 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2718 {:ok, _} = CommonAPI.pin(activity.id, user)
2722 |> assign(:user, user)
2723 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2724 |> json_response(200)
2726 id_str = to_string(activity.id)
2728 assert [%{"id" => ^id_str, "pinned" => true}] = result
2731 test "pin status", %{conn: conn, user: user, activity: activity} do
2732 id_str = to_string(activity.id)
2734 assert %{"id" => ^id_str, "pinned" => true} =
2736 |> assign(:user, user)
2737 |> post("/api/v1/statuses/#{activity.id}/pin")
2738 |> json_response(200)
2740 assert [%{"id" => ^id_str, "pinned" => true}] =
2742 |> assign(:user, user)
2743 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2744 |> json_response(200)
2747 test "unpin status", %{conn: conn, user: user, activity: activity} do
2748 {:ok, _} = CommonAPI.pin(activity.id, user)
2750 id_str = to_string(activity.id)
2751 user = refresh_record(user)
2753 assert %{"id" => ^id_str, "pinned" => false} =
2755 |> assign(:user, user)
2756 |> post("/api/v1/statuses/#{activity.id}/unpin")
2757 |> json_response(200)
2761 |> assign(:user, user)
2762 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2763 |> json_response(200)
2766 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2767 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2769 id_str_one = to_string(activity_one.id)
2771 assert %{"id" => ^id_str_one, "pinned" => true} =
2773 |> assign(:user, user)
2774 |> post("/api/v1/statuses/#{id_str_one}/pin")
2775 |> json_response(200)
2777 user = refresh_record(user)
2779 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2781 |> assign(:user, user)
2782 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2783 |> json_response(400)
2789 Pleroma.Config.put([:rich_media, :enabled], true)
2792 Pleroma.Config.put([:rich_media, :enabled], false)
2795 user = insert(:user)
2799 test "returns rich-media card", %{conn: conn, user: user} do
2800 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2803 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2804 "provider_name" => "www.imdb.com",
2805 "provider_url" => "http://www.imdb.com",
2806 "title" => "The Rock",
2808 "url" => "http://www.imdb.com/title/tt0117500/",
2810 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2813 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2814 "title" => "The Rock",
2815 "type" => "video.movie",
2816 "url" => "http://www.imdb.com/title/tt0117500/",
2818 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2825 |> get("/api/v1/statuses/#{activity.id}/card")
2826 |> json_response(200)
2828 assert response == card_data
2830 # works with private posts
2832 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2836 |> assign(:user, user)
2837 |> get("/api/v1/statuses/#{activity.id}/card")
2838 |> json_response(200)
2840 assert response_two == card_data
2843 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2844 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp-missing-data"})
2848 |> get("/api/v1/statuses/#{activity.id}/card")
2849 |> json_response(:ok)
2851 assert response == %{
2853 "title" => "Pleroma",
2854 "description" => "",
2856 "provider_name" => "pleroma.social",
2857 "provider_url" => "https://pleroma.social",
2858 "url" => "https://pleroma.social/",
2861 "title" => "Pleroma",
2862 "type" => "website",
2863 "url" => "https://pleroma.social/"
2871 user = insert(:user)
2872 for_user = insert(:user)
2875 CommonAPI.post(user, %{
2876 "status" => "heweoo?"
2880 CommonAPI.post(user, %{
2881 "status" => "heweoo!"
2886 |> assign(:user, for_user)
2887 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2889 assert json_response(response1, 200)["bookmarked"] == true
2893 |> assign(:user, for_user)
2894 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2896 assert json_response(response2, 200)["bookmarked"] == true
2900 |> assign(:user, for_user)
2901 |> get("/api/v1/bookmarks")
2903 assert [json_response(response2, 200), json_response(response1, 200)] ==
2904 json_response(bookmarks, 200)
2908 |> assign(:user, for_user)
2909 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2911 assert json_response(response1, 200)["bookmarked"] == false
2915 |> assign(:user, for_user)
2916 |> get("/api/v1/bookmarks")
2918 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2921 describe "conversation muting" do
2923 user = insert(:user)
2924 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2926 [user: user, activity: activity]
2929 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2930 id_str = to_string(activity.id)
2932 assert %{"id" => ^id_str, "muted" => true} =
2934 |> assign(:user, user)
2935 |> post("/api/v1/statuses/#{activity.id}/mute")
2936 |> json_response(200)
2939 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2940 {:ok, _} = CommonAPI.add_mute(user, activity)
2942 id_str = to_string(activity.id)
2943 user = refresh_record(user)
2945 assert %{"id" => ^id_str, "muted" => false} =
2947 |> assign(:user, user)
2948 |> post("/api/v1/statuses/#{activity.id}/unmute")
2949 |> json_response(200)
2953 describe "reports" do
2955 reporter = insert(:user)
2956 target_user = insert(:user)
2958 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2960 [reporter: reporter, target_user: target_user, activity: activity]
2963 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2964 assert %{"action_taken" => false, "id" => _} =
2966 |> assign(:user, reporter)
2967 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2968 |> json_response(200)
2971 test "submit a report with statuses and comment", %{
2974 target_user: target_user,
2977 assert %{"action_taken" => false, "id" => _} =
2979 |> assign(:user, reporter)
2980 |> post("/api/v1/reports", %{
2981 "account_id" => target_user.id,
2982 "status_ids" => [activity.id],
2983 "comment" => "bad status!"
2985 |> json_response(200)
2988 test "account_id is required", %{
2993 assert %{"error" => "Valid `account_id` required"} =
2995 |> assign(:user, reporter)
2996 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2997 |> json_response(400)
3000 test "comment must be up to the size specified in the config", %{
3003 target_user: target_user
3005 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
3006 comment = String.pad_trailing("a", max_size + 1, "a")
3008 error = %{"error" => "Comment must be up to #{max_size} characters"}
3012 |> assign(:user, reporter)
3013 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3014 |> json_response(400)
3018 describe "link headers" do
3019 test "preserves parameters in link headers", %{conn: conn} do
3020 user = insert(:user)
3021 other_user = insert(:user)
3024 CommonAPI.post(other_user, %{
3025 "status" => "hi @#{user.nickname}",
3026 "visibility" => "public"
3030 CommonAPI.post(other_user, %{
3031 "status" => "hi @#{user.nickname}",
3032 "visibility" => "public"
3035 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3036 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3040 |> assign(:user, user)
3041 |> get("/api/v1/notifications", %{media_only: true})
3043 assert [link_header] = get_resp_header(conn, "link")
3044 assert link_header =~ ~r/media_only=true/
3045 assert link_header =~ ~r/min_id=#{notification2.id}/
3046 assert link_header =~ ~r/max_id=#{notification1.id}/
3050 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3051 # Need to set an old-style integer ID to reproduce the problem
3052 # (these are no longer assigned to new accounts but were preserved
3053 # for existing accounts during the migration to flakeIDs)
3054 user_one = insert(:user, %{id: 1212})
3055 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3059 |> get("/api/v1/accounts/#{user_one.id}")
3063 |> get("/api/v1/accounts/#{user_two.nickname}")
3067 |> get("/api/v1/accounts/#{user_two.id}")
3069 acc_one = json_response(resp_one, 200)
3070 acc_two = json_response(resp_two, 200)
3071 acc_three = json_response(resp_three, 200)
3072 refute acc_one == acc_two
3073 assert acc_two == acc_three
3076 describe "custom emoji" do
3077 test "with tags", %{conn: conn} do
3080 |> get("/api/v1/custom_emojis")
3081 |> json_response(200)
3083 assert Map.has_key?(emoji, "shortcode")
3084 assert Map.has_key?(emoji, "static_url")
3085 assert Map.has_key?(emoji, "tags")
3086 assert is_list(emoji["tags"])
3087 assert Map.has_key?(emoji, "url")
3088 assert Map.has_key?(emoji, "visible_in_picker")
3092 describe "index/2 redirections" do
3093 setup %{conn: conn} do
3097 signing_salt: "cooldude"
3102 |> Plug.Session.call(Plug.Session.init(session_opts))
3105 test_path = "/web/statuses/test"
3106 %{conn: conn, path: test_path}
3109 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3110 conn = get(conn, path)
3112 assert conn.status == 302
3113 assert redirected_to(conn) == "/web/login"
3116 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3117 token = insert(:oauth_token)
3121 |> assign(:user, token.user)
3122 |> put_session(:oauth_token, token.token)
3125 assert conn.status == 200
3128 test "saves referer path to session", %{conn: conn, path: path} do
3129 conn = get(conn, path)
3130 return_to = Plug.Conn.get_session(conn, :return_to)
3132 assert return_to == path
3135 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3136 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3137 auth = insert(:oauth_authorization, app: app)
3141 |> put_session(:return_to, path)
3142 |> get("/web/login", %{code: auth.token})
3144 assert conn.status == 302
3145 assert redirected_to(conn) == path
3148 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3149 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3150 auth = insert(:oauth_authorization, app: app)
3152 conn = get(conn, "/web/login", %{code: auth.token})
3154 assert conn.status == 302
3155 assert redirected_to(conn) == "/web/getting-started"
3159 describe "scheduled activities" do
3160 test "creates a scheduled activity", %{conn: conn} do
3161 user = insert(:user)
3162 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3166 |> assign(:user, user)
3167 |> post("/api/v1/statuses", %{
3168 "status" => "scheduled",
3169 "scheduled_at" => scheduled_at
3172 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3173 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3174 assert [] == Repo.all(Activity)
3177 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3178 user = insert(:user)
3179 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3181 file = %Plug.Upload{
3182 content_type: "image/jpg",
3183 path: Path.absname("test/fixtures/image.jpg"),
3184 filename: "an_image.jpg"
3187 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3191 |> assign(:user, user)
3192 |> post("/api/v1/statuses", %{
3193 "media_ids" => [to_string(upload.id)],
3194 "status" => "scheduled",
3195 "scheduled_at" => scheduled_at
3198 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3199 assert %{"type" => "image"} = media_attachment
3202 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3204 user = insert(:user)
3207 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3211 |> assign(:user, user)
3212 |> post("/api/v1/statuses", %{
3213 "status" => "not scheduled",
3214 "scheduled_at" => scheduled_at
3217 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3218 assert [] == Repo.all(ScheduledActivity)
3221 test "returns error when daily user limit is exceeded", %{conn: conn} do
3222 user = insert(:user)
3225 NaiveDateTime.utc_now()
3226 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3227 |> NaiveDateTime.to_iso8601()
3229 attrs = %{params: %{}, scheduled_at: today}
3230 {:ok, _} = ScheduledActivity.create(user, attrs)
3231 {:ok, _} = ScheduledActivity.create(user, attrs)
3235 |> assign(:user, user)
3236 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3238 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3241 test "returns error when total user limit is exceeded", %{conn: conn} do
3242 user = insert(:user)
3245 NaiveDateTime.utc_now()
3246 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3247 |> NaiveDateTime.to_iso8601()
3250 NaiveDateTime.utc_now()
3251 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3252 |> NaiveDateTime.to_iso8601()
3254 attrs = %{params: %{}, scheduled_at: today}
3255 {:ok, _} = ScheduledActivity.create(user, attrs)
3256 {:ok, _} = ScheduledActivity.create(user, attrs)
3257 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3261 |> assign(:user, user)
3262 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3264 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3267 test "shows scheduled activities", %{conn: conn} do
3268 user = insert(:user)
3269 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3270 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3271 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3272 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3276 |> assign(:user, user)
3281 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3283 result = json_response(conn_res, 200)
3284 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3289 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3291 result = json_response(conn_res, 200)
3292 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3297 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3299 result = json_response(conn_res, 200)
3300 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3303 test "shows a scheduled activity", %{conn: conn} do
3304 user = insert(:user)
3305 scheduled_activity = insert(:scheduled_activity, user: user)
3309 |> assign(:user, user)
3310 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3312 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3313 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3317 |> assign(:user, user)
3318 |> get("/api/v1/scheduled_statuses/404")
3320 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3323 test "updates a scheduled activity", %{conn: conn} do
3324 user = insert(:user)
3325 scheduled_activity = insert(:scheduled_activity, user: user)
3328 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3332 |> assign(:user, user)
3333 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3334 scheduled_at: new_scheduled_at
3337 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3338 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3342 |> assign(:user, user)
3343 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3345 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3348 test "deletes a scheduled activity", %{conn: conn} do
3349 user = insert(:user)
3350 scheduled_activity = insert(:scheduled_activity, user: user)
3354 |> assign(:user, user)
3355 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3357 assert %{} = json_response(res_conn, 200)
3358 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3362 |> assign(:user, user)
3363 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3365 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3369 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3370 user1 = insert(:user)
3371 user2 = insert(:user)
3372 user3 = insert(:user)
3374 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3376 # Reply to status from another user
3379 |> assign(:user, user2)
3380 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3382 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3384 activity = Activity.get_by_id_with_object(id)
3386 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3387 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3389 # Reblog from the third user
3392 |> assign(:user, user3)
3393 |> post("/api/v1/statuses/#{activity.id}/reblog")
3395 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3396 json_response(conn2, 200)
3398 assert to_string(activity.id) == id
3400 # Getting third user status
3403 |> assign(:user, user3)
3404 |> get("api/v1/timelines/home")
3406 [reblogged_activity] = json_response(conn3, 200)
3408 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3410 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3411 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3414 describe "create account by app" do
3416 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3417 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3418 interval = Pleroma.Config.get([:app_account_creation, :interval])
3420 Pleroma.Config.put([:app_account_creation, :enabled], true)
3421 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3422 Pleroma.Config.put([:app_account_creation, :interval], 1)
3425 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3426 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3427 Pleroma.Config.put([:app_account_creation, :interval], interval)
3433 test "Account registration via Application", %{conn: conn} do
3436 |> post("/api/v1/apps", %{
3437 client_name: "client_name",
3438 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3439 scopes: "read, write, follow"
3443 "client_id" => client_id,
3444 "client_secret" => client_secret,
3446 "name" => "client_name",
3447 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3450 } = json_response(conn, 200)
3454 |> post("/oauth/token", %{
3455 grant_type: "client_credentials",
3456 client_id: client_id,
3457 client_secret: client_secret
3460 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3461 json_response(conn, 200)
3464 token_from_db = Repo.get_by(Token, token: token)
3465 assert token_from_db
3467 assert scope == "read write follow"
3471 |> put_req_header("authorization", "Bearer " <> token)
3472 |> post("/api/v1/accounts", %{
3474 email: "lain@example.org",
3475 password: "PlzDontHackLain",
3480 "access_token" => token,
3481 "created_at" => _created_at,
3483 "token_type" => "Bearer"
3484 } = json_response(conn, 200)
3486 token_from_db = Repo.get_by(Token, token: token)
3487 assert token_from_db
3488 token_from_db = Repo.preload(token_from_db, :user)
3489 assert token_from_db.user
3491 assert token_from_db.user.info.confirmation_pending
3494 test "rate limit", %{conn: conn} do
3495 app_token = insert(:oauth_token, user: nil)
3498 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3499 |> Map.put(:remote_ip, {15, 15, 15, 15})
3504 |> post("/api/v1/accounts", %{
3505 username: "#{i}lain",
3506 email: "#{i}lain@example.org",
3507 password: "PlzDontHackLain",
3512 "access_token" => token,
3513 "created_at" => _created_at,
3515 "token_type" => "Bearer"
3516 } = json_response(conn, 200)
3518 token_from_db = Repo.get_by(Token, token: token)
3519 assert token_from_db
3520 token_from_db = Repo.preload(token_from_db, :user)
3521 assert token_from_db.user
3523 assert token_from_db.user.info.confirmation_pending
3528 |> post("/api/v1/accounts", %{
3530 email: "6lain@example.org",
3531 password: "PlzDontHackLain",
3535 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}
3539 describe "GET /api/v1/polls/:id" do
3540 test "returns poll entity for object id", %{conn: conn} do
3541 user = insert(:user)
3544 CommonAPI.post(user, %{
3545 "status" => "Pleroma does",
3546 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3549 object = Object.normalize(activity)
3553 |> assign(:user, user)
3554 |> get("/api/v1/polls/#{object.id}")
3556 response = json_response(conn, 200)
3558 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3561 test "does not expose polls for private statuses", %{conn: conn} do
3562 user = insert(:user)
3563 other_user = insert(:user)
3566 CommonAPI.post(user, %{
3567 "status" => "Pleroma does",
3568 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3569 "visibility" => "private"
3572 object = Object.normalize(activity)
3576 |> assign(:user, other_user)
3577 |> get("/api/v1/polls/#{object.id}")
3579 assert json_response(conn, 404)
3583 describe "POST /api/v1/polls/:id/votes" do
3584 test "votes are added to the poll", %{conn: conn} do
3585 user = insert(:user)
3586 other_user = insert(:user)
3589 CommonAPI.post(user, %{
3590 "status" => "A very delicious sandwich",
3592 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3598 object = Object.normalize(activity)
3602 |> assign(:user, other_user)
3603 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3605 assert json_response(conn, 200)
3606 object = Object.get_by_id(object.id)
3608 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3613 test "author can't vote", %{conn: conn} do
3614 user = insert(:user)
3617 CommonAPI.post(user, %{
3618 "status" => "Am I cute?",
3619 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3622 object = Object.normalize(activity)
3625 |> assign(:user, user)
3626 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3627 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3629 object = Object.get_by_id(object.id)
3631 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3634 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3635 user = insert(:user)
3636 other_user = insert(:user)
3639 CommonAPI.post(user, %{
3640 "status" => "The glass is",
3641 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3644 object = Object.normalize(activity)
3647 |> assign(:user, other_user)
3648 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3649 |> json_response(422) == %{"error" => "Too many choices"}
3651 object = Object.get_by_id(object.id)
3653 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->