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)
417 user_three = insert(:user)
419 {:ok, user_two} = User.follow(user_two, user_one)
422 CommonAPI.post(user_one, %{
423 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
424 "visibility" => "direct"
427 {:ok, _follower_only} =
428 CommonAPI.post(user_one, %{
429 "status" => "Hi @#{user_two.nickname}!",
430 "visibility" => "private"
435 |> assign(:user, user_one)
436 |> get("/api/v1/conversations")
438 assert response = json_response(res_conn, 200)
443 "accounts" => res_accounts,
444 "last_status" => res_last_status,
449 account_ids = Enum.map(res_accounts, & &1["id"])
450 assert length(res_accounts) == 2
451 assert user_two.id in account_ids
452 assert user_three.id in account_ids
453 assert is_binary(res_id)
454 assert unread == true
455 assert res_last_status["id"] == direct.id
457 # Apparently undocumented API endpoint
460 |> assign(:user, user_one)
461 |> post("/api/v1/conversations/#{res_id}/read")
463 assert response = json_response(res_conn, 200)
464 assert length(response["accounts"]) == 2
465 assert response["last_status"]["id"] == direct.id
466 assert response["unread"] == false
468 # (vanilla) Mastodon frontend behaviour
471 |> assign(:user, user_one)
472 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
474 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
477 test "doesn't include DMs from blocked users", %{conn: conn} do
478 blocker = insert(:user)
479 blocked = insert(:user)
481 {:ok, blocker} = User.block(blocker, blocked)
483 {:ok, _blocked_direct} =
484 CommonAPI.post(blocked, %{
485 "status" => "Hi @#{blocker.nickname}!",
486 "visibility" => "direct"
490 CommonAPI.post(user, %{
491 "status" => "Hi @#{blocker.nickname}!",
492 "visibility" => "direct"
497 |> assign(:user, user)
498 |> get("api/v1/timelines/direct")
500 [status] = json_response(res_conn, 200)
501 assert status["id"] == direct.id
504 test "replying to a status", %{conn: conn} do
507 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
511 |> assign(:user, user)
512 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
514 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
516 activity = Activity.get_by_id(id)
518 assert activity.data["context"] == replied_to.data["context"]
519 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
522 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
527 |> assign(:user, user)
528 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
530 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
532 activity = Activity.get_by_id(id)
537 test "verify_credentials", %{conn: conn} do
542 |> assign(:user, user)
543 |> get("/api/v1/accounts/verify_credentials")
545 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
546 assert id == to_string(user.id)
549 test "verify_credentials default scope unlisted", %{conn: conn} do
550 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
554 |> assign(:user, user)
555 |> get("/api/v1/accounts/verify_credentials")
557 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
558 assert id == to_string(user.id)
561 test "apps/verify_credentials", %{conn: conn} do
562 token = insert(:oauth_token)
566 |> assign(:user, token.user)
567 |> assign(:token, token)
568 |> get("/api/v1/apps/verify_credentials")
570 app = Repo.preload(token, :app).app
573 "name" => app.client_name,
574 "website" => app.website,
575 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
578 assert expected == json_response(conn, 200)
581 test "creates an oauth app", %{conn: conn} do
583 app_attrs = build(:oauth_app)
587 |> assign(:user, user)
588 |> post("/api/v1/apps", %{
589 client_name: app_attrs.client_name,
590 redirect_uris: app_attrs.redirect_uris
593 [app] = Repo.all(App)
596 "name" => app.client_name,
597 "website" => app.website,
598 "client_id" => app.client_id,
599 "client_secret" => app.client_secret,
600 "id" => app.id |> to_string(),
601 "redirect_uri" => app.redirect_uris,
602 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
605 assert expected == json_response(conn, 200)
608 test "get a status", %{conn: conn} do
609 activity = insert(:note_activity)
613 |> get("/api/v1/statuses/#{activity.id}")
615 assert %{"id" => id} = json_response(conn, 200)
616 assert id == to_string(activity.id)
619 describe "deleting a status" do
620 test "when you created it", %{conn: conn} do
621 activity = insert(:note_activity)
622 author = User.get_cached_by_ap_id(activity.data["actor"])
626 |> assign(:user, author)
627 |> delete("/api/v1/statuses/#{activity.id}")
629 assert %{} = json_response(conn, 200)
631 refute Activity.get_by_id(activity.id)
634 test "when you didn't create it", %{conn: conn} do
635 activity = insert(:note_activity)
640 |> assign(:user, user)
641 |> delete("/api/v1/statuses/#{activity.id}")
643 assert %{"error" => _} = json_response(conn, 403)
645 assert Activity.get_by_id(activity.id) == activity
648 test "when you're an admin or moderator", %{conn: conn} do
649 activity1 = insert(:note_activity)
650 activity2 = insert(:note_activity)
651 admin = insert(:user, info: %{is_admin: true})
652 moderator = insert(:user, info: %{is_moderator: true})
656 |> assign(:user, admin)
657 |> delete("/api/v1/statuses/#{activity1.id}")
659 assert %{} = json_response(res_conn, 200)
663 |> assign(:user, moderator)
664 |> delete("/api/v1/statuses/#{activity2.id}")
666 assert %{} = json_response(res_conn, 200)
668 refute Activity.get_by_id(activity1.id)
669 refute Activity.get_by_id(activity2.id)
673 describe "filters" do
674 test "creating a filter", %{conn: conn} do
677 filter = %Pleroma.Filter{
684 |> assign(:user, user)
685 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
687 assert response = json_response(conn, 200)
688 assert response["phrase"] == filter.phrase
689 assert response["context"] == filter.context
690 assert response["irreversible"] == false
691 assert response["id"] != nil
692 assert response["id"] != ""
695 test "fetching a list of filters", %{conn: conn} do
698 query_one = %Pleroma.Filter{
705 query_two = %Pleroma.Filter{
712 {:ok, filter_one} = Pleroma.Filter.create(query_one)
713 {:ok, filter_two} = Pleroma.Filter.create(query_two)
717 |> assign(:user, user)
718 |> get("/api/v1/filters")
719 |> json_response(200)
725 filters: [filter_two, filter_one]
729 test "get a filter", %{conn: conn} do
732 query = %Pleroma.Filter{
739 {:ok, filter} = Pleroma.Filter.create(query)
743 |> assign(:user, user)
744 |> get("/api/v1/filters/#{filter.filter_id}")
746 assert _response = json_response(conn, 200)
749 test "update a filter", %{conn: conn} do
752 query = %Pleroma.Filter{
759 {:ok, _filter} = Pleroma.Filter.create(query)
761 new = %Pleroma.Filter{
768 |> assign(:user, user)
769 |> put("/api/v1/filters/#{query.filter_id}", %{
774 assert response = json_response(conn, 200)
775 assert response["phrase"] == new.phrase
776 assert response["context"] == new.context
779 test "delete a filter", %{conn: conn} do
782 query = %Pleroma.Filter{
789 {:ok, filter} = Pleroma.Filter.create(query)
793 |> assign(:user, user)
794 |> delete("/api/v1/filters/#{filter.filter_id}")
796 assert response = json_response(conn, 200)
797 assert response == %{}
802 test "creating a list", %{conn: conn} do
807 |> assign(:user, user)
808 |> post("/api/v1/lists", %{"title" => "cuties"})
810 assert %{"title" => title} = json_response(conn, 200)
811 assert title == "cuties"
814 test "adding users to a list", %{conn: conn} do
816 other_user = insert(:user)
817 {:ok, list} = Pleroma.List.create("name", user)
821 |> assign(:user, user)
822 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
824 assert %{} == json_response(conn, 200)
825 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
826 assert following == [other_user.follower_address]
829 test "removing users from a list", %{conn: conn} do
831 other_user = insert(:user)
832 third_user = insert(:user)
833 {:ok, list} = Pleroma.List.create("name", user)
834 {:ok, list} = Pleroma.List.follow(list, other_user)
835 {:ok, list} = Pleroma.List.follow(list, third_user)
839 |> assign(:user, user)
840 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
842 assert %{} == json_response(conn, 200)
843 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
844 assert following == [third_user.follower_address]
847 test "listing users in a list", %{conn: conn} do
849 other_user = insert(:user)
850 {:ok, list} = Pleroma.List.create("name", user)
851 {:ok, list} = Pleroma.List.follow(list, other_user)
855 |> assign(:user, user)
856 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
858 assert [%{"id" => id}] = json_response(conn, 200)
859 assert id == to_string(other_user.id)
862 test "retrieving a list", %{conn: conn} do
864 {:ok, list} = Pleroma.List.create("name", user)
868 |> assign(:user, user)
869 |> get("/api/v1/lists/#{list.id}")
871 assert %{"id" => id} = json_response(conn, 200)
872 assert id == to_string(list.id)
875 test "renaming a list", %{conn: conn} do
877 {:ok, list} = Pleroma.List.create("name", user)
881 |> assign(:user, user)
882 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
884 assert %{"title" => name} = json_response(conn, 200)
885 assert name == "newname"
888 test "deleting a list", %{conn: conn} do
890 {:ok, list} = Pleroma.List.create("name", user)
894 |> assign(:user, user)
895 |> delete("/api/v1/lists/#{list.id}")
897 assert %{} = json_response(conn, 200)
898 assert is_nil(Repo.get(Pleroma.List, list.id))
901 test "list timeline", %{conn: conn} do
903 other_user = insert(:user)
904 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
905 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
906 {:ok, list} = Pleroma.List.create("name", user)
907 {:ok, list} = Pleroma.List.follow(list, other_user)
911 |> assign(:user, user)
912 |> get("/api/v1/timelines/list/#{list.id}")
914 assert [%{"id" => id}] = json_response(conn, 200)
916 assert id == to_string(activity_two.id)
919 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
921 other_user = insert(:user)
922 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
924 {:ok, _activity_two} =
925 TwitterAPI.create_status(other_user, %{
926 "status" => "Marisa is cute.",
927 "visibility" => "private"
930 {:ok, list} = Pleroma.List.create("name", user)
931 {:ok, list} = Pleroma.List.follow(list, other_user)
935 |> assign(:user, user)
936 |> get("/api/v1/timelines/list/#{list.id}")
938 assert [%{"id" => id}] = json_response(conn, 200)
940 assert id == to_string(activity_one.id)
944 describe "notifications" do
945 test "list of notifications", %{conn: conn} do
947 other_user = insert(:user)
950 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
952 {:ok, [_notification]} = Notification.create_notifications(activity)
956 |> assign(:user, user)
957 |> get("/api/v1/notifications")
960 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
962 }\">@<span>#{user.nickname}</span></a></span>"
964 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
965 assert response == expected_response
968 test "getting a single notification", %{conn: conn} do
970 other_user = insert(:user)
973 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
975 {:ok, [notification]} = Notification.create_notifications(activity)
979 |> assign(:user, user)
980 |> get("/api/v1/notifications/#{notification.id}")
983 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
985 }\">@<span>#{user.nickname}</span></a></span>"
987 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
988 assert response == expected_response
991 test "dismissing a single notification", %{conn: conn} do
993 other_user = insert(:user)
996 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
998 {:ok, [notification]} = Notification.create_notifications(activity)
1002 |> assign(:user, user)
1003 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1005 assert %{} = json_response(conn, 200)
1008 test "clearing all notifications", %{conn: conn} do
1009 user = insert(:user)
1010 other_user = insert(:user)
1013 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
1015 {:ok, [_notification]} = Notification.create_notifications(activity)
1019 |> assign(:user, user)
1020 |> post("/api/v1/notifications/clear")
1022 assert %{} = json_response(conn, 200)
1026 |> assign(:user, user)
1027 |> get("/api/v1/notifications")
1029 assert all = json_response(conn, 200)
1033 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1034 user = insert(:user)
1035 other_user = insert(:user)
1037 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1038 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1039 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1040 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1042 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1043 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1044 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1045 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1049 |> assign(:user, user)
1054 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1056 result = json_response(conn_res, 200)
1057 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1062 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1064 result = json_response(conn_res, 200)
1065 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1070 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1072 result = json_response(conn_res, 200)
1073 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1076 test "filters notifications using exclude_types", %{conn: conn} do
1077 user = insert(:user)
1078 other_user = insert(:user)
1080 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1081 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1082 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1083 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1084 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1086 mention_notification_id =
1087 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1089 favorite_notification_id =
1090 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1092 reblog_notification_id =
1093 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1095 follow_notification_id =
1096 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1100 |> assign(:user, user)
1103 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1105 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1108 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1110 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1113 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1115 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1118 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1120 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1123 test "destroy multiple", %{conn: conn} do
1124 user = insert(:user)
1125 other_user = insert(:user)
1127 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1128 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1129 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1130 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1132 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1133 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1134 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1135 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1139 |> assign(:user, user)
1143 |> get("/api/v1/notifications")
1145 result = json_response(conn_res, 200)
1146 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1150 |> assign(:user, other_user)
1154 |> get("/api/v1/notifications")
1156 result = json_response(conn_res, 200)
1157 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1161 |> delete("/api/v1/notifications/destroy_multiple", %{
1162 "ids" => [notification1_id, notification2_id]
1165 assert json_response(conn_destroy, 200) == %{}
1169 |> get("/api/v1/notifications")
1171 result = json_response(conn_res, 200)
1172 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1176 describe "reblogging" do
1177 test "reblogs and returns the reblogged status", %{conn: conn} do
1178 activity = insert(:note_activity)
1179 user = insert(:user)
1183 |> assign(:user, user)
1184 |> post("/api/v1/statuses/#{activity.id}/reblog")
1187 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1189 } = json_response(conn, 200)
1191 assert to_string(activity.id) == id
1194 test "reblogged status for another user", %{conn: conn} do
1195 activity = insert(:note_activity)
1196 user1 = insert(:user)
1197 user2 = insert(:user)
1198 user3 = insert(:user)
1199 CommonAPI.favorite(activity.id, user2)
1200 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1201 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1202 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1206 |> assign(:user, user3)
1207 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1210 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1211 "reblogged" => false,
1212 "favourited" => false,
1213 "bookmarked" => false
1214 } = json_response(conn_res, 200)
1218 |> assign(:user, user2)
1219 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1222 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1223 "reblogged" => true,
1224 "favourited" => true,
1225 "bookmarked" => true
1226 } = json_response(conn_res, 200)
1228 assert to_string(activity.id) == id
1232 describe "unreblogging" do
1233 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1234 activity = insert(:note_activity)
1235 user = insert(:user)
1237 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1241 |> assign(:user, user)
1242 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1244 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1246 assert to_string(activity.id) == id
1250 describe "favoriting" do
1251 test "favs a status and returns it", %{conn: conn} do
1252 activity = insert(:note_activity)
1253 user = insert(:user)
1257 |> assign(:user, user)
1258 |> post("/api/v1/statuses/#{activity.id}/favourite")
1260 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1261 json_response(conn, 200)
1263 assert to_string(activity.id) == id
1266 test "returns 500 for a wrong id", %{conn: conn} do
1267 user = insert(:user)
1271 |> assign(:user, user)
1272 |> post("/api/v1/statuses/1/favourite")
1273 |> json_response(500)
1275 assert resp == "Something went wrong"
1279 describe "unfavoriting" do
1280 test "unfavorites a status and returns it", %{conn: conn} do
1281 activity = insert(:note_activity)
1282 user = insert(:user)
1284 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1288 |> assign(:user, user)
1289 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1291 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1292 json_response(conn, 200)
1294 assert to_string(activity.id) == id
1298 describe "user timelines" do
1299 test "gets a users statuses", %{conn: conn} do
1300 user_one = insert(:user)
1301 user_two = insert(:user)
1302 user_three = insert(:user)
1304 {:ok, user_three} = User.follow(user_three, user_one)
1306 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1308 {:ok, direct_activity} =
1309 CommonAPI.post(user_one, %{
1310 "status" => "Hi, @#{user_two.nickname}.",
1311 "visibility" => "direct"
1314 {:ok, private_activity} =
1315 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1319 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1321 assert [%{"id" => id}] = json_response(resp, 200)
1322 assert id == to_string(activity.id)
1326 |> assign(:user, user_two)
1327 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1329 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1330 assert id_one == to_string(direct_activity.id)
1331 assert id_two == to_string(activity.id)
1335 |> assign(:user, user_three)
1336 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1338 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1339 assert id_one == to_string(private_activity.id)
1340 assert id_two == to_string(activity.id)
1343 test "unimplemented pinned statuses feature", %{conn: conn} do
1344 note = insert(:note_activity)
1345 user = User.get_cached_by_ap_id(note.data["actor"])
1349 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1351 assert json_response(conn, 200) == []
1354 test "gets an users media", %{conn: conn} do
1355 note = insert(:note_activity)
1356 user = User.get_cached_by_ap_id(note.data["actor"])
1358 file = %Plug.Upload{
1359 content_type: "image/jpg",
1360 path: Path.absname("test/fixtures/image.jpg"),
1361 filename: "an_image.jpg"
1365 TwitterAPI.upload(file, user, "json")
1369 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1373 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1375 assert [%{"id" => id}] = json_response(conn, 200)
1376 assert id == to_string(image_post.id)
1380 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1382 assert [%{"id" => id}] = json_response(conn, 200)
1383 assert id == to_string(image_post.id)
1386 test "gets a user's statuses without reblogs", %{conn: conn} do
1387 user = insert(:user)
1388 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1389 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1393 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1395 assert [%{"id" => id}] = json_response(conn, 200)
1396 assert id == to_string(post.id)
1400 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1402 assert [%{"id" => id}] = json_response(conn, 200)
1403 assert id == to_string(post.id)
1407 describe "user relationships" do
1408 test "returns the relationships for the current user", %{conn: conn} do
1409 user = insert(:user)
1410 other_user = insert(:user)
1411 {:ok, user} = User.follow(user, other_user)
1415 |> assign(:user, user)
1416 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1418 assert [relationship] = json_response(conn, 200)
1420 assert to_string(other_user.id) == relationship["id"]
1424 describe "locked accounts" do
1425 test "/api/v1/follow_requests works" do
1426 user = insert(:user, %{info: %User.Info{locked: true}})
1427 other_user = insert(:user)
1429 {:ok, _activity} = ActivityPub.follow(other_user, user)
1431 user = User.get_cached_by_id(user.id)
1432 other_user = User.get_cached_by_id(other_user.id)
1434 assert User.following?(other_user, user) == false
1438 |> assign(:user, user)
1439 |> get("/api/v1/follow_requests")
1441 assert [relationship] = json_response(conn, 200)
1442 assert to_string(other_user.id) == relationship["id"]
1445 test "/api/v1/follow_requests/:id/authorize works" do
1446 user = insert(:user, %{info: %User.Info{locked: true}})
1447 other_user = insert(:user)
1449 {:ok, _activity} = ActivityPub.follow(other_user, user)
1451 user = User.get_cached_by_id(user.id)
1452 other_user = User.get_cached_by_id(other_user.id)
1454 assert User.following?(other_user, user) == false
1458 |> assign(:user, user)
1459 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1461 assert relationship = json_response(conn, 200)
1462 assert to_string(other_user.id) == relationship["id"]
1464 user = User.get_cached_by_id(user.id)
1465 other_user = User.get_cached_by_id(other_user.id)
1467 assert User.following?(other_user, user) == true
1470 test "verify_credentials", %{conn: conn} do
1471 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1475 |> assign(:user, user)
1476 |> get("/api/v1/accounts/verify_credentials")
1478 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1479 assert id == to_string(user.id)
1482 test "/api/v1/follow_requests/:id/reject works" do
1483 user = insert(:user, %{info: %User.Info{locked: true}})
1484 other_user = insert(:user)
1486 {:ok, _activity} = ActivityPub.follow(other_user, user)
1488 user = User.get_cached_by_id(user.id)
1492 |> assign(:user, user)
1493 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1495 assert relationship = json_response(conn, 200)
1496 assert to_string(other_user.id) == relationship["id"]
1498 user = User.get_cached_by_id(user.id)
1499 other_user = User.get_cached_by_id(other_user.id)
1501 assert User.following?(other_user, user) == false
1505 test "account fetching", %{conn: conn} do
1506 user = insert(:user)
1510 |> get("/api/v1/accounts/#{user.id}")
1512 assert %{"id" => id} = json_response(conn, 200)
1513 assert id == to_string(user.id)
1517 |> get("/api/v1/accounts/-1")
1519 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1522 test "account fetching also works nickname", %{conn: conn} do
1523 user = insert(:user)
1527 |> get("/api/v1/accounts/#{user.nickname}")
1529 assert %{"id" => id} = json_response(conn, 200)
1530 assert id == user.id
1533 test "media upload", %{conn: conn} do
1534 file = %Plug.Upload{
1535 content_type: "image/jpg",
1536 path: Path.absname("test/fixtures/image.jpg"),
1537 filename: "an_image.jpg"
1540 desc = "Description of the image"
1542 user = insert(:user)
1546 |> assign(:user, user)
1547 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1549 assert media = json_response(conn, 200)
1551 assert media["type"] == "image"
1552 assert media["description"] == desc
1555 object = Repo.get(Object, media["id"])
1556 assert object.data["actor"] == User.ap_id(user)
1559 test "mascot upload", %{conn: conn} do
1560 user = insert(:user)
1562 non_image_file = %Plug.Upload{
1563 content_type: "audio/mpeg",
1564 path: Path.absname("test/fixtures/sound.mp3"),
1565 filename: "sound.mp3"
1570 |> assign(:user, user)
1571 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1573 assert json_response(conn, 415)
1575 file = %Plug.Upload{
1576 content_type: "image/jpg",
1577 path: Path.absname("test/fixtures/image.jpg"),
1578 filename: "an_image.jpg"
1583 |> assign(:user, user)
1584 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1586 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1589 test "mascot retrieving", %{conn: conn} do
1590 user = insert(:user)
1591 # When user hasn't set a mascot, we should just get pleroma tan back
1594 |> assign(:user, user)
1595 |> get("/api/v1/pleroma/mascot")
1597 assert %{"url" => url} = json_response(conn, 200)
1598 assert url =~ "pleroma-fox-tan-smol"
1600 # When a user sets their mascot, we should get that back
1601 file = %Plug.Upload{
1602 content_type: "image/jpg",
1603 path: Path.absname("test/fixtures/image.jpg"),
1604 filename: "an_image.jpg"
1609 |> assign(:user, user)
1610 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1612 assert json_response(conn, 200)
1614 user = User.get_cached_by_id(user.id)
1618 |> assign(:user, user)
1619 |> get("/api/v1/pleroma/mascot")
1621 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1622 assert url =~ "an_image"
1625 test "hashtag timeline", %{conn: conn} do
1626 following = insert(:user)
1629 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1631 {:ok, [_activity]} =
1632 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1636 |> get("/api/v1/timelines/tag/2hu")
1638 assert [%{"id" => id}] = json_response(nconn, 200)
1640 assert id == to_string(activity.id)
1642 # works for different capitalization too
1645 |> get("/api/v1/timelines/tag/2HU")
1647 assert [%{"id" => id}] = json_response(nconn, 200)
1649 assert id == to_string(activity.id)
1653 test "multi-hashtag timeline", %{conn: conn} do
1654 user = insert(:user)
1656 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1657 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1658 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1662 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1664 [status_none, status_test1, status_test] = json_response(any_test, 200)
1666 assert to_string(activity_test.id) == status_test["id"]
1667 assert to_string(activity_test1.id) == status_test1["id"]
1668 assert to_string(activity_none.id) == status_none["id"]
1672 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1674 assert [status_test1] == json_response(restricted_test, 200)
1676 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1678 assert [status_none] == json_response(all_test, 200)
1681 test "getting followers", %{conn: conn} do
1682 user = insert(:user)
1683 other_user = insert(:user)
1684 {:ok, user} = User.follow(user, other_user)
1688 |> get("/api/v1/accounts/#{other_user.id}/followers")
1690 assert [%{"id" => id}] = json_response(conn, 200)
1691 assert id == to_string(user.id)
1694 test "getting followers, hide_followers", %{conn: conn} do
1695 user = insert(:user)
1696 other_user = insert(:user, %{info: %{hide_followers: true}})
1697 {:ok, _user} = User.follow(user, other_user)
1701 |> get("/api/v1/accounts/#{other_user.id}/followers")
1703 assert [] == json_response(conn, 200)
1706 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1707 user = insert(:user)
1708 other_user = insert(:user, %{info: %{hide_followers: true}})
1709 {:ok, _user} = User.follow(user, other_user)
1713 |> assign(:user, other_user)
1714 |> get("/api/v1/accounts/#{other_user.id}/followers")
1716 refute [] == json_response(conn, 200)
1719 test "getting followers, pagination", %{conn: conn} do
1720 user = insert(:user)
1721 follower1 = insert(:user)
1722 follower2 = insert(:user)
1723 follower3 = insert(:user)
1724 {:ok, _} = User.follow(follower1, user)
1725 {:ok, _} = User.follow(follower2, user)
1726 {:ok, _} = User.follow(follower3, user)
1730 |> assign(:user, user)
1734 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1736 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1737 assert id3 == follower3.id
1738 assert id2 == follower2.id
1742 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1744 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1745 assert id2 == follower2.id
1746 assert id1 == follower1.id
1750 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1752 assert [%{"id" => id2}] = json_response(res_conn, 200)
1753 assert id2 == follower2.id
1755 assert [link_header] = get_resp_header(res_conn, "link")
1756 assert link_header =~ ~r/min_id=#{follower2.id}/
1757 assert link_header =~ ~r/max_id=#{follower2.id}/
1760 test "getting following", %{conn: conn} do
1761 user = insert(:user)
1762 other_user = insert(:user)
1763 {:ok, user} = User.follow(user, other_user)
1767 |> get("/api/v1/accounts/#{user.id}/following")
1769 assert [%{"id" => id}] = json_response(conn, 200)
1770 assert id == to_string(other_user.id)
1773 test "getting following, hide_follows", %{conn: conn} do
1774 user = insert(:user, %{info: %{hide_follows: true}})
1775 other_user = insert(:user)
1776 {:ok, user} = User.follow(user, other_user)
1780 |> get("/api/v1/accounts/#{user.id}/following")
1782 assert [] == json_response(conn, 200)
1785 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1786 user = insert(:user, %{info: %{hide_follows: true}})
1787 other_user = insert(:user)
1788 {:ok, user} = User.follow(user, other_user)
1792 |> assign(:user, user)
1793 |> get("/api/v1/accounts/#{user.id}/following")
1795 refute [] == json_response(conn, 200)
1798 test "getting following, pagination", %{conn: conn} do
1799 user = insert(:user)
1800 following1 = insert(:user)
1801 following2 = insert(:user)
1802 following3 = insert(:user)
1803 {:ok, _} = User.follow(user, following1)
1804 {:ok, _} = User.follow(user, following2)
1805 {:ok, _} = User.follow(user, following3)
1809 |> assign(:user, user)
1813 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1815 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1816 assert id3 == following3.id
1817 assert id2 == following2.id
1821 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1823 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1824 assert id2 == following2.id
1825 assert id1 == following1.id
1829 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1831 assert [%{"id" => id2}] = json_response(res_conn, 200)
1832 assert id2 == following2.id
1834 assert [link_header] = get_resp_header(res_conn, "link")
1835 assert link_header =~ ~r/min_id=#{following2.id}/
1836 assert link_header =~ ~r/max_id=#{following2.id}/
1839 test "following / unfollowing a user", %{conn: conn} do
1840 user = insert(:user)
1841 other_user = insert(:user)
1845 |> assign(:user, user)
1846 |> post("/api/v1/accounts/#{other_user.id}/follow")
1848 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1850 user = User.get_cached_by_id(user.id)
1854 |> assign(:user, user)
1855 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1857 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1859 user = User.get_cached_by_id(user.id)
1863 |> assign(:user, user)
1864 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1866 assert %{"id" => id} = json_response(conn, 200)
1867 assert id == to_string(other_user.id)
1870 test "following without reblogs" do
1871 follower = insert(:user)
1872 followed = insert(:user)
1873 other_user = insert(:user)
1877 |> assign(:user, follower)
1878 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1880 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1882 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1883 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1887 |> assign(:user, User.get_cached_by_id(follower.id))
1888 |> get("/api/v1/timelines/home")
1890 assert [] == json_response(conn, 200)
1894 |> assign(:user, follower)
1895 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1897 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1901 |> assign(:user, User.get_cached_by_id(follower.id))
1902 |> get("/api/v1/timelines/home")
1904 expected_activity_id = reblog.id
1905 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1908 test "following / unfollowing errors" do
1909 user = insert(:user)
1913 |> assign(:user, user)
1916 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1917 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1920 user = User.get_cached_by_id(user.id)
1921 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1922 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1924 # self follow via uri
1925 user = User.get_cached_by_id(user.id)
1926 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1927 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1929 # follow non existing user
1930 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1931 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1933 # follow non existing user via uri
1934 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1935 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1937 # unfollow non existing user
1938 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1939 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1942 test "muting / unmuting a user", %{conn: conn} do
1943 user = insert(:user)
1944 other_user = insert(:user)
1948 |> assign(:user, user)
1949 |> post("/api/v1/accounts/#{other_user.id}/mute")
1951 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1953 user = User.get_cached_by_id(user.id)
1957 |> assign(:user, user)
1958 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1960 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1963 test "subscribing / unsubscribing to a user", %{conn: conn} do
1964 user = insert(:user)
1965 subscription_target = insert(:user)
1969 |> assign(:user, user)
1970 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1972 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1976 |> assign(:user, user)
1977 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1979 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1982 test "getting a list of mutes", %{conn: conn} do
1983 user = insert(:user)
1984 other_user = insert(:user)
1986 {:ok, user} = User.mute(user, other_user)
1990 |> assign(:user, user)
1991 |> get("/api/v1/mutes")
1993 other_user_id = to_string(other_user.id)
1994 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1997 test "blocking / unblocking a user", %{conn: conn} do
1998 user = insert(:user)
1999 other_user = insert(:user)
2003 |> assign(:user, user)
2004 |> post("/api/v1/accounts/#{other_user.id}/block")
2006 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2008 user = User.get_cached_by_id(user.id)
2012 |> assign(:user, user)
2013 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2015 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2018 test "getting a list of blocks", %{conn: conn} do
2019 user = insert(:user)
2020 other_user = insert(:user)
2022 {:ok, user} = User.block(user, other_user)
2026 |> assign(:user, user)
2027 |> get("/api/v1/blocks")
2029 other_user_id = to_string(other_user.id)
2030 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2033 test "blocking / unblocking a domain", %{conn: conn} do
2034 user = insert(:user)
2035 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2039 |> assign(:user, user)
2040 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2042 assert %{} = json_response(conn, 200)
2043 user = User.get_cached_by_ap_id(user.ap_id)
2044 assert User.blocks?(user, other_user)
2048 |> assign(:user, user)
2049 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2051 assert %{} = json_response(conn, 200)
2052 user = User.get_cached_by_ap_id(user.ap_id)
2053 refute User.blocks?(user, other_user)
2056 test "getting a list of domain blocks", %{conn: conn} do
2057 user = insert(:user)
2059 {:ok, user} = User.block_domain(user, "bad.site")
2060 {:ok, user} = User.block_domain(user, "even.worse.site")
2064 |> assign(:user, user)
2065 |> get("/api/v1/domain_blocks")
2067 domain_blocks = json_response(conn, 200)
2069 assert "bad.site" in domain_blocks
2070 assert "even.worse.site" in domain_blocks
2073 test "unimplemented follow_requests, blocks, domain blocks" do
2074 user = insert(:user)
2076 ["blocks", "domain_blocks", "follow_requests"]
2077 |> Enum.each(fn endpoint ->
2080 |> assign(:user, user)
2081 |> get("/api/v1/#{endpoint}")
2083 assert [] = json_response(conn, 200)
2087 test "account search", %{conn: conn} do
2088 user = insert(:user)
2089 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2090 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2094 |> assign(:user, user)
2095 |> get("/api/v1/accounts/search", %{"q" => "shp"})
2096 |> json_response(200)
2098 result_ids = for result <- results, do: result["acct"]
2100 assert user_two.nickname in result_ids
2101 assert user_three.nickname in result_ids
2105 |> assign(:user, user)
2106 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
2107 |> json_response(200)
2109 result_ids = for result <- results, do: result["acct"]
2111 assert user_three.nickname in result_ids
2114 test "search", %{conn: conn} do
2115 user = insert(:user)
2116 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2117 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2119 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
2122 CommonAPI.post(user, %{
2123 "status" => "This is about 2hu, but private",
2124 "visibility" => "private"
2127 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
2131 |> get("/api/v1/search", %{"q" => "2hu"})
2133 assert results = json_response(conn, 200)
2135 [account | _] = results["accounts"]
2136 assert account["id"] == to_string(user_three.id)
2138 assert results["hashtags"] == []
2140 [status] = results["statuses"]
2141 assert status["id"] == to_string(activity.id)
2144 test "search fetches remote statuses", %{conn: conn} do
2148 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
2150 assert results = json_response(conn, 200)
2152 [status] = results["statuses"]
2153 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
2157 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
2159 CommonAPI.post(insert(:user), %{
2160 "status" => "This is about 2hu, but private",
2161 "visibility" => "private"
2167 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2169 assert results = json_response(conn, 200)
2171 [] = results["statuses"]
2175 test "search fetches remote accounts", %{conn: conn} do
2176 user = insert(:user)
2180 |> assign(:user, user)
2181 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2183 assert results = json_response(conn, 200)
2184 [account] = results["accounts"]
2185 assert account["acct"] == "shp@social.heldscal.la"
2188 test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
2191 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
2193 assert results = json_response(conn, 200)
2194 assert [] == results["accounts"]
2197 test "returns the favorites of a user", %{conn: conn} do
2198 user = insert(:user)
2199 other_user = insert(:user)
2201 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2202 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2204 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2208 |> assign(:user, user)
2209 |> get("/api/v1/favourites")
2211 assert [status] = json_response(first_conn, 200)
2212 assert status["id"] == to_string(activity.id)
2214 assert [{"link", _link_header}] =
2215 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2217 # Honours query params
2218 {:ok, second_activity} =
2219 CommonAPI.post(other_user, %{
2221 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2224 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2226 last_like = status["id"]
2230 |> assign(:user, user)
2231 |> get("/api/v1/favourites?since_id=#{last_like}")
2233 assert [second_status] = json_response(second_conn, 200)
2234 assert second_status["id"] == to_string(second_activity.id)
2238 |> assign(:user, user)
2239 |> get("/api/v1/favourites?limit=0")
2241 assert [] = json_response(third_conn, 200)
2244 describe "getting favorites timeline of specified user" do
2246 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2247 [current_user: current_user, user: user]
2250 test "returns list of statuses favorited by specified user", %{
2252 current_user: current_user,
2255 [activity | _] = insert_pair(:note_activity)
2256 CommonAPI.favorite(activity.id, user)
2260 |> assign(:user, current_user)
2261 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2262 |> json_response(:ok)
2266 assert length(response) == 1
2267 assert like["id"] == activity.id
2270 test "returns favorites for specified user_id when user is not logged in", %{
2274 activity = insert(:note_activity)
2275 CommonAPI.favorite(activity.id, user)
2279 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2280 |> json_response(:ok)
2282 assert length(response) == 1
2285 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2287 current_user: current_user,
2291 CommonAPI.post(current_user, %{
2292 "status" => "Hi @#{user.nickname}!",
2293 "visibility" => "direct"
2296 CommonAPI.favorite(direct.id, user)
2300 |> assign(:user, current_user)
2301 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2302 |> json_response(:ok)
2304 assert length(response) == 1
2306 anonymous_response =
2308 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2309 |> json_response(:ok)
2311 assert Enum.empty?(anonymous_response)
2314 test "does not return others' favorited DM when user is not one of recipients", %{
2316 current_user: current_user,
2319 user_two = insert(:user)
2322 CommonAPI.post(user_two, %{
2323 "status" => "Hi @#{user.nickname}!",
2324 "visibility" => "direct"
2327 CommonAPI.favorite(direct.id, user)
2331 |> assign(:user, current_user)
2332 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2333 |> json_response(:ok)
2335 assert Enum.empty?(response)
2338 test "paginates favorites using since_id and max_id", %{
2340 current_user: current_user,
2343 activities = insert_list(10, :note_activity)
2345 Enum.each(activities, fn activity ->
2346 CommonAPI.favorite(activity.id, user)
2349 third_activity = Enum.at(activities, 2)
2350 seventh_activity = Enum.at(activities, 6)
2354 |> assign(:user, current_user)
2355 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2356 since_id: third_activity.id,
2357 max_id: seventh_activity.id
2359 |> json_response(:ok)
2361 assert length(response) == 3
2362 refute third_activity in response
2363 refute seventh_activity in response
2366 test "limits favorites using limit parameter", %{
2368 current_user: current_user,
2372 |> insert_list(:note_activity)
2373 |> Enum.each(fn activity ->
2374 CommonAPI.favorite(activity.id, user)
2379 |> assign(:user, current_user)
2380 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2381 |> json_response(:ok)
2383 assert length(response) == 3
2386 test "returns empty response when user does not have any favorited statuses", %{
2388 current_user: current_user,
2393 |> assign(:user, current_user)
2394 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2395 |> json_response(:ok)
2397 assert Enum.empty?(response)
2400 test "returns 404 error when specified user is not exist", %{conn: conn} do
2401 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2403 assert json_response(conn, 404) == %{"error" => "Record not found"}
2406 test "returns 403 error when user has hidden own favorites", %{
2408 current_user: current_user
2410 user = insert(:user, %{info: %{hide_favorites: true}})
2411 activity = insert(:note_activity)
2412 CommonAPI.favorite(activity.id, user)
2416 |> assign(:user, current_user)
2417 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2419 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2422 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2423 user = insert(:user)
2424 activity = insert(:note_activity)
2425 CommonAPI.favorite(activity.id, user)
2429 |> assign(:user, current_user)
2430 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2432 assert user.info.hide_favorites
2433 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2437 describe "updating credentials" do
2438 test "sets user settings in a generic way", %{conn: conn} do
2439 user = insert(:user)
2443 |> assign(:user, user)
2444 |> patch("/api/v1/accounts/update_credentials", %{
2445 "pleroma_settings_store" => %{
2452 assert user = json_response(res_conn, 200)
2453 assert user["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}}
2455 user = Repo.get(User, user["id"])
2459 |> assign(:user, user)
2460 |> patch("/api/v1/accounts/update_credentials", %{
2461 "pleroma_settings_store" => %{
2468 assert user = json_response(res_conn, 200)
2470 assert user["pleroma"]["settings_store"] ==
2472 "pleroma_fe" => %{"theme" => "bla"},
2473 "masto_fe" => %{"theme" => "bla"}
2476 user = Repo.get(User, user["id"])
2480 |> assign(:user, user)
2481 |> patch("/api/v1/accounts/update_credentials", %{
2482 "pleroma_settings_store" => %{
2489 assert user = json_response(res_conn, 200)
2491 assert user["pleroma"]["settings_store"] ==
2493 "pleroma_fe" => %{"theme" => "bla"},
2494 "masto_fe" => %{"theme" => "blub"}
2498 test "updates the user's bio", %{conn: conn} do
2499 user = insert(:user)
2500 user2 = insert(:user)
2504 |> assign(:user, user)
2505 |> patch("/api/v1/accounts/update_credentials", %{
2506 "note" => "I drink #cofe with @#{user2.nickname}"
2509 assert user = json_response(conn, 200)
2511 assert user["note"] ==
2512 ~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=") <>
2514 ~s(" class="u-url mention" href=") <>
2515 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2518 test "updates the user's locking status", %{conn: conn} do
2519 user = insert(:user)
2523 |> assign(:user, user)
2524 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2526 assert user = json_response(conn, 200)
2527 assert user["locked"] == true
2530 test "updates the user's default scope", %{conn: conn} do
2531 user = insert(:user)
2535 |> assign(:user, user)
2536 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2538 assert user = json_response(conn, 200)
2539 assert user["source"]["privacy"] == "cofe"
2542 test "updates the user's hide_followers status", %{conn: conn} do
2543 user = insert(:user)
2547 |> assign(:user, user)
2548 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2550 assert user = json_response(conn, 200)
2551 assert user["pleroma"]["hide_followers"] == true
2554 test "updates the user's skip_thread_containment option", %{conn: conn} do
2555 user = insert(:user)
2559 |> assign(:user, user)
2560 |> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"})
2561 |> json_response(200)
2563 assert response["pleroma"]["skip_thread_containment"] == true
2564 assert refresh_record(user).info.skip_thread_containment
2567 test "updates the user's hide_follows status", %{conn: conn} do
2568 user = insert(:user)
2572 |> assign(:user, user)
2573 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2575 assert user = json_response(conn, 200)
2576 assert user["pleroma"]["hide_follows"] == true
2579 test "updates the user's hide_favorites status", %{conn: conn} do
2580 user = insert(:user)
2584 |> assign(:user, user)
2585 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2587 assert user = json_response(conn, 200)
2588 assert user["pleroma"]["hide_favorites"] == true
2591 test "updates the user's show_role status", %{conn: conn} do
2592 user = insert(:user)
2596 |> assign(:user, user)
2597 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2599 assert user = json_response(conn, 200)
2600 assert user["source"]["pleroma"]["show_role"] == false
2603 test "updates the user's no_rich_text status", %{conn: conn} do
2604 user = insert(:user)
2608 |> assign(:user, user)
2609 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2611 assert user = json_response(conn, 200)
2612 assert user["source"]["pleroma"]["no_rich_text"] == true
2615 test "updates the user's name", %{conn: conn} do
2616 user = insert(:user)
2620 |> assign(:user, user)
2621 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2623 assert user = json_response(conn, 200)
2624 assert user["display_name"] == "markorepairs"
2627 test "updates the user's avatar", %{conn: conn} do
2628 user = insert(:user)
2630 new_avatar = %Plug.Upload{
2631 content_type: "image/jpg",
2632 path: Path.absname("test/fixtures/image.jpg"),
2633 filename: "an_image.jpg"
2638 |> assign(:user, user)
2639 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2641 assert user_response = json_response(conn, 200)
2642 assert user_response["avatar"] != User.avatar_url(user)
2645 test "updates the user's banner", %{conn: conn} do
2646 user = insert(:user)
2648 new_header = %Plug.Upload{
2649 content_type: "image/jpg",
2650 path: Path.absname("test/fixtures/image.jpg"),
2651 filename: "an_image.jpg"
2656 |> assign(:user, user)
2657 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2659 assert user_response = json_response(conn, 200)
2660 assert user_response["header"] != User.banner_url(user)
2663 test "requires 'write' permission", %{conn: conn} do
2664 token1 = insert(:oauth_token, scopes: ["read"])
2665 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2667 for token <- [token1, token2] do
2670 |> put_req_header("authorization", "Bearer #{token.token}")
2671 |> patch("/api/v1/accounts/update_credentials", %{})
2673 if token == token1 do
2674 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2676 assert json_response(conn, 200)
2681 test "updates profile emojos", %{conn: conn} do
2682 user = insert(:user)
2684 note = "*sips :blank:*"
2685 name = "I am :firefox:"
2689 |> assign(:user, user)
2690 |> patch("/api/v1/accounts/update_credentials", %{
2692 "display_name" => name
2695 assert json_response(conn, 200)
2699 |> get("/api/v1/accounts/#{user.id}")
2701 assert user = json_response(conn, 200)
2703 assert user["note"] == note
2704 assert user["display_name"] == name
2705 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2709 test "get instance information", %{conn: conn} do
2710 conn = get(conn, "/api/v1/instance")
2711 assert result = json_response(conn, 200)
2713 email = Pleroma.Config.get([:instance, :email])
2714 # Note: not checking for "max_toot_chars" since it's optional
2720 "email" => from_config_email,
2722 "streaming_api" => _
2727 "registrations" => _,
2731 assert email == from_config_email
2734 test "get instance stats", %{conn: conn} do
2735 user = insert(:user, %{local: true})
2737 user2 = insert(:user, %{local: true})
2738 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2740 insert(:user, %{local: false, nickname: "u@peer1.com"})
2741 insert(:user, %{local: false, nickname: "u@peer2.com"})
2743 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2745 # Stats should count users with missing or nil `info.deactivated` value
2746 user = User.get_cached_by_id(user.id)
2747 info_change = Changeset.change(user.info, %{deactivated: nil})
2751 |> Changeset.change()
2752 |> Changeset.put_embed(:info, info_change)
2753 |> User.update_and_set_cache()
2755 Pleroma.Stats.update_stats()
2757 conn = get(conn, "/api/v1/instance")
2759 assert result = json_response(conn, 200)
2761 stats = result["stats"]
2764 assert stats["user_count"] == 1
2765 assert stats["status_count"] == 1
2766 assert stats["domain_count"] == 2
2769 test "get peers", %{conn: conn} do
2770 insert(:user, %{local: false, nickname: "u@peer1.com"})
2771 insert(:user, %{local: false, nickname: "u@peer2.com"})
2773 Pleroma.Stats.update_stats()
2775 conn = get(conn, "/api/v1/instance/peers")
2777 assert result = json_response(conn, 200)
2779 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2782 test "put settings", %{conn: conn} do
2783 user = insert(:user)
2787 |> assign(:user, user)
2788 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2790 assert _result = json_response(conn, 200)
2792 user = User.get_cached_by_ap_id(user.ap_id)
2793 assert user.info.settings == %{"programming" => "socks"}
2796 describe "pinned statuses" do
2798 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2800 user = insert(:user)
2801 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2803 [user: user, activity: activity]
2806 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2807 {:ok, _} = CommonAPI.pin(activity.id, user)
2811 |> assign(:user, user)
2812 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2813 |> json_response(200)
2815 id_str = to_string(activity.id)
2817 assert [%{"id" => ^id_str, "pinned" => true}] = result
2820 test "pin status", %{conn: conn, user: user, activity: activity} do
2821 id_str = to_string(activity.id)
2823 assert %{"id" => ^id_str, "pinned" => true} =
2825 |> assign(:user, user)
2826 |> post("/api/v1/statuses/#{activity.id}/pin")
2827 |> json_response(200)
2829 assert [%{"id" => ^id_str, "pinned" => true}] =
2831 |> assign(:user, user)
2832 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2833 |> json_response(200)
2836 test "unpin status", %{conn: conn, user: user, activity: activity} do
2837 {:ok, _} = CommonAPI.pin(activity.id, user)
2839 id_str = to_string(activity.id)
2840 user = refresh_record(user)
2842 assert %{"id" => ^id_str, "pinned" => false} =
2844 |> assign(:user, user)
2845 |> post("/api/v1/statuses/#{activity.id}/unpin")
2846 |> json_response(200)
2850 |> assign(:user, user)
2851 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2852 |> json_response(200)
2855 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2856 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2858 id_str_one = to_string(activity_one.id)
2860 assert %{"id" => ^id_str_one, "pinned" => true} =
2862 |> assign(:user, user)
2863 |> post("/api/v1/statuses/#{id_str_one}/pin")
2864 |> json_response(200)
2866 user = refresh_record(user)
2868 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2870 |> assign(:user, user)
2871 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2872 |> json_response(400)
2878 Pleroma.Config.put([:rich_media, :enabled], true)
2881 Pleroma.Config.put([:rich_media, :enabled], false)
2884 user = insert(:user)
2888 test "returns rich-media card", %{conn: conn, user: user} do
2889 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2892 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2893 "provider_name" => "www.imdb.com",
2894 "provider_url" => "http://www.imdb.com",
2895 "title" => "The Rock",
2897 "url" => "http://www.imdb.com/title/tt0117500/",
2899 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2902 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2903 "title" => "The Rock",
2904 "type" => "video.movie",
2905 "url" => "http://www.imdb.com/title/tt0117500/",
2907 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2914 |> get("/api/v1/statuses/#{activity.id}/card")
2915 |> json_response(200)
2917 assert response == card_data
2919 # works with private posts
2921 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2925 |> assign(:user, user)
2926 |> get("/api/v1/statuses/#{activity.id}/card")
2927 |> json_response(200)
2929 assert response_two == card_data
2932 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2933 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp-missing-data"})
2937 |> get("/api/v1/statuses/#{activity.id}/card")
2938 |> json_response(:ok)
2940 assert response == %{
2942 "title" => "Pleroma",
2943 "description" => "",
2945 "provider_name" => "pleroma.social",
2946 "provider_url" => "https://pleroma.social",
2947 "url" => "https://pleroma.social/",
2950 "title" => "Pleroma",
2951 "type" => "website",
2952 "url" => "https://pleroma.social/"
2960 user = insert(:user)
2961 for_user = insert(:user)
2964 CommonAPI.post(user, %{
2965 "status" => "heweoo?"
2969 CommonAPI.post(user, %{
2970 "status" => "heweoo!"
2975 |> assign(:user, for_user)
2976 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2978 assert json_response(response1, 200)["bookmarked"] == true
2982 |> assign(:user, for_user)
2983 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2985 assert json_response(response2, 200)["bookmarked"] == true
2989 |> assign(:user, for_user)
2990 |> get("/api/v1/bookmarks")
2992 assert [json_response(response2, 200), json_response(response1, 200)] ==
2993 json_response(bookmarks, 200)
2997 |> assign(:user, for_user)
2998 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
3000 assert json_response(response1, 200)["bookmarked"] == false
3004 |> assign(:user, for_user)
3005 |> get("/api/v1/bookmarks")
3007 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
3010 describe "conversation muting" do
3012 user = insert(:user)
3013 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
3015 [user: user, activity: activity]
3018 test "mute conversation", %{conn: conn, user: user, activity: activity} do
3019 id_str = to_string(activity.id)
3021 assert %{"id" => ^id_str, "muted" => true} =
3023 |> assign(:user, user)
3024 |> post("/api/v1/statuses/#{activity.id}/mute")
3025 |> json_response(200)
3028 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
3029 {:ok, _} = CommonAPI.add_mute(user, activity)
3031 id_str = to_string(activity.id)
3032 user = refresh_record(user)
3034 assert %{"id" => ^id_str, "muted" => false} =
3036 |> assign(:user, user)
3037 |> post("/api/v1/statuses/#{activity.id}/unmute")
3038 |> json_response(200)
3042 describe "reports" do
3044 reporter = insert(:user)
3045 target_user = insert(:user)
3047 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
3049 [reporter: reporter, target_user: target_user, activity: activity]
3052 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
3053 assert %{"action_taken" => false, "id" => _} =
3055 |> assign(:user, reporter)
3056 |> post("/api/v1/reports", %{"account_id" => target_user.id})
3057 |> json_response(200)
3060 test "submit a report with statuses and comment", %{
3063 target_user: target_user,
3066 assert %{"action_taken" => false, "id" => _} =
3068 |> assign(:user, reporter)
3069 |> post("/api/v1/reports", %{
3070 "account_id" => target_user.id,
3071 "status_ids" => [activity.id],
3072 "comment" => "bad status!"
3074 |> json_response(200)
3077 test "account_id is required", %{
3082 assert %{"error" => "Valid `account_id` required"} =
3084 |> assign(:user, reporter)
3085 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3086 |> json_response(400)
3089 test "comment must be up to the size specified in the config", %{
3092 target_user: target_user
3094 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
3095 comment = String.pad_trailing("a", max_size + 1, "a")
3097 error = %{"error" => "Comment must be up to #{max_size} characters"}
3101 |> assign(:user, reporter)
3102 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3103 |> json_response(400)
3107 describe "link headers" do
3108 test "preserves parameters in link headers", %{conn: conn} do
3109 user = insert(:user)
3110 other_user = insert(:user)
3113 CommonAPI.post(other_user, %{
3114 "status" => "hi @#{user.nickname}",
3115 "visibility" => "public"
3119 CommonAPI.post(other_user, %{
3120 "status" => "hi @#{user.nickname}",
3121 "visibility" => "public"
3124 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3125 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3129 |> assign(:user, user)
3130 |> get("/api/v1/notifications", %{media_only: true})
3132 assert [link_header] = get_resp_header(conn, "link")
3133 assert link_header =~ ~r/media_only=true/
3134 assert link_header =~ ~r/min_id=#{notification2.id}/
3135 assert link_header =~ ~r/max_id=#{notification1.id}/
3139 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3140 # Need to set an old-style integer ID to reproduce the problem
3141 # (these are no longer assigned to new accounts but were preserved
3142 # for existing accounts during the migration to flakeIDs)
3143 user_one = insert(:user, %{id: 1212})
3144 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3148 |> get("/api/v1/accounts/#{user_one.id}")
3152 |> get("/api/v1/accounts/#{user_two.nickname}")
3156 |> get("/api/v1/accounts/#{user_two.id}")
3158 acc_one = json_response(resp_one, 200)
3159 acc_two = json_response(resp_two, 200)
3160 acc_three = json_response(resp_three, 200)
3161 refute acc_one == acc_two
3162 assert acc_two == acc_three
3165 describe "custom emoji" do
3166 test "with tags", %{conn: conn} do
3169 |> get("/api/v1/custom_emojis")
3170 |> json_response(200)
3172 assert Map.has_key?(emoji, "shortcode")
3173 assert Map.has_key?(emoji, "static_url")
3174 assert Map.has_key?(emoji, "tags")
3175 assert is_list(emoji["tags"])
3176 assert Map.has_key?(emoji, "url")
3177 assert Map.has_key?(emoji, "visible_in_picker")
3181 describe "index/2 redirections" do
3182 setup %{conn: conn} do
3186 signing_salt: "cooldude"
3191 |> Plug.Session.call(Plug.Session.init(session_opts))
3194 test_path = "/web/statuses/test"
3195 %{conn: conn, path: test_path}
3198 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3199 conn = get(conn, path)
3201 assert conn.status == 302
3202 assert redirected_to(conn) == "/web/login"
3205 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3206 token = insert(:oauth_token)
3210 |> assign(:user, token.user)
3211 |> put_session(:oauth_token, token.token)
3214 assert conn.status == 200
3217 test "saves referer path to session", %{conn: conn, path: path} do
3218 conn = get(conn, path)
3219 return_to = Plug.Conn.get_session(conn, :return_to)
3221 assert return_to == path
3224 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3225 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3226 auth = insert(:oauth_authorization, app: app)
3230 |> put_session(:return_to, path)
3231 |> get("/web/login", %{code: auth.token})
3233 assert conn.status == 302
3234 assert redirected_to(conn) == path
3237 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3238 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3239 auth = insert(:oauth_authorization, app: app)
3241 conn = get(conn, "/web/login", %{code: auth.token})
3243 assert conn.status == 302
3244 assert redirected_to(conn) == "/web/getting-started"
3248 describe "scheduled activities" do
3249 test "creates a scheduled activity", %{conn: conn} do
3250 user = insert(:user)
3251 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3255 |> assign(:user, user)
3256 |> post("/api/v1/statuses", %{
3257 "status" => "scheduled",
3258 "scheduled_at" => scheduled_at
3261 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3262 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3263 assert [] == Repo.all(Activity)
3266 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3267 user = insert(:user)
3268 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3270 file = %Plug.Upload{
3271 content_type: "image/jpg",
3272 path: Path.absname("test/fixtures/image.jpg"),
3273 filename: "an_image.jpg"
3276 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3280 |> assign(:user, user)
3281 |> post("/api/v1/statuses", %{
3282 "media_ids" => [to_string(upload.id)],
3283 "status" => "scheduled",
3284 "scheduled_at" => scheduled_at
3287 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3288 assert %{"type" => "image"} = media_attachment
3291 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3293 user = insert(:user)
3296 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3300 |> assign(:user, user)
3301 |> post("/api/v1/statuses", %{
3302 "status" => "not scheduled",
3303 "scheduled_at" => scheduled_at
3306 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3307 assert [] == Repo.all(ScheduledActivity)
3310 test "returns error when daily user limit is exceeded", %{conn: conn} do
3311 user = insert(:user)
3314 NaiveDateTime.utc_now()
3315 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3316 |> NaiveDateTime.to_iso8601()
3318 attrs = %{params: %{}, scheduled_at: today}
3319 {:ok, _} = ScheduledActivity.create(user, attrs)
3320 {:ok, _} = ScheduledActivity.create(user, attrs)
3324 |> assign(:user, user)
3325 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3327 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3330 test "returns error when total user limit is exceeded", %{conn: conn} do
3331 user = insert(:user)
3334 NaiveDateTime.utc_now()
3335 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3336 |> NaiveDateTime.to_iso8601()
3339 NaiveDateTime.utc_now()
3340 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3341 |> NaiveDateTime.to_iso8601()
3343 attrs = %{params: %{}, scheduled_at: today}
3344 {:ok, _} = ScheduledActivity.create(user, attrs)
3345 {:ok, _} = ScheduledActivity.create(user, attrs)
3346 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3350 |> assign(:user, user)
3351 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3353 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3356 test "shows scheduled activities", %{conn: conn} do
3357 user = insert(:user)
3358 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3359 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3360 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3361 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3365 |> assign(:user, user)
3370 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3372 result = json_response(conn_res, 200)
3373 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3378 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3380 result = json_response(conn_res, 200)
3381 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3386 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3388 result = json_response(conn_res, 200)
3389 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3392 test "shows a scheduled activity", %{conn: conn} do
3393 user = insert(:user)
3394 scheduled_activity = insert(:scheduled_activity, user: user)
3398 |> assign(:user, user)
3399 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3401 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3402 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3406 |> assign(:user, user)
3407 |> get("/api/v1/scheduled_statuses/404")
3409 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3412 test "updates a scheduled activity", %{conn: conn} do
3413 user = insert(:user)
3414 scheduled_activity = insert(:scheduled_activity, user: user)
3417 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3421 |> assign(:user, user)
3422 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3423 scheduled_at: new_scheduled_at
3426 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3427 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3431 |> assign(:user, user)
3432 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3434 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3437 test "deletes a scheduled activity", %{conn: conn} do
3438 user = insert(:user)
3439 scheduled_activity = insert(:scheduled_activity, user: user)
3443 |> assign(:user, user)
3444 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3446 assert %{} = json_response(res_conn, 200)
3447 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3451 |> assign(:user, user)
3452 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3454 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3458 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3459 user1 = insert(:user)
3460 user2 = insert(:user)
3461 user3 = insert(:user)
3463 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3465 # Reply to status from another user
3468 |> assign(:user, user2)
3469 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3471 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3473 activity = Activity.get_by_id_with_object(id)
3475 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3476 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3478 # Reblog from the third user
3481 |> assign(:user, user3)
3482 |> post("/api/v1/statuses/#{activity.id}/reblog")
3484 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3485 json_response(conn2, 200)
3487 assert to_string(activity.id) == id
3489 # Getting third user status
3492 |> assign(:user, user3)
3493 |> get("api/v1/timelines/home")
3495 [reblogged_activity] = json_response(conn3, 200)
3497 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3499 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3500 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3503 describe "create account by app" do
3505 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3506 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3507 interval = Pleroma.Config.get([:app_account_creation, :interval])
3509 Pleroma.Config.put([:app_account_creation, :enabled], true)
3510 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3511 Pleroma.Config.put([:app_account_creation, :interval], 1)
3514 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3515 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3516 Pleroma.Config.put([:app_account_creation, :interval], interval)
3522 test "Account registration via Application", %{conn: conn} do
3525 |> post("/api/v1/apps", %{
3526 client_name: "client_name",
3527 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3528 scopes: "read, write, follow"
3532 "client_id" => client_id,
3533 "client_secret" => client_secret,
3535 "name" => "client_name",
3536 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3539 } = json_response(conn, 200)
3543 |> post("/oauth/token", %{
3544 grant_type: "client_credentials",
3545 client_id: client_id,
3546 client_secret: client_secret
3549 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3550 json_response(conn, 200)
3553 token_from_db = Repo.get_by(Token, token: token)
3554 assert token_from_db
3556 assert scope == "read write follow"
3560 |> put_req_header("authorization", "Bearer " <> token)
3561 |> post("/api/v1/accounts", %{
3563 email: "lain@example.org",
3564 password: "PlzDontHackLain",
3569 "access_token" => token,
3570 "created_at" => _created_at,
3572 "token_type" => "Bearer"
3573 } = json_response(conn, 200)
3575 token_from_db = Repo.get_by(Token, token: token)
3576 assert token_from_db
3577 token_from_db = Repo.preload(token_from_db, :user)
3578 assert token_from_db.user
3580 assert token_from_db.user.info.confirmation_pending
3583 test "rate limit", %{conn: conn} do
3584 app_token = insert(:oauth_token, user: nil)
3587 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3588 |> Map.put(:remote_ip, {15, 15, 15, 15})
3593 |> post("/api/v1/accounts", %{
3594 username: "#{i}lain",
3595 email: "#{i}lain@example.org",
3596 password: "PlzDontHackLain",
3601 "access_token" => token,
3602 "created_at" => _created_at,
3604 "token_type" => "Bearer"
3605 } = json_response(conn, 200)
3607 token_from_db = Repo.get_by(Token, token: token)
3608 assert token_from_db
3609 token_from_db = Repo.preload(token_from_db, :user)
3610 assert token_from_db.user
3612 assert token_from_db.user.info.confirmation_pending
3617 |> post("/api/v1/accounts", %{
3619 email: "6lain@example.org",
3620 password: "PlzDontHackLain",
3624 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}
3628 describe "GET /api/v1/polls/:id" do
3629 test "returns poll entity for object id", %{conn: conn} do
3630 user = insert(:user)
3633 CommonAPI.post(user, %{
3634 "status" => "Pleroma does",
3635 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3638 object = Object.normalize(activity)
3642 |> assign(:user, user)
3643 |> get("/api/v1/polls/#{object.id}")
3645 response = json_response(conn, 200)
3647 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3650 test "does not expose polls for private statuses", %{conn: conn} do
3651 user = insert(:user)
3652 other_user = insert(:user)
3655 CommonAPI.post(user, %{
3656 "status" => "Pleroma does",
3657 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3658 "visibility" => "private"
3661 object = Object.normalize(activity)
3665 |> assign(:user, other_user)
3666 |> get("/api/v1/polls/#{object.id}")
3668 assert json_response(conn, 404)
3672 describe "POST /api/v1/polls/:id/votes" do
3673 test "votes are added to the poll", %{conn: conn} do
3674 user = insert(:user)
3675 other_user = insert(:user)
3678 CommonAPI.post(user, %{
3679 "status" => "A very delicious sandwich",
3681 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3687 object = Object.normalize(activity)
3691 |> assign(:user, other_user)
3692 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3694 assert json_response(conn, 200)
3695 object = Object.get_by_id(object.id)
3697 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3702 test "author can't vote", %{conn: conn} do
3703 user = insert(:user)
3706 CommonAPI.post(user, %{
3707 "status" => "Am I cute?",
3708 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3711 object = Object.normalize(activity)
3714 |> assign(:user, user)
3715 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3716 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3718 object = Object.get_by_id(object.id)
3720 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3723 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3724 user = insert(:user)
3725 other_user = insert(:user)
3728 CommonAPI.post(user, %{
3729 "status" => "The glass is",
3730 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3733 object = Object.normalize(activity)
3736 |> assign(:user, other_user)
3737 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3738 |> json_response(422) == %{"error" => "Too many choices"}
3740 object = Object.get_by_id(object.id)
3742 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->