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 "media upload" do
1426 upload_config = Pleroma.Config.get([Pleroma.Upload])
1427 proxy_config = Pleroma.Config.get([:media_proxy])
1430 Pleroma.Config.put([Pleroma.Upload], upload_config)
1431 Pleroma.Config.put([:media_proxy], proxy_config)
1434 user = insert(:user)
1438 |> assign(:user, user)
1440 image = %Plug.Upload{
1441 content_type: "image/jpg",
1442 path: Path.absname("test/fixtures/image.jpg"),
1443 filename: "an_image.jpg"
1446 [conn: conn, image: image]
1449 test "returns uploaded image", %{conn: conn, image: image} do
1450 desc = "Description of the image"
1454 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1455 |> json_response(:ok)
1457 assert media["type"] == "image"
1458 assert media["description"] == desc
1461 object = Repo.get(Object, media["id"])
1462 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1465 test "returns proxied url when media proxy is enabled", %{conn: conn, image: image} do
1466 Pleroma.Config.put([Pleroma.Upload, :base_url], "https://media.pleroma.social")
1468 proxy_url = "https://cache.pleroma.social"
1469 Pleroma.Config.put([:media_proxy, :enabled], true)
1470 Pleroma.Config.put([:media_proxy, :base_url], proxy_url)
1474 |> post("/api/v1/media", %{"file" => image})
1475 |> json_response(:ok)
1477 assert String.starts_with?(media["url"], proxy_url)
1480 test "returns media url when proxy is enabled but media url is whitelisted", %{
1484 media_url = "https://media.pleroma.social"
1485 Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
1487 Pleroma.Config.put([:media_proxy, :enabled], true)
1488 Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
1489 Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
1493 |> post("/api/v1/media", %{"file" => image})
1494 |> json_response(:ok)
1496 assert String.starts_with?(media["url"], media_url)
1500 describe "locked accounts" do
1501 test "/api/v1/follow_requests works" do
1502 user = insert(:user, %{info: %User.Info{locked: true}})
1503 other_user = insert(:user)
1505 {:ok, _activity} = ActivityPub.follow(other_user, user)
1507 user = User.get_cached_by_id(user.id)
1508 other_user = User.get_cached_by_id(other_user.id)
1510 assert User.following?(other_user, user) == false
1514 |> assign(:user, user)
1515 |> get("/api/v1/follow_requests")
1517 assert [relationship] = json_response(conn, 200)
1518 assert to_string(other_user.id) == relationship["id"]
1521 test "/api/v1/follow_requests/:id/authorize works" do
1522 user = insert(:user, %{info: %User.Info{locked: true}})
1523 other_user = insert(:user)
1525 {:ok, _activity} = ActivityPub.follow(other_user, user)
1527 user = User.get_cached_by_id(user.id)
1528 other_user = User.get_cached_by_id(other_user.id)
1530 assert User.following?(other_user, user) == false
1534 |> assign(:user, user)
1535 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1537 assert relationship = json_response(conn, 200)
1538 assert to_string(other_user.id) == relationship["id"]
1540 user = User.get_cached_by_id(user.id)
1541 other_user = User.get_cached_by_id(other_user.id)
1543 assert User.following?(other_user, user) == true
1546 test "verify_credentials", %{conn: conn} do
1547 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1551 |> assign(:user, user)
1552 |> get("/api/v1/accounts/verify_credentials")
1554 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1555 assert id == to_string(user.id)
1558 test "/api/v1/follow_requests/:id/reject works" do
1559 user = insert(:user, %{info: %User.Info{locked: true}})
1560 other_user = insert(:user)
1562 {:ok, _activity} = ActivityPub.follow(other_user, user)
1564 user = User.get_cached_by_id(user.id)
1568 |> assign(:user, user)
1569 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1571 assert relationship = json_response(conn, 200)
1572 assert to_string(other_user.id) == relationship["id"]
1574 user = User.get_cached_by_id(user.id)
1575 other_user = User.get_cached_by_id(other_user.id)
1577 assert User.following?(other_user, user) == false
1581 test "account fetching", %{conn: conn} do
1582 user = insert(:user)
1586 |> get("/api/v1/accounts/#{user.id}")
1588 assert %{"id" => id} = json_response(conn, 200)
1589 assert id == to_string(user.id)
1593 |> get("/api/v1/accounts/-1")
1595 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1598 test "account fetching also works nickname", %{conn: conn} do
1599 user = insert(:user)
1603 |> get("/api/v1/accounts/#{user.nickname}")
1605 assert %{"id" => id} = json_response(conn, 200)
1606 assert id == user.id
1609 test "mascot upload", %{conn: conn} do
1610 user = insert(:user)
1612 non_image_file = %Plug.Upload{
1613 content_type: "audio/mpeg",
1614 path: Path.absname("test/fixtures/sound.mp3"),
1615 filename: "sound.mp3"
1620 |> assign(:user, user)
1621 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1623 assert json_response(conn, 415)
1625 file = %Plug.Upload{
1626 content_type: "image/jpg",
1627 path: Path.absname("test/fixtures/image.jpg"),
1628 filename: "an_image.jpg"
1633 |> assign(:user, user)
1634 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1636 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1639 test "mascot retrieving", %{conn: conn} do
1640 user = insert(:user)
1641 # When user hasn't set a mascot, we should just get pleroma tan back
1644 |> assign(:user, user)
1645 |> get("/api/v1/pleroma/mascot")
1647 assert %{"url" => url} = json_response(conn, 200)
1648 assert url =~ "pleroma-fox-tan-smol"
1650 # When a user sets their mascot, we should get that back
1651 file = %Plug.Upload{
1652 content_type: "image/jpg",
1653 path: Path.absname("test/fixtures/image.jpg"),
1654 filename: "an_image.jpg"
1659 |> assign(:user, user)
1660 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1662 assert json_response(conn, 200)
1664 user = User.get_cached_by_id(user.id)
1668 |> assign(:user, user)
1669 |> get("/api/v1/pleroma/mascot")
1671 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1672 assert url =~ "an_image"
1675 test "hashtag timeline", %{conn: conn} do
1676 following = insert(:user)
1679 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1681 {:ok, [_activity]} =
1682 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1686 |> get("/api/v1/timelines/tag/2hu")
1688 assert [%{"id" => id}] = json_response(nconn, 200)
1690 assert id == to_string(activity.id)
1692 # works for different capitalization too
1695 |> get("/api/v1/timelines/tag/2HU")
1697 assert [%{"id" => id}] = json_response(nconn, 200)
1699 assert id == to_string(activity.id)
1703 test "multi-hashtag timeline", %{conn: conn} do
1704 user = insert(:user)
1706 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1707 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1708 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1712 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1714 [status_none, status_test1, status_test] = json_response(any_test, 200)
1716 assert to_string(activity_test.id) == status_test["id"]
1717 assert to_string(activity_test1.id) == status_test1["id"]
1718 assert to_string(activity_none.id) == status_none["id"]
1722 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1724 assert [status_test1] == json_response(restricted_test, 200)
1726 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1728 assert [status_none] == json_response(all_test, 200)
1731 test "getting followers", %{conn: conn} do
1732 user = insert(:user)
1733 other_user = insert(:user)
1734 {:ok, user} = User.follow(user, other_user)
1738 |> get("/api/v1/accounts/#{other_user.id}/followers")
1740 assert [%{"id" => id}] = json_response(conn, 200)
1741 assert id == to_string(user.id)
1744 test "getting followers, hide_followers", %{conn: conn} do
1745 user = insert(:user)
1746 other_user = insert(:user, %{info: %{hide_followers: true}})
1747 {:ok, _user} = User.follow(user, other_user)
1751 |> get("/api/v1/accounts/#{other_user.id}/followers")
1753 assert [] == json_response(conn, 200)
1756 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1757 user = insert(:user)
1758 other_user = insert(:user, %{info: %{hide_followers: true}})
1759 {:ok, _user} = User.follow(user, other_user)
1763 |> assign(:user, other_user)
1764 |> get("/api/v1/accounts/#{other_user.id}/followers")
1766 refute [] == json_response(conn, 200)
1769 test "getting followers, pagination", %{conn: conn} do
1770 user = insert(:user)
1771 follower1 = insert(:user)
1772 follower2 = insert(:user)
1773 follower3 = insert(:user)
1774 {:ok, _} = User.follow(follower1, user)
1775 {:ok, _} = User.follow(follower2, user)
1776 {:ok, _} = User.follow(follower3, user)
1780 |> assign(:user, user)
1784 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1786 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1787 assert id3 == follower3.id
1788 assert id2 == follower2.id
1792 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1794 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1795 assert id2 == follower2.id
1796 assert id1 == follower1.id
1800 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1802 assert [%{"id" => id2}] = json_response(res_conn, 200)
1803 assert id2 == follower2.id
1805 assert [link_header] = get_resp_header(res_conn, "link")
1806 assert link_header =~ ~r/min_id=#{follower2.id}/
1807 assert link_header =~ ~r/max_id=#{follower2.id}/
1810 test "getting following", %{conn: conn} do
1811 user = insert(:user)
1812 other_user = insert(:user)
1813 {:ok, user} = User.follow(user, other_user)
1817 |> get("/api/v1/accounts/#{user.id}/following")
1819 assert [%{"id" => id}] = json_response(conn, 200)
1820 assert id == to_string(other_user.id)
1823 test "getting following, hide_follows", %{conn: conn} do
1824 user = insert(:user, %{info: %{hide_follows: true}})
1825 other_user = insert(:user)
1826 {:ok, user} = User.follow(user, other_user)
1830 |> get("/api/v1/accounts/#{user.id}/following")
1832 assert [] == json_response(conn, 200)
1835 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1836 user = insert(:user, %{info: %{hide_follows: true}})
1837 other_user = insert(:user)
1838 {:ok, user} = User.follow(user, other_user)
1842 |> assign(:user, user)
1843 |> get("/api/v1/accounts/#{user.id}/following")
1845 refute [] == json_response(conn, 200)
1848 test "getting following, pagination", %{conn: conn} do
1849 user = insert(:user)
1850 following1 = insert(:user)
1851 following2 = insert(:user)
1852 following3 = insert(:user)
1853 {:ok, _} = User.follow(user, following1)
1854 {:ok, _} = User.follow(user, following2)
1855 {:ok, _} = User.follow(user, following3)
1859 |> assign(:user, user)
1863 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1865 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1866 assert id3 == following3.id
1867 assert id2 == following2.id
1871 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1873 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1874 assert id2 == following2.id
1875 assert id1 == following1.id
1879 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1881 assert [%{"id" => id2}] = json_response(res_conn, 200)
1882 assert id2 == following2.id
1884 assert [link_header] = get_resp_header(res_conn, "link")
1885 assert link_header =~ ~r/min_id=#{following2.id}/
1886 assert link_header =~ ~r/max_id=#{following2.id}/
1889 test "following / unfollowing a user", %{conn: conn} do
1890 user = insert(:user)
1891 other_user = insert(:user)
1895 |> assign(:user, user)
1896 |> post("/api/v1/accounts/#{other_user.id}/follow")
1898 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1900 user = User.get_cached_by_id(user.id)
1904 |> assign(:user, user)
1905 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1907 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1909 user = User.get_cached_by_id(user.id)
1913 |> assign(:user, user)
1914 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1916 assert %{"id" => id} = json_response(conn, 200)
1917 assert id == to_string(other_user.id)
1920 test "following without reblogs" do
1921 follower = insert(:user)
1922 followed = insert(:user)
1923 other_user = insert(:user)
1927 |> assign(:user, follower)
1928 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1930 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1932 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1933 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1937 |> assign(:user, User.get_cached_by_id(follower.id))
1938 |> get("/api/v1/timelines/home")
1940 assert [] == json_response(conn, 200)
1944 |> assign(:user, follower)
1945 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1947 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1951 |> assign(:user, User.get_cached_by_id(follower.id))
1952 |> get("/api/v1/timelines/home")
1954 expected_activity_id = reblog.id
1955 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1958 test "following / unfollowing errors" do
1959 user = insert(:user)
1963 |> assign(:user, user)
1966 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1967 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1970 user = User.get_cached_by_id(user.id)
1971 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1972 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1974 # self follow via uri
1975 user = User.get_cached_by_id(user.id)
1976 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1977 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1979 # follow non existing user
1980 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1981 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1983 # follow non existing user via uri
1984 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1985 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1987 # unfollow non existing user
1988 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1989 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1992 test "muting / unmuting a user", %{conn: conn} do
1993 user = insert(:user)
1994 other_user = insert(:user)
1998 |> assign(:user, user)
1999 |> post("/api/v1/accounts/#{other_user.id}/mute")
2001 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
2003 user = User.get_cached_by_id(user.id)
2007 |> assign(:user, user)
2008 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2010 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
2013 test "subscribing / unsubscribing to a user", %{conn: conn} do
2014 user = insert(:user)
2015 subscription_target = insert(:user)
2019 |> assign(:user, user)
2020 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2022 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2026 |> assign(:user, user)
2027 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2029 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2032 test "getting a list of mutes", %{conn: conn} do
2033 user = insert(:user)
2034 other_user = insert(:user)
2036 {:ok, user} = User.mute(user, other_user)
2040 |> assign(:user, user)
2041 |> get("/api/v1/mutes")
2043 other_user_id = to_string(other_user.id)
2044 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2047 test "blocking / unblocking a user", %{conn: conn} do
2048 user = insert(:user)
2049 other_user = insert(:user)
2053 |> assign(:user, user)
2054 |> post("/api/v1/accounts/#{other_user.id}/block")
2056 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2058 user = User.get_cached_by_id(user.id)
2062 |> assign(:user, user)
2063 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2065 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2068 test "getting a list of blocks", %{conn: conn} do
2069 user = insert(:user)
2070 other_user = insert(:user)
2072 {:ok, user} = User.block(user, other_user)
2076 |> assign(:user, user)
2077 |> get("/api/v1/blocks")
2079 other_user_id = to_string(other_user.id)
2080 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2083 test "blocking / unblocking a domain", %{conn: conn} do
2084 user = insert(:user)
2085 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2089 |> assign(:user, user)
2090 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2092 assert %{} = json_response(conn, 200)
2093 user = User.get_cached_by_ap_id(user.ap_id)
2094 assert User.blocks?(user, other_user)
2098 |> assign(:user, user)
2099 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2101 assert %{} = json_response(conn, 200)
2102 user = User.get_cached_by_ap_id(user.ap_id)
2103 refute User.blocks?(user, other_user)
2106 test "getting a list of domain blocks", %{conn: conn} do
2107 user = insert(:user)
2109 {:ok, user} = User.block_domain(user, "bad.site")
2110 {:ok, user} = User.block_domain(user, "even.worse.site")
2114 |> assign(:user, user)
2115 |> get("/api/v1/domain_blocks")
2117 domain_blocks = json_response(conn, 200)
2119 assert "bad.site" in domain_blocks
2120 assert "even.worse.site" in domain_blocks
2123 test "unimplemented follow_requests, blocks, domain blocks" do
2124 user = insert(:user)
2126 ["blocks", "domain_blocks", "follow_requests"]
2127 |> Enum.each(fn endpoint ->
2130 |> assign(:user, user)
2131 |> get("/api/v1/#{endpoint}")
2133 assert [] = json_response(conn, 200)
2137 test "account search", %{conn: conn} do
2138 user = insert(:user)
2139 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2140 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2144 |> assign(:user, user)
2145 |> get("/api/v1/accounts/search", %{"q" => "shp"})
2146 |> json_response(200)
2148 result_ids = for result <- results, do: result["acct"]
2150 assert user_two.nickname in result_ids
2151 assert user_three.nickname in result_ids
2155 |> assign(:user, user)
2156 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
2157 |> json_response(200)
2159 result_ids = for result <- results, do: result["acct"]
2161 assert user_three.nickname in result_ids
2164 test "search", %{conn: conn} do
2165 user = insert(:user)
2166 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2167 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2169 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
2172 CommonAPI.post(user, %{
2173 "status" => "This is about 2hu, but private",
2174 "visibility" => "private"
2177 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
2181 |> get("/api/v1/search", %{"q" => "2hu"})
2183 assert results = json_response(conn, 200)
2185 [account | _] = results["accounts"]
2186 assert account["id"] == to_string(user_three.id)
2188 assert results["hashtags"] == []
2190 [status] = results["statuses"]
2191 assert status["id"] == to_string(activity.id)
2194 test "search fetches remote statuses", %{conn: conn} do
2198 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
2200 assert results = json_response(conn, 200)
2202 [status] = results["statuses"]
2203 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
2207 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
2209 CommonAPI.post(insert(:user), %{
2210 "status" => "This is about 2hu, but private",
2211 "visibility" => "private"
2217 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2219 assert results = json_response(conn, 200)
2221 [] = results["statuses"]
2225 test "search fetches remote accounts", %{conn: conn} do
2226 user = insert(:user)
2230 |> assign(:user, user)
2231 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2233 assert results = json_response(conn, 200)
2234 [account] = results["accounts"]
2235 assert account["acct"] == "shp@social.heldscal.la"
2238 test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
2241 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
2243 assert results = json_response(conn, 200)
2244 assert [] == results["accounts"]
2247 test "returns the favorites of a user", %{conn: conn} do
2248 user = insert(:user)
2249 other_user = insert(:user)
2251 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2252 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2254 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2258 |> assign(:user, user)
2259 |> get("/api/v1/favourites")
2261 assert [status] = json_response(first_conn, 200)
2262 assert status["id"] == to_string(activity.id)
2264 assert [{"link", _link_header}] =
2265 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2267 # Honours query params
2268 {:ok, second_activity} =
2269 CommonAPI.post(other_user, %{
2271 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2274 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2276 last_like = status["id"]
2280 |> assign(:user, user)
2281 |> get("/api/v1/favourites?since_id=#{last_like}")
2283 assert [second_status] = json_response(second_conn, 200)
2284 assert second_status["id"] == to_string(second_activity.id)
2288 |> assign(:user, user)
2289 |> get("/api/v1/favourites?limit=0")
2291 assert [] = json_response(third_conn, 200)
2294 describe "getting favorites timeline of specified user" do
2296 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2297 [current_user: current_user, user: user]
2300 test "returns list of statuses favorited by specified user", %{
2302 current_user: current_user,
2305 [activity | _] = insert_pair(:note_activity)
2306 CommonAPI.favorite(activity.id, user)
2310 |> assign(:user, current_user)
2311 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2312 |> json_response(:ok)
2316 assert length(response) == 1
2317 assert like["id"] == activity.id
2320 test "returns favorites for specified user_id when user is not logged in", %{
2324 activity = insert(:note_activity)
2325 CommonAPI.favorite(activity.id, user)
2329 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2330 |> json_response(:ok)
2332 assert length(response) == 1
2335 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2337 current_user: current_user,
2341 CommonAPI.post(current_user, %{
2342 "status" => "Hi @#{user.nickname}!",
2343 "visibility" => "direct"
2346 CommonAPI.favorite(direct.id, user)
2350 |> assign(:user, current_user)
2351 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2352 |> json_response(:ok)
2354 assert length(response) == 1
2356 anonymous_response =
2358 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2359 |> json_response(:ok)
2361 assert Enum.empty?(anonymous_response)
2364 test "does not return others' favorited DM when user is not one of recipients", %{
2366 current_user: current_user,
2369 user_two = insert(:user)
2372 CommonAPI.post(user_two, %{
2373 "status" => "Hi @#{user.nickname}!",
2374 "visibility" => "direct"
2377 CommonAPI.favorite(direct.id, user)
2381 |> assign(:user, current_user)
2382 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2383 |> json_response(:ok)
2385 assert Enum.empty?(response)
2388 test "paginates favorites using since_id and max_id", %{
2390 current_user: current_user,
2393 activities = insert_list(10, :note_activity)
2395 Enum.each(activities, fn activity ->
2396 CommonAPI.favorite(activity.id, user)
2399 third_activity = Enum.at(activities, 2)
2400 seventh_activity = Enum.at(activities, 6)
2404 |> assign(:user, current_user)
2405 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2406 since_id: third_activity.id,
2407 max_id: seventh_activity.id
2409 |> json_response(:ok)
2411 assert length(response) == 3
2412 refute third_activity in response
2413 refute seventh_activity in response
2416 test "limits favorites using limit parameter", %{
2418 current_user: current_user,
2422 |> insert_list(:note_activity)
2423 |> Enum.each(fn activity ->
2424 CommonAPI.favorite(activity.id, user)
2429 |> assign(:user, current_user)
2430 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2431 |> json_response(:ok)
2433 assert length(response) == 3
2436 test "returns empty response when user does not have any favorited statuses", %{
2438 current_user: current_user,
2443 |> assign(:user, current_user)
2444 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2445 |> json_response(:ok)
2447 assert Enum.empty?(response)
2450 test "returns 404 error when specified user is not exist", %{conn: conn} do
2451 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2453 assert json_response(conn, 404) == %{"error" => "Record not found"}
2456 test "returns 403 error when user has hidden own favorites", %{
2458 current_user: current_user
2460 user = insert(:user, %{info: %{hide_favorites: true}})
2461 activity = insert(:note_activity)
2462 CommonAPI.favorite(activity.id, user)
2466 |> assign(:user, current_user)
2467 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2469 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2472 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2473 user = insert(:user)
2474 activity = insert(:note_activity)
2475 CommonAPI.favorite(activity.id, user)
2479 |> assign(:user, current_user)
2480 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2482 assert user.info.hide_favorites
2483 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2487 describe "updating credentials" do
2488 test "sets user settings in a generic way", %{conn: conn} do
2489 user = insert(:user)
2493 |> assign(:user, user)
2494 |> patch("/api/v1/accounts/update_credentials", %{
2495 "pleroma_settings_store" => %{
2502 assert user = json_response(res_conn, 200)
2503 assert user["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}}
2505 user = Repo.get(User, user["id"])
2509 |> assign(:user, user)
2510 |> patch("/api/v1/accounts/update_credentials", %{
2511 "pleroma_settings_store" => %{
2518 assert user = json_response(res_conn, 200)
2520 assert user["pleroma"]["settings_store"] ==
2522 "pleroma_fe" => %{"theme" => "bla"},
2523 "masto_fe" => %{"theme" => "bla"}
2526 user = Repo.get(User, user["id"])
2530 |> assign(:user, user)
2531 |> patch("/api/v1/accounts/update_credentials", %{
2532 "pleroma_settings_store" => %{
2539 assert user = json_response(res_conn, 200)
2541 assert user["pleroma"]["settings_store"] ==
2543 "pleroma_fe" => %{"theme" => "bla"},
2544 "masto_fe" => %{"theme" => "blub"}
2548 test "updates the user's bio", %{conn: conn} do
2549 user = insert(:user)
2550 user2 = insert(:user)
2554 |> assign(:user, user)
2555 |> patch("/api/v1/accounts/update_credentials", %{
2556 "note" => "I drink #cofe with @#{user2.nickname}"
2559 assert user = json_response(conn, 200)
2561 assert user["note"] ==
2562 ~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=") <>
2564 ~s(" class="u-url mention" href=") <>
2565 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2568 test "updates the user's locking status", %{conn: conn} do
2569 user = insert(:user)
2573 |> assign(:user, user)
2574 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2576 assert user = json_response(conn, 200)
2577 assert user["locked"] == true
2580 test "updates the user's default scope", %{conn: conn} do
2581 user = insert(:user)
2585 |> assign(:user, user)
2586 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2588 assert user = json_response(conn, 200)
2589 assert user["source"]["privacy"] == "cofe"
2592 test "updates the user's hide_followers status", %{conn: conn} do
2593 user = insert(:user)
2597 |> assign(:user, user)
2598 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2600 assert user = json_response(conn, 200)
2601 assert user["pleroma"]["hide_followers"] == true
2604 test "updates the user's skip_thread_containment option", %{conn: conn} do
2605 user = insert(:user)
2609 |> assign(:user, user)
2610 |> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"})
2611 |> json_response(200)
2613 assert response["pleroma"]["skip_thread_containment"] == true
2614 assert refresh_record(user).info.skip_thread_containment
2617 test "updates the user's hide_follows status", %{conn: conn} do
2618 user = insert(:user)
2622 |> assign(:user, user)
2623 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2625 assert user = json_response(conn, 200)
2626 assert user["pleroma"]["hide_follows"] == true
2629 test "updates the user's hide_favorites status", %{conn: conn} do
2630 user = insert(:user)
2634 |> assign(:user, user)
2635 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2637 assert user = json_response(conn, 200)
2638 assert user["pleroma"]["hide_favorites"] == true
2641 test "updates the user's show_role status", %{conn: conn} do
2642 user = insert(:user)
2646 |> assign(:user, user)
2647 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2649 assert user = json_response(conn, 200)
2650 assert user["source"]["pleroma"]["show_role"] == false
2653 test "updates the user's no_rich_text status", %{conn: conn} do
2654 user = insert(:user)
2658 |> assign(:user, user)
2659 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2661 assert user = json_response(conn, 200)
2662 assert user["source"]["pleroma"]["no_rich_text"] == true
2665 test "updates the user's name", %{conn: conn} do
2666 user = insert(:user)
2670 |> assign(:user, user)
2671 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2673 assert user = json_response(conn, 200)
2674 assert user["display_name"] == "markorepairs"
2677 test "updates the user's avatar", %{conn: conn} do
2678 user = insert(:user)
2680 new_avatar = %Plug.Upload{
2681 content_type: "image/jpg",
2682 path: Path.absname("test/fixtures/image.jpg"),
2683 filename: "an_image.jpg"
2688 |> assign(:user, user)
2689 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2691 assert user_response = json_response(conn, 200)
2692 assert user_response["avatar"] != User.avatar_url(user)
2695 test "updates the user's banner", %{conn: conn} do
2696 user = insert(:user)
2698 new_header = %Plug.Upload{
2699 content_type: "image/jpg",
2700 path: Path.absname("test/fixtures/image.jpg"),
2701 filename: "an_image.jpg"
2706 |> assign(:user, user)
2707 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2709 assert user_response = json_response(conn, 200)
2710 assert user_response["header"] != User.banner_url(user)
2713 test "requires 'write' permission", %{conn: conn} do
2714 token1 = insert(:oauth_token, scopes: ["read"])
2715 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2717 for token <- [token1, token2] do
2720 |> put_req_header("authorization", "Bearer #{token.token}")
2721 |> patch("/api/v1/accounts/update_credentials", %{})
2723 if token == token1 do
2724 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2726 assert json_response(conn, 200)
2731 test "updates profile emojos", %{conn: conn} do
2732 user = insert(:user)
2734 note = "*sips :blank:*"
2735 name = "I am :firefox:"
2739 |> assign(:user, user)
2740 |> patch("/api/v1/accounts/update_credentials", %{
2742 "display_name" => name
2745 assert json_response(conn, 200)
2749 |> get("/api/v1/accounts/#{user.id}")
2751 assert user = json_response(conn, 200)
2753 assert user["note"] == note
2754 assert user["display_name"] == name
2755 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2759 test "get instance information", %{conn: conn} do
2760 conn = get(conn, "/api/v1/instance")
2761 assert result = json_response(conn, 200)
2763 email = Pleroma.Config.get([:instance, :email])
2764 # Note: not checking for "max_toot_chars" since it's optional
2770 "email" => from_config_email,
2772 "streaming_api" => _
2777 "registrations" => _,
2781 assert email == from_config_email
2784 test "get instance stats", %{conn: conn} do
2785 user = insert(:user, %{local: true})
2787 user2 = insert(:user, %{local: true})
2788 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2790 insert(:user, %{local: false, nickname: "u@peer1.com"})
2791 insert(:user, %{local: false, nickname: "u@peer2.com"})
2793 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2795 # Stats should count users with missing or nil `info.deactivated` value
2796 user = User.get_cached_by_id(user.id)
2797 info_change = Changeset.change(user.info, %{deactivated: nil})
2801 |> Changeset.change()
2802 |> Changeset.put_embed(:info, info_change)
2803 |> User.update_and_set_cache()
2805 Pleroma.Stats.update_stats()
2807 conn = get(conn, "/api/v1/instance")
2809 assert result = json_response(conn, 200)
2811 stats = result["stats"]
2814 assert stats["user_count"] == 1
2815 assert stats["status_count"] == 1
2816 assert stats["domain_count"] == 2
2819 test "get peers", %{conn: conn} do
2820 insert(:user, %{local: false, nickname: "u@peer1.com"})
2821 insert(:user, %{local: false, nickname: "u@peer2.com"})
2823 Pleroma.Stats.update_stats()
2825 conn = get(conn, "/api/v1/instance/peers")
2827 assert result = json_response(conn, 200)
2829 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2832 test "put settings", %{conn: conn} do
2833 user = insert(:user)
2837 |> assign(:user, user)
2838 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2840 assert _result = json_response(conn, 200)
2842 user = User.get_cached_by_ap_id(user.ap_id)
2843 assert user.info.settings == %{"programming" => "socks"}
2846 describe "pinned statuses" do
2848 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2850 user = insert(:user)
2851 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2853 [user: user, activity: activity]
2856 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2857 {:ok, _} = CommonAPI.pin(activity.id, user)
2861 |> assign(:user, user)
2862 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2863 |> json_response(200)
2865 id_str = to_string(activity.id)
2867 assert [%{"id" => ^id_str, "pinned" => true}] = result
2870 test "pin status", %{conn: conn, user: user, activity: activity} do
2871 id_str = to_string(activity.id)
2873 assert %{"id" => ^id_str, "pinned" => true} =
2875 |> assign(:user, user)
2876 |> post("/api/v1/statuses/#{activity.id}/pin")
2877 |> json_response(200)
2879 assert [%{"id" => ^id_str, "pinned" => true}] =
2881 |> assign(:user, user)
2882 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2883 |> json_response(200)
2886 test "unpin status", %{conn: conn, user: user, activity: activity} do
2887 {:ok, _} = CommonAPI.pin(activity.id, user)
2889 id_str = to_string(activity.id)
2890 user = refresh_record(user)
2892 assert %{"id" => ^id_str, "pinned" => false} =
2894 |> assign(:user, user)
2895 |> post("/api/v1/statuses/#{activity.id}/unpin")
2896 |> json_response(200)
2900 |> assign(:user, user)
2901 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2902 |> json_response(200)
2905 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2906 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2908 id_str_one = to_string(activity_one.id)
2910 assert %{"id" => ^id_str_one, "pinned" => true} =
2912 |> assign(:user, user)
2913 |> post("/api/v1/statuses/#{id_str_one}/pin")
2914 |> json_response(200)
2916 user = refresh_record(user)
2918 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2920 |> assign(:user, user)
2921 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2922 |> json_response(400)
2928 Pleroma.Config.put([:rich_media, :enabled], true)
2931 Pleroma.Config.put([:rich_media, :enabled], false)
2934 user = insert(:user)
2938 test "returns rich-media card", %{conn: conn, user: user} do
2939 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2942 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2943 "provider_name" => "www.imdb.com",
2944 "provider_url" => "http://www.imdb.com",
2945 "title" => "The Rock",
2947 "url" => "http://www.imdb.com/title/tt0117500/",
2949 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2952 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2953 "title" => "The Rock",
2954 "type" => "video.movie",
2955 "url" => "http://www.imdb.com/title/tt0117500/",
2957 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2964 |> get("/api/v1/statuses/#{activity.id}/card")
2965 |> json_response(200)
2967 assert response == card_data
2969 # works with private posts
2971 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2975 |> assign(:user, user)
2976 |> get("/api/v1/statuses/#{activity.id}/card")
2977 |> json_response(200)
2979 assert response_two == card_data
2982 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2983 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp-missing-data"})
2987 |> get("/api/v1/statuses/#{activity.id}/card")
2988 |> json_response(:ok)
2990 assert response == %{
2992 "title" => "Pleroma",
2993 "description" => "",
2995 "provider_name" => "pleroma.social",
2996 "provider_url" => "https://pleroma.social",
2997 "url" => "https://pleroma.social/",
3000 "title" => "Pleroma",
3001 "type" => "website",
3002 "url" => "https://pleroma.social/"
3010 user = insert(:user)
3011 for_user = insert(:user)
3014 CommonAPI.post(user, %{
3015 "status" => "heweoo?"
3019 CommonAPI.post(user, %{
3020 "status" => "heweoo!"
3025 |> assign(:user, for_user)
3026 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
3028 assert json_response(response1, 200)["bookmarked"] == true
3032 |> assign(:user, for_user)
3033 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
3035 assert json_response(response2, 200)["bookmarked"] == true
3039 |> assign(:user, for_user)
3040 |> get("/api/v1/bookmarks")
3042 assert [json_response(response2, 200), json_response(response1, 200)] ==
3043 json_response(bookmarks, 200)
3047 |> assign(:user, for_user)
3048 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
3050 assert json_response(response1, 200)["bookmarked"] == false
3054 |> assign(:user, for_user)
3055 |> get("/api/v1/bookmarks")
3057 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
3060 describe "conversation muting" do
3062 user = insert(:user)
3063 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
3065 [user: user, activity: activity]
3068 test "mute conversation", %{conn: conn, user: user, activity: activity} do
3069 id_str = to_string(activity.id)
3071 assert %{"id" => ^id_str, "muted" => true} =
3073 |> assign(:user, user)
3074 |> post("/api/v1/statuses/#{activity.id}/mute")
3075 |> json_response(200)
3078 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
3079 {:ok, _} = CommonAPI.add_mute(user, activity)
3081 id_str = to_string(activity.id)
3082 user = refresh_record(user)
3084 assert %{"id" => ^id_str, "muted" => false} =
3086 |> assign(:user, user)
3087 |> post("/api/v1/statuses/#{activity.id}/unmute")
3088 |> json_response(200)
3092 describe "reports" do
3094 reporter = insert(:user)
3095 target_user = insert(:user)
3097 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
3099 [reporter: reporter, target_user: target_user, activity: activity]
3102 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
3103 assert %{"action_taken" => false, "id" => _} =
3105 |> assign(:user, reporter)
3106 |> post("/api/v1/reports", %{"account_id" => target_user.id})
3107 |> json_response(200)
3110 test "submit a report with statuses and comment", %{
3113 target_user: target_user,
3116 assert %{"action_taken" => false, "id" => _} =
3118 |> assign(:user, reporter)
3119 |> post("/api/v1/reports", %{
3120 "account_id" => target_user.id,
3121 "status_ids" => [activity.id],
3122 "comment" => "bad status!"
3124 |> json_response(200)
3127 test "account_id is required", %{
3132 assert %{"error" => "Valid `account_id` required"} =
3134 |> assign(:user, reporter)
3135 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3136 |> json_response(400)
3139 test "comment must be up to the size specified in the config", %{
3142 target_user: target_user
3144 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
3145 comment = String.pad_trailing("a", max_size + 1, "a")
3147 error = %{"error" => "Comment must be up to #{max_size} characters"}
3151 |> assign(:user, reporter)
3152 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3153 |> json_response(400)
3157 describe "link headers" do
3158 test "preserves parameters in link headers", %{conn: conn} do
3159 user = insert(:user)
3160 other_user = insert(:user)
3163 CommonAPI.post(other_user, %{
3164 "status" => "hi @#{user.nickname}",
3165 "visibility" => "public"
3169 CommonAPI.post(other_user, %{
3170 "status" => "hi @#{user.nickname}",
3171 "visibility" => "public"
3174 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3175 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3179 |> assign(:user, user)
3180 |> get("/api/v1/notifications", %{media_only: true})
3182 assert [link_header] = get_resp_header(conn, "link")
3183 assert link_header =~ ~r/media_only=true/
3184 assert link_header =~ ~r/min_id=#{notification2.id}/
3185 assert link_header =~ ~r/max_id=#{notification1.id}/
3189 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3190 # Need to set an old-style integer ID to reproduce the problem
3191 # (these are no longer assigned to new accounts but were preserved
3192 # for existing accounts during the migration to flakeIDs)
3193 user_one = insert(:user, %{id: 1212})
3194 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3198 |> get("/api/v1/accounts/#{user_one.id}")
3202 |> get("/api/v1/accounts/#{user_two.nickname}")
3206 |> get("/api/v1/accounts/#{user_two.id}")
3208 acc_one = json_response(resp_one, 200)
3209 acc_two = json_response(resp_two, 200)
3210 acc_three = json_response(resp_three, 200)
3211 refute acc_one == acc_two
3212 assert acc_two == acc_three
3215 describe "custom emoji" do
3216 test "with tags", %{conn: conn} do
3219 |> get("/api/v1/custom_emojis")
3220 |> json_response(200)
3222 assert Map.has_key?(emoji, "shortcode")
3223 assert Map.has_key?(emoji, "static_url")
3224 assert Map.has_key?(emoji, "tags")
3225 assert is_list(emoji["tags"])
3226 assert Map.has_key?(emoji, "url")
3227 assert Map.has_key?(emoji, "visible_in_picker")
3231 describe "index/2 redirections" do
3232 setup %{conn: conn} do
3236 signing_salt: "cooldude"
3241 |> Plug.Session.call(Plug.Session.init(session_opts))
3244 test_path = "/web/statuses/test"
3245 %{conn: conn, path: test_path}
3248 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3249 conn = get(conn, path)
3251 assert conn.status == 302
3252 assert redirected_to(conn) == "/web/login"
3255 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3256 token = insert(:oauth_token)
3260 |> assign(:user, token.user)
3261 |> put_session(:oauth_token, token.token)
3264 assert conn.status == 200
3267 test "saves referer path to session", %{conn: conn, path: path} do
3268 conn = get(conn, path)
3269 return_to = Plug.Conn.get_session(conn, :return_to)
3271 assert return_to == path
3274 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3275 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3276 auth = insert(:oauth_authorization, app: app)
3280 |> put_session(:return_to, path)
3281 |> get("/web/login", %{code: auth.token})
3283 assert conn.status == 302
3284 assert redirected_to(conn) == path
3287 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3288 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3289 auth = insert(:oauth_authorization, app: app)
3291 conn = get(conn, "/web/login", %{code: auth.token})
3293 assert conn.status == 302
3294 assert redirected_to(conn) == "/web/getting-started"
3298 describe "scheduled activities" do
3299 test "creates a scheduled activity", %{conn: conn} do
3300 user = insert(:user)
3301 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3305 |> assign(:user, user)
3306 |> post("/api/v1/statuses", %{
3307 "status" => "scheduled",
3308 "scheduled_at" => scheduled_at
3311 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3312 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3313 assert [] == Repo.all(Activity)
3316 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3317 user = insert(:user)
3318 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3320 file = %Plug.Upload{
3321 content_type: "image/jpg",
3322 path: Path.absname("test/fixtures/image.jpg"),
3323 filename: "an_image.jpg"
3326 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3330 |> assign(:user, user)
3331 |> post("/api/v1/statuses", %{
3332 "media_ids" => [to_string(upload.id)],
3333 "status" => "scheduled",
3334 "scheduled_at" => scheduled_at
3337 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3338 assert %{"type" => "image"} = media_attachment
3341 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3343 user = insert(:user)
3346 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3350 |> assign(:user, user)
3351 |> post("/api/v1/statuses", %{
3352 "status" => "not scheduled",
3353 "scheduled_at" => scheduled_at
3356 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3357 assert [] == Repo.all(ScheduledActivity)
3360 test "returns error when daily user limit is exceeded", %{conn: conn} do
3361 user = insert(:user)
3364 NaiveDateTime.utc_now()
3365 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3366 |> NaiveDateTime.to_iso8601()
3368 attrs = %{params: %{}, scheduled_at: today}
3369 {:ok, _} = ScheduledActivity.create(user, attrs)
3370 {:ok, _} = ScheduledActivity.create(user, attrs)
3374 |> assign(:user, user)
3375 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3377 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3380 test "returns error when total user limit is exceeded", %{conn: conn} do
3381 user = insert(:user)
3384 NaiveDateTime.utc_now()
3385 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3386 |> NaiveDateTime.to_iso8601()
3389 NaiveDateTime.utc_now()
3390 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3391 |> NaiveDateTime.to_iso8601()
3393 attrs = %{params: %{}, scheduled_at: today}
3394 {:ok, _} = ScheduledActivity.create(user, attrs)
3395 {:ok, _} = ScheduledActivity.create(user, attrs)
3396 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3400 |> assign(:user, user)
3401 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3403 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3406 test "shows scheduled activities", %{conn: conn} do
3407 user = insert(:user)
3408 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3409 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3410 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3411 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3415 |> assign(:user, user)
3420 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3422 result = json_response(conn_res, 200)
3423 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3428 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3430 result = json_response(conn_res, 200)
3431 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3436 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3438 result = json_response(conn_res, 200)
3439 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3442 test "shows a scheduled activity", %{conn: conn} do
3443 user = insert(:user)
3444 scheduled_activity = insert(:scheduled_activity, user: user)
3448 |> assign(:user, user)
3449 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3451 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3452 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3456 |> assign(:user, user)
3457 |> get("/api/v1/scheduled_statuses/404")
3459 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3462 test "updates a scheduled activity", %{conn: conn} do
3463 user = insert(:user)
3464 scheduled_activity = insert(:scheduled_activity, user: user)
3467 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3471 |> assign(:user, user)
3472 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3473 scheduled_at: new_scheduled_at
3476 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3477 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3481 |> assign(:user, user)
3482 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3484 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3487 test "deletes a scheduled activity", %{conn: conn} do
3488 user = insert(:user)
3489 scheduled_activity = insert(:scheduled_activity, user: user)
3493 |> assign(:user, user)
3494 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3496 assert %{} = json_response(res_conn, 200)
3497 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3501 |> assign(:user, user)
3502 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3504 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3508 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3509 user1 = insert(:user)
3510 user2 = insert(:user)
3511 user3 = insert(:user)
3513 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3515 # Reply to status from another user
3518 |> assign(:user, user2)
3519 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3521 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3523 activity = Activity.get_by_id_with_object(id)
3525 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3526 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3528 # Reblog from the third user
3531 |> assign(:user, user3)
3532 |> post("/api/v1/statuses/#{activity.id}/reblog")
3534 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3535 json_response(conn2, 200)
3537 assert to_string(activity.id) == id
3539 # Getting third user status
3542 |> assign(:user, user3)
3543 |> get("api/v1/timelines/home")
3545 [reblogged_activity] = json_response(conn3, 200)
3547 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3549 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3550 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3553 describe "create account by app" do
3554 test "Account registration via Application", %{conn: conn} do
3557 |> post("/api/v1/apps", %{
3558 client_name: "client_name",
3559 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3560 scopes: "read, write, follow"
3564 "client_id" => client_id,
3565 "client_secret" => client_secret,
3567 "name" => "client_name",
3568 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3571 } = json_response(conn, 200)
3575 |> post("/oauth/token", %{
3576 grant_type: "client_credentials",
3577 client_id: client_id,
3578 client_secret: client_secret
3581 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3582 json_response(conn, 200)
3585 token_from_db = Repo.get_by(Token, token: token)
3586 assert token_from_db
3588 assert scope == "read write follow"
3592 |> put_req_header("authorization", "Bearer " <> token)
3593 |> post("/api/v1/accounts", %{
3595 email: "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
3615 test "rate limit", %{conn: conn} do
3616 app_token = insert(:oauth_token, user: nil)
3619 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3620 |> Map.put(:remote_ip, {15, 15, 15, 15})
3625 |> post("/api/v1/accounts", %{
3626 username: "#{i}lain",
3627 email: "#{i}lain@example.org",
3628 password: "PlzDontHackLain",
3633 "access_token" => token,
3634 "created_at" => _created_at,
3636 "token_type" => "Bearer"
3637 } = json_response(conn, 200)
3639 token_from_db = Repo.get_by(Token, token: token)
3640 assert token_from_db
3641 token_from_db = Repo.preload(token_from_db, :user)
3642 assert token_from_db.user
3644 assert token_from_db.user.info.confirmation_pending
3649 |> post("/api/v1/accounts", %{
3651 email: "6lain@example.org",
3652 password: "PlzDontHackLain",
3656 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3660 describe "GET /api/v1/polls/:id" do
3661 test "returns poll entity for object id", %{conn: conn} do
3662 user = insert(:user)
3665 CommonAPI.post(user, %{
3666 "status" => "Pleroma does",
3667 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3670 object = Object.normalize(activity)
3674 |> assign(:user, user)
3675 |> get("/api/v1/polls/#{object.id}")
3677 response = json_response(conn, 200)
3679 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3682 test "does not expose polls for private statuses", %{conn: conn} do
3683 user = insert(:user)
3684 other_user = insert(:user)
3687 CommonAPI.post(user, %{
3688 "status" => "Pleroma does",
3689 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3690 "visibility" => "private"
3693 object = Object.normalize(activity)
3697 |> assign(:user, other_user)
3698 |> get("/api/v1/polls/#{object.id}")
3700 assert json_response(conn, 404)
3704 describe "POST /api/v1/polls/:id/votes" do
3705 test "votes are added to the poll", %{conn: conn} do
3706 user = insert(:user)
3707 other_user = insert(:user)
3710 CommonAPI.post(user, %{
3711 "status" => "A very delicious sandwich",
3713 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3719 object = Object.normalize(activity)
3723 |> assign(:user, other_user)
3724 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3726 assert json_response(conn, 200)
3727 object = Object.get_by_id(object.id)
3729 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3734 test "author can't vote", %{conn: conn} do
3735 user = insert(:user)
3738 CommonAPI.post(user, %{
3739 "status" => "Am I cute?",
3740 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3743 object = Object.normalize(activity)
3746 |> assign(:user, user)
3747 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3748 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3750 object = Object.get_by_id(object.id)
3752 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3755 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3756 user = insert(:user)
3757 other_user = insert(:user)
3760 CommonAPI.post(user, %{
3761 "status" => "The glass is",
3762 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3765 object = Object.normalize(activity)
3768 |> assign(:user, other_user)
3769 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3770 |> json_response(422) == %{"error" => "Too many choices"}
3772 object = Object.get_by_id(object.id)
3774 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->