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 test "posting a sensitive status", %{conn: conn} do
154 |> assign(:user, user)
155 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
157 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
158 assert Activity.get_by_id(id)
161 test "posting a fake status", %{conn: conn} do
166 |> assign(:user, user)
167 |> post("/api/v1/statuses", %{
169 "\"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"
172 real_status = json_response(real_conn, 200)
175 assert Object.get_by_ap_id(real_status["uri"])
179 |> Map.put("id", nil)
180 |> Map.put("url", nil)
181 |> Map.put("uri", nil)
182 |> Map.put("created_at", nil)
183 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
187 |> assign(:user, user)
188 |> post("/api/v1/statuses", %{
190 "\"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",
194 fake_status = json_response(fake_conn, 200)
197 refute Object.get_by_ap_id(fake_status["uri"])
201 |> Map.put("id", nil)
202 |> Map.put("url", nil)
203 |> Map.put("uri", nil)
204 |> Map.put("created_at", nil)
205 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
207 assert real_status == fake_status
210 test "posting a status with OGP link preview", %{conn: conn} do
211 Pleroma.Config.put([:rich_media, :enabled], true)
216 |> assign(:user, user)
217 |> post("/api/v1/statuses", %{
218 "status" => "http://example.com/ogp"
221 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
222 assert Activity.get_by_id(id)
223 Pleroma.Config.put([:rich_media, :enabled], false)
226 test "posting a direct status", %{conn: conn} do
227 user1 = insert(:user)
228 user2 = insert(:user)
229 content = "direct cofe @#{user2.nickname}"
233 |> assign(:user, user1)
234 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
236 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
237 assert activity = Activity.get_by_id(id)
238 assert activity.recipients == [user2.ap_id, user1.ap_id]
239 assert activity.data["to"] == [user2.ap_id]
240 assert activity.data["cc"] == []
243 test "direct timeline", %{conn: conn} do
244 user_one = insert(:user)
245 user_two = insert(:user)
247 {:ok, user_two} = User.follow(user_two, user_one)
250 CommonAPI.post(user_one, %{
251 "status" => "Hi @#{user_two.nickname}!",
252 "visibility" => "direct"
255 {:ok, _follower_only} =
256 CommonAPI.post(user_one, %{
257 "status" => "Hi @#{user_two.nickname}!",
258 "visibility" => "private"
261 # Only direct should be visible here
264 |> assign(:user, user_two)
265 |> get("api/v1/timelines/direct")
267 [status] = json_response(res_conn, 200)
269 assert %{"visibility" => "direct"} = status
270 assert status["url"] != direct.data["id"]
272 # User should be able to see his own direct message
275 |> assign(:user, user_one)
276 |> get("api/v1/timelines/direct")
278 [status] = json_response(res_conn, 200)
280 assert %{"visibility" => "direct"} = status
282 # Both should be visible here
285 |> assign(:user, user_two)
286 |> get("api/v1/timelines/home")
288 [_s1, _s2] = json_response(res_conn, 200)
291 Enum.each(1..20, fn _ ->
293 CommonAPI.post(user_one, %{
294 "status" => "Hi @#{user_two.nickname}!",
295 "visibility" => "direct"
301 |> assign(:user, user_two)
302 |> get("api/v1/timelines/direct")
304 statuses = json_response(res_conn, 200)
305 assert length(statuses) == 20
309 |> assign(:user, user_two)
310 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
312 [status] = json_response(res_conn, 200)
314 assert status["url"] != direct.data["id"]
317 test "Conversations", %{conn: conn} do
318 user_one = insert(:user)
319 user_two = insert(:user)
321 {:ok, user_two} = User.follow(user_two, user_one)
324 CommonAPI.post(user_one, %{
325 "status" => "Hi @#{user_two.nickname}!",
326 "visibility" => "direct"
329 {:ok, _follower_only} =
330 CommonAPI.post(user_one, %{
331 "status" => "Hi @#{user_two.nickname}!",
332 "visibility" => "private"
337 |> assign(:user, user_one)
338 |> get("/api/v1/conversations")
340 assert response = json_response(res_conn, 200)
345 "accounts" => res_accounts,
346 "last_status" => res_last_status,
351 assert length(res_accounts) == 2
352 assert is_binary(res_id)
353 assert unread == true
354 assert res_last_status["id"] == direct.id
356 # Apparently undocumented API endpoint
359 |> assign(:user, user_one)
360 |> post("/api/v1/conversations/#{res_id}/read")
362 assert response = json_response(res_conn, 200)
363 assert length(response["accounts"]) == 2
364 assert response["last_status"]["id"] == direct.id
365 assert response["unread"] == false
367 # (vanilla) Mastodon frontend behaviour
370 |> assign(:user, user_one)
371 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
373 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
376 test "doesn't include DMs from blocked users", %{conn: conn} do
377 blocker = insert(:user)
378 blocked = insert(:user)
380 {:ok, blocker} = User.block(blocker, blocked)
382 {:ok, _blocked_direct} =
383 CommonAPI.post(blocked, %{
384 "status" => "Hi @#{blocker.nickname}!",
385 "visibility" => "direct"
389 CommonAPI.post(user, %{
390 "status" => "Hi @#{blocker.nickname}!",
391 "visibility" => "direct"
396 |> assign(:user, user)
397 |> get("api/v1/timelines/direct")
399 [status] = json_response(res_conn, 200)
400 assert status["id"] == direct.id
403 test "replying to a status", %{conn: conn} do
406 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
410 |> assign(:user, user)
411 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
413 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
415 activity = Activity.get_by_id(id)
417 assert activity.data["context"] == replied_to.data["context"]
418 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
421 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
426 |> assign(:user, user)
427 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
429 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
431 activity = Activity.get_by_id(id)
436 test "verify_credentials", %{conn: conn} do
441 |> assign(:user, user)
442 |> get("/api/v1/accounts/verify_credentials")
444 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
445 assert id == to_string(user.id)
448 test "verify_credentials default scope unlisted", %{conn: conn} do
449 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
453 |> assign(:user, user)
454 |> get("/api/v1/accounts/verify_credentials")
456 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
457 assert id == to_string(user.id)
460 test "apps/verify_credentials", %{conn: conn} do
461 token = insert(:oauth_token)
465 |> assign(:user, token.user)
466 |> assign(:token, token)
467 |> get("/api/v1/apps/verify_credentials")
469 app = Repo.preload(token, :app).app
472 "name" => app.client_name,
473 "website" => app.website,
474 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
477 assert expected == json_response(conn, 200)
480 test "creates an oauth app", %{conn: conn} do
482 app_attrs = build(:oauth_app)
486 |> assign(:user, user)
487 |> post("/api/v1/apps", %{
488 client_name: app_attrs.client_name,
489 redirect_uris: app_attrs.redirect_uris
492 [app] = Repo.all(App)
495 "name" => app.client_name,
496 "website" => app.website,
497 "client_id" => app.client_id,
498 "client_secret" => app.client_secret,
499 "id" => app.id |> to_string(),
500 "redirect_uri" => app.redirect_uris,
501 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
504 assert expected == json_response(conn, 200)
507 test "get a status", %{conn: conn} do
508 activity = insert(:note_activity)
512 |> get("/api/v1/statuses/#{activity.id}")
514 assert %{"id" => id} = json_response(conn, 200)
515 assert id == to_string(activity.id)
518 describe "deleting a status" do
519 test "when you created it", %{conn: conn} do
520 activity = insert(:note_activity)
521 author = User.get_cached_by_ap_id(activity.data["actor"])
525 |> assign(:user, author)
526 |> delete("/api/v1/statuses/#{activity.id}")
528 assert %{} = json_response(conn, 200)
530 refute Activity.get_by_id(activity.id)
533 test "when you didn't create it", %{conn: conn} do
534 activity = insert(:note_activity)
539 |> assign(:user, user)
540 |> delete("/api/v1/statuses/#{activity.id}")
542 assert %{"error" => _} = json_response(conn, 403)
544 assert Activity.get_by_id(activity.id) == activity
547 test "when you're an admin or moderator", %{conn: conn} do
548 activity1 = insert(:note_activity)
549 activity2 = insert(:note_activity)
550 admin = insert(:user, info: %{is_admin: true})
551 moderator = insert(:user, info: %{is_moderator: true})
555 |> assign(:user, admin)
556 |> delete("/api/v1/statuses/#{activity1.id}")
558 assert %{} = json_response(res_conn, 200)
562 |> assign(:user, moderator)
563 |> delete("/api/v1/statuses/#{activity2.id}")
565 assert %{} = json_response(res_conn, 200)
567 refute Activity.get_by_id(activity1.id)
568 refute Activity.get_by_id(activity2.id)
572 describe "filters" do
573 test "creating a filter", %{conn: conn} do
576 filter = %Pleroma.Filter{
583 |> assign(:user, user)
584 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
586 assert response = json_response(conn, 200)
587 assert response["phrase"] == filter.phrase
588 assert response["context"] == filter.context
589 assert response["irreversible"] == false
590 assert response["id"] != nil
591 assert response["id"] != ""
594 test "fetching a list of filters", %{conn: conn} do
597 query_one = %Pleroma.Filter{
604 query_two = %Pleroma.Filter{
611 {:ok, filter_one} = Pleroma.Filter.create(query_one)
612 {:ok, filter_two} = Pleroma.Filter.create(query_two)
616 |> assign(:user, user)
617 |> get("/api/v1/filters")
618 |> json_response(200)
624 filters: [filter_two, filter_one]
628 test "get a filter", %{conn: conn} do
631 query = %Pleroma.Filter{
638 {:ok, filter} = Pleroma.Filter.create(query)
642 |> assign(:user, user)
643 |> get("/api/v1/filters/#{filter.filter_id}")
645 assert _response = json_response(conn, 200)
648 test "update a filter", %{conn: conn} do
651 query = %Pleroma.Filter{
658 {:ok, _filter} = Pleroma.Filter.create(query)
660 new = %Pleroma.Filter{
667 |> assign(:user, user)
668 |> put("/api/v1/filters/#{query.filter_id}", %{
673 assert response = json_response(conn, 200)
674 assert response["phrase"] == new.phrase
675 assert response["context"] == new.context
678 test "delete a filter", %{conn: conn} do
681 query = %Pleroma.Filter{
688 {:ok, filter} = Pleroma.Filter.create(query)
692 |> assign(:user, user)
693 |> delete("/api/v1/filters/#{filter.filter_id}")
695 assert response = json_response(conn, 200)
696 assert response == %{}
701 test "creating a list", %{conn: conn} do
706 |> assign(:user, user)
707 |> post("/api/v1/lists", %{"title" => "cuties"})
709 assert %{"title" => title} = json_response(conn, 200)
710 assert title == "cuties"
713 test "adding users to a list", %{conn: conn} do
715 other_user = insert(:user)
716 {:ok, list} = Pleroma.List.create("name", user)
720 |> assign(:user, user)
721 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
723 assert %{} == json_response(conn, 200)
724 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
725 assert following == [other_user.follower_address]
728 test "removing users from a list", %{conn: conn} do
730 other_user = insert(:user)
731 third_user = insert(:user)
732 {:ok, list} = Pleroma.List.create("name", user)
733 {:ok, list} = Pleroma.List.follow(list, other_user)
734 {:ok, list} = Pleroma.List.follow(list, third_user)
738 |> assign(:user, user)
739 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
741 assert %{} == json_response(conn, 200)
742 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
743 assert following == [third_user.follower_address]
746 test "listing users in a list", %{conn: conn} do
748 other_user = insert(:user)
749 {:ok, list} = Pleroma.List.create("name", user)
750 {:ok, list} = Pleroma.List.follow(list, other_user)
754 |> assign(:user, user)
755 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
757 assert [%{"id" => id}] = json_response(conn, 200)
758 assert id == to_string(other_user.id)
761 test "retrieving a list", %{conn: conn} do
763 {:ok, list} = Pleroma.List.create("name", user)
767 |> assign(:user, user)
768 |> get("/api/v1/lists/#{list.id}")
770 assert %{"id" => id} = json_response(conn, 200)
771 assert id == to_string(list.id)
774 test "renaming a list", %{conn: conn} do
776 {:ok, list} = Pleroma.List.create("name", user)
780 |> assign(:user, user)
781 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
783 assert %{"title" => name} = json_response(conn, 200)
784 assert name == "newname"
787 test "deleting a list", %{conn: conn} do
789 {:ok, list} = Pleroma.List.create("name", user)
793 |> assign(:user, user)
794 |> delete("/api/v1/lists/#{list.id}")
796 assert %{} = json_response(conn, 200)
797 assert is_nil(Repo.get(Pleroma.List, list.id))
800 test "list timeline", %{conn: conn} do
802 other_user = insert(:user)
803 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
804 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
805 {:ok, list} = Pleroma.List.create("name", user)
806 {:ok, list} = Pleroma.List.follow(list, other_user)
810 |> assign(:user, user)
811 |> get("/api/v1/timelines/list/#{list.id}")
813 assert [%{"id" => id}] = json_response(conn, 200)
815 assert id == to_string(activity_two.id)
818 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
820 other_user = insert(:user)
821 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
823 {:ok, _activity_two} =
824 TwitterAPI.create_status(other_user, %{
825 "status" => "Marisa is cute.",
826 "visibility" => "private"
829 {:ok, list} = Pleroma.List.create("name", user)
830 {:ok, list} = Pleroma.List.follow(list, other_user)
834 |> assign(:user, user)
835 |> get("/api/v1/timelines/list/#{list.id}")
837 assert [%{"id" => id}] = json_response(conn, 200)
839 assert id == to_string(activity_one.id)
843 describe "notifications" do
844 test "list of notifications", %{conn: conn} do
846 other_user = insert(:user)
849 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
851 {:ok, [_notification]} = Notification.create_notifications(activity)
855 |> assign(:user, user)
856 |> get("/api/v1/notifications")
859 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
861 }\">@<span>#{user.nickname}</span></a></span>"
863 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
864 assert response == expected_response
867 test "getting a single notification", %{conn: conn} do
869 other_user = insert(:user)
872 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
874 {:ok, [notification]} = Notification.create_notifications(activity)
878 |> assign(:user, user)
879 |> get("/api/v1/notifications/#{notification.id}")
882 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
884 }\">@<span>#{user.nickname}</span></a></span>"
886 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
887 assert response == expected_response
890 test "dismissing a single notification", %{conn: conn} do
892 other_user = insert(:user)
895 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
897 {:ok, [notification]} = Notification.create_notifications(activity)
901 |> assign(:user, user)
902 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
904 assert %{} = json_response(conn, 200)
907 test "clearing all notifications", %{conn: conn} do
909 other_user = insert(:user)
912 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
914 {:ok, [_notification]} = Notification.create_notifications(activity)
918 |> assign(:user, user)
919 |> post("/api/v1/notifications/clear")
921 assert %{} = json_response(conn, 200)
925 |> assign(:user, user)
926 |> get("/api/v1/notifications")
928 assert all = json_response(conn, 200)
932 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
934 other_user = insert(:user)
936 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
937 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
938 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
939 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
941 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
942 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
943 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
944 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
948 |> assign(:user, user)
953 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
955 result = json_response(conn_res, 200)
956 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
961 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
963 result = json_response(conn_res, 200)
964 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
969 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
971 result = json_response(conn_res, 200)
972 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
975 test "filters notifications using exclude_types", %{conn: conn} do
977 other_user = insert(:user)
979 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
980 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
981 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
982 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
983 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
985 mention_notification_id =
986 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
988 favorite_notification_id =
989 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
991 reblog_notification_id =
992 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
994 follow_notification_id =
995 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
999 |> assign(:user, user)
1002 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1004 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1007 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1009 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1012 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1014 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1017 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1019 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1022 test "destroy multiple", %{conn: conn} do
1023 user = insert(:user)
1024 other_user = insert(:user)
1026 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1027 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1028 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1029 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1031 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1032 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1033 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1034 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1038 |> assign(:user, user)
1042 |> get("/api/v1/notifications")
1044 result = json_response(conn_res, 200)
1045 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1049 |> assign(:user, other_user)
1053 |> get("/api/v1/notifications")
1055 result = json_response(conn_res, 200)
1056 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1060 |> delete("/api/v1/notifications/destroy_multiple", %{
1061 "ids" => [notification1_id, notification2_id]
1064 assert json_response(conn_destroy, 200) == %{}
1068 |> get("/api/v1/notifications")
1070 result = json_response(conn_res, 200)
1071 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1075 describe "reblogging" do
1076 test "reblogs and returns the reblogged status", %{conn: conn} do
1077 activity = insert(:note_activity)
1078 user = insert(:user)
1082 |> assign(:user, user)
1083 |> post("/api/v1/statuses/#{activity.id}/reblog")
1086 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1088 } = json_response(conn, 200)
1090 assert to_string(activity.id) == id
1093 test "reblogged status for another user", %{conn: conn} do
1094 activity = insert(:note_activity)
1095 user1 = insert(:user)
1096 user2 = insert(:user)
1097 user3 = insert(:user)
1098 CommonAPI.favorite(activity.id, user2)
1099 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1100 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1101 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1105 |> assign(:user, user3)
1106 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1109 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1110 "reblogged" => false,
1111 "favourited" => false,
1112 "bookmarked" => false
1113 } = json_response(conn_res, 200)
1117 |> assign(:user, user2)
1118 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1121 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1122 "reblogged" => true,
1123 "favourited" => true,
1124 "bookmarked" => true
1125 } = json_response(conn_res, 200)
1127 assert to_string(activity.id) == id
1131 describe "unreblogging" do
1132 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1133 activity = insert(:note_activity)
1134 user = insert(:user)
1136 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1140 |> assign(:user, user)
1141 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1143 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1145 assert to_string(activity.id) == id
1149 describe "favoriting" do
1150 test "favs a status and returns it", %{conn: conn} do
1151 activity = insert(:note_activity)
1152 user = insert(:user)
1156 |> assign(:user, user)
1157 |> post("/api/v1/statuses/#{activity.id}/favourite")
1159 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1160 json_response(conn, 200)
1162 assert to_string(activity.id) == id
1165 test "returns 500 for a wrong id", %{conn: conn} do
1166 user = insert(:user)
1170 |> assign(:user, user)
1171 |> post("/api/v1/statuses/1/favourite")
1172 |> json_response(500)
1174 assert resp == "Something went wrong"
1178 describe "unfavoriting" do
1179 test "unfavorites a status and returns it", %{conn: conn} do
1180 activity = insert(:note_activity)
1181 user = insert(:user)
1183 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1187 |> assign(:user, user)
1188 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1190 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1191 json_response(conn, 200)
1193 assert to_string(activity.id) == id
1197 describe "user timelines" do
1198 test "gets a users statuses", %{conn: conn} do
1199 user_one = insert(:user)
1200 user_two = insert(:user)
1201 user_three = insert(:user)
1203 {:ok, user_three} = User.follow(user_three, user_one)
1205 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1207 {:ok, direct_activity} =
1208 CommonAPI.post(user_one, %{
1209 "status" => "Hi, @#{user_two.nickname}.",
1210 "visibility" => "direct"
1213 {:ok, private_activity} =
1214 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1218 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1220 assert [%{"id" => id}] = json_response(resp, 200)
1221 assert id == to_string(activity.id)
1225 |> assign(:user, user_two)
1226 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1228 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1229 assert id_one == to_string(direct_activity.id)
1230 assert id_two == to_string(activity.id)
1234 |> assign(:user, user_three)
1235 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1237 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1238 assert id_one == to_string(private_activity.id)
1239 assert id_two == to_string(activity.id)
1242 test "unimplemented pinned statuses feature", %{conn: conn} do
1243 note = insert(:note_activity)
1244 user = User.get_cached_by_ap_id(note.data["actor"])
1248 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1250 assert json_response(conn, 200) == []
1253 test "gets an users media", %{conn: conn} do
1254 note = insert(:note_activity)
1255 user = User.get_cached_by_ap_id(note.data["actor"])
1257 file = %Plug.Upload{
1258 content_type: "image/jpg",
1259 path: Path.absname("test/fixtures/image.jpg"),
1260 filename: "an_image.jpg"
1264 TwitterAPI.upload(file, user, "json")
1268 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1272 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1274 assert [%{"id" => id}] = json_response(conn, 200)
1275 assert id == to_string(image_post.id)
1279 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1281 assert [%{"id" => id}] = json_response(conn, 200)
1282 assert id == to_string(image_post.id)
1285 test "gets a user's statuses without reblogs", %{conn: conn} do
1286 user = insert(:user)
1287 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1288 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1292 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1294 assert [%{"id" => id}] = json_response(conn, 200)
1295 assert id == to_string(post.id)
1299 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1301 assert [%{"id" => id}] = json_response(conn, 200)
1302 assert id == to_string(post.id)
1306 describe "user relationships" do
1307 test "returns the relationships for the current user", %{conn: conn} do
1308 user = insert(:user)
1309 other_user = insert(:user)
1310 {:ok, user} = User.follow(user, other_user)
1314 |> assign(:user, user)
1315 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1317 assert [relationship] = json_response(conn, 200)
1319 assert to_string(other_user.id) == relationship["id"]
1323 describe "locked accounts" do
1324 test "/api/v1/follow_requests works" do
1325 user = insert(:user, %{info: %User.Info{locked: true}})
1326 other_user = insert(:user)
1328 {:ok, _activity} = ActivityPub.follow(other_user, user)
1330 user = User.get_cached_by_id(user.id)
1331 other_user = User.get_cached_by_id(other_user.id)
1333 assert User.following?(other_user, user) == false
1337 |> assign(:user, user)
1338 |> get("/api/v1/follow_requests")
1340 assert [relationship] = json_response(conn, 200)
1341 assert to_string(other_user.id) == relationship["id"]
1344 test "/api/v1/follow_requests/:id/authorize works" do
1345 user = insert(:user, %{info: %User.Info{locked: true}})
1346 other_user = insert(:user)
1348 {:ok, _activity} = ActivityPub.follow(other_user, user)
1350 user = User.get_cached_by_id(user.id)
1351 other_user = User.get_cached_by_id(other_user.id)
1353 assert User.following?(other_user, user) == false
1357 |> assign(:user, user)
1358 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1360 assert relationship = json_response(conn, 200)
1361 assert to_string(other_user.id) == relationship["id"]
1363 user = User.get_cached_by_id(user.id)
1364 other_user = User.get_cached_by_id(other_user.id)
1366 assert User.following?(other_user, user) == true
1369 test "verify_credentials", %{conn: conn} do
1370 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1374 |> assign(:user, user)
1375 |> get("/api/v1/accounts/verify_credentials")
1377 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1378 assert id == to_string(user.id)
1381 test "/api/v1/follow_requests/:id/reject works" do
1382 user = insert(:user, %{info: %User.Info{locked: true}})
1383 other_user = insert(:user)
1385 {:ok, _activity} = ActivityPub.follow(other_user, user)
1387 user = User.get_cached_by_id(user.id)
1391 |> assign(:user, user)
1392 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1394 assert relationship = json_response(conn, 200)
1395 assert to_string(other_user.id) == relationship["id"]
1397 user = User.get_cached_by_id(user.id)
1398 other_user = User.get_cached_by_id(other_user.id)
1400 assert User.following?(other_user, user) == false
1404 test "account fetching", %{conn: conn} do
1405 user = insert(:user)
1409 |> get("/api/v1/accounts/#{user.id}")
1411 assert %{"id" => id} = json_response(conn, 200)
1412 assert id == to_string(user.id)
1416 |> get("/api/v1/accounts/-1")
1418 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1421 test "account fetching also works nickname", %{conn: conn} do
1422 user = insert(:user)
1426 |> get("/api/v1/accounts/#{user.nickname}")
1428 assert %{"id" => id} = json_response(conn, 200)
1429 assert id == user.id
1432 test "media upload", %{conn: conn} do
1433 file = %Plug.Upload{
1434 content_type: "image/jpg",
1435 path: Path.absname("test/fixtures/image.jpg"),
1436 filename: "an_image.jpg"
1439 desc = "Description of the image"
1441 user = insert(:user)
1445 |> assign(:user, user)
1446 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1448 assert media = json_response(conn, 200)
1450 assert media["type"] == "image"
1451 assert media["description"] == desc
1454 object = Repo.get(Object, media["id"])
1455 assert object.data["actor"] == User.ap_id(user)
1458 test "mascot upload", %{conn: conn} do
1459 user = insert(:user)
1461 non_image_file = %Plug.Upload{
1462 content_type: "audio/mpeg",
1463 path: Path.absname("test/fixtures/sound.mp3"),
1464 filename: "sound.mp3"
1469 |> assign(:user, user)
1470 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1472 assert json_response(conn, 415)
1474 file = %Plug.Upload{
1475 content_type: "image/jpg",
1476 path: Path.absname("test/fixtures/image.jpg"),
1477 filename: "an_image.jpg"
1482 |> assign(:user, user)
1483 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1485 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1488 test "mascot retrieving", %{conn: conn} do
1489 user = insert(:user)
1490 # When user hasn't set a mascot, we should just get pleroma tan back
1493 |> assign(:user, user)
1494 |> get("/api/v1/pleroma/mascot")
1496 assert %{"url" => url} = json_response(conn, 200)
1497 assert url =~ "pleroma-fox-tan-smol"
1499 # When a user sets their mascot, we should get that back
1500 file = %Plug.Upload{
1501 content_type: "image/jpg",
1502 path: Path.absname("test/fixtures/image.jpg"),
1503 filename: "an_image.jpg"
1508 |> assign(:user, user)
1509 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1510 assert json_response(conn, 200)
1512 user = User.get_cached_by_id(user.id)
1516 |> assign(:user, user)
1517 |> get("/api/v1/pleroma/mascot")
1519 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1520 assert url =~ "an_image"
1523 test "hashtag timeline", %{conn: conn} do
1524 following = insert(:user)
1527 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1529 {:ok, [_activity]} =
1530 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1534 |> get("/api/v1/timelines/tag/2hu")
1536 assert [%{"id" => id}] = json_response(nconn, 200)
1538 assert id == to_string(activity.id)
1540 # works for different capitalization too
1543 |> get("/api/v1/timelines/tag/2HU")
1545 assert [%{"id" => id}] = json_response(nconn, 200)
1547 assert id == to_string(activity.id)
1551 test "multi-hashtag timeline", %{conn: conn} do
1552 user = insert(:user)
1554 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1555 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1556 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1560 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1562 [status_none, status_test1, status_test] = json_response(any_test, 200)
1564 assert to_string(activity_test.id) == status_test["id"]
1565 assert to_string(activity_test1.id) == status_test1["id"]
1566 assert to_string(activity_none.id) == status_none["id"]
1570 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1572 assert [status_test1] == json_response(restricted_test, 200)
1574 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1576 assert [status_none] == json_response(all_test, 200)
1579 test "getting followers", %{conn: conn} do
1580 user = insert(:user)
1581 other_user = insert(:user)
1582 {:ok, user} = User.follow(user, other_user)
1586 |> get("/api/v1/accounts/#{other_user.id}/followers")
1588 assert [%{"id" => id}] = json_response(conn, 200)
1589 assert id == to_string(user.id)
1592 test "getting followers, hide_followers", %{conn: conn} do
1593 user = insert(:user)
1594 other_user = insert(:user, %{info: %{hide_followers: true}})
1595 {:ok, _user} = User.follow(user, other_user)
1599 |> get("/api/v1/accounts/#{other_user.id}/followers")
1601 assert [] == json_response(conn, 200)
1604 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1605 user = insert(:user)
1606 other_user = insert(:user, %{info: %{hide_followers: true}})
1607 {:ok, _user} = User.follow(user, other_user)
1611 |> assign(:user, other_user)
1612 |> get("/api/v1/accounts/#{other_user.id}/followers")
1614 refute [] == json_response(conn, 200)
1617 test "getting followers, pagination", %{conn: conn} do
1618 user = insert(:user)
1619 follower1 = insert(:user)
1620 follower2 = insert(:user)
1621 follower3 = insert(:user)
1622 {:ok, _} = User.follow(follower1, user)
1623 {:ok, _} = User.follow(follower2, user)
1624 {:ok, _} = User.follow(follower3, user)
1628 |> assign(:user, user)
1632 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1634 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1635 assert id3 == follower3.id
1636 assert id2 == follower2.id
1640 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1642 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1643 assert id2 == follower2.id
1644 assert id1 == follower1.id
1648 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1650 assert [%{"id" => id2}] = json_response(res_conn, 200)
1651 assert id2 == follower2.id
1653 assert [link_header] = get_resp_header(res_conn, "link")
1654 assert link_header =~ ~r/min_id=#{follower2.id}/
1655 assert link_header =~ ~r/max_id=#{follower2.id}/
1658 test "getting following", %{conn: conn} do
1659 user = insert(:user)
1660 other_user = insert(:user)
1661 {:ok, user} = User.follow(user, other_user)
1665 |> get("/api/v1/accounts/#{user.id}/following")
1667 assert [%{"id" => id}] = json_response(conn, 200)
1668 assert id == to_string(other_user.id)
1671 test "getting following, hide_follows", %{conn: conn} do
1672 user = insert(:user, %{info: %{hide_follows: true}})
1673 other_user = insert(:user)
1674 {:ok, user} = User.follow(user, other_user)
1678 |> get("/api/v1/accounts/#{user.id}/following")
1680 assert [] == json_response(conn, 200)
1683 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1684 user = insert(:user, %{info: %{hide_follows: true}})
1685 other_user = insert(:user)
1686 {:ok, user} = User.follow(user, other_user)
1690 |> assign(:user, user)
1691 |> get("/api/v1/accounts/#{user.id}/following")
1693 refute [] == json_response(conn, 200)
1696 test "getting following, pagination", %{conn: conn} do
1697 user = insert(:user)
1698 following1 = insert(:user)
1699 following2 = insert(:user)
1700 following3 = insert(:user)
1701 {:ok, _} = User.follow(user, following1)
1702 {:ok, _} = User.follow(user, following2)
1703 {:ok, _} = User.follow(user, following3)
1707 |> assign(:user, user)
1711 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1713 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1714 assert id3 == following3.id
1715 assert id2 == following2.id
1719 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1721 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1722 assert id2 == following2.id
1723 assert id1 == following1.id
1727 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1729 assert [%{"id" => id2}] = json_response(res_conn, 200)
1730 assert id2 == following2.id
1732 assert [link_header] = get_resp_header(res_conn, "link")
1733 assert link_header =~ ~r/min_id=#{following2.id}/
1734 assert link_header =~ ~r/max_id=#{following2.id}/
1737 test "following / unfollowing a user", %{conn: conn} do
1738 user = insert(:user)
1739 other_user = insert(:user)
1743 |> assign(:user, user)
1744 |> post("/api/v1/accounts/#{other_user.id}/follow")
1746 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1748 user = User.get_cached_by_id(user.id)
1752 |> assign(:user, user)
1753 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1755 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1757 user = User.get_cached_by_id(user.id)
1761 |> assign(:user, user)
1762 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1764 assert %{"id" => id} = json_response(conn, 200)
1765 assert id == to_string(other_user.id)
1768 test "following without reblogs" do
1769 follower = insert(:user)
1770 followed = insert(:user)
1771 other_user = insert(:user)
1775 |> assign(:user, follower)
1776 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1778 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1780 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1781 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1785 |> assign(:user, User.get_cached_by_id(follower.id))
1786 |> get("/api/v1/timelines/home")
1788 assert [] == json_response(conn, 200)
1792 |> assign(:user, follower)
1793 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1795 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1799 |> assign(:user, User.get_cached_by_id(follower.id))
1800 |> get("/api/v1/timelines/home")
1802 expected_activity_id = reblog.id
1803 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1806 test "following / unfollowing errors" do
1807 user = insert(:user)
1811 |> assign(:user, user)
1814 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1815 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1818 user = User.get_cached_by_id(user.id)
1819 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1820 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1822 # self follow via uri
1823 user = User.get_cached_by_id(user.id)
1824 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1825 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1827 # follow non existing user
1828 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1829 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1831 # follow non existing user via uri
1832 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1833 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1835 # unfollow non existing user
1836 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1837 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1840 test "muting / unmuting a user", %{conn: conn} do
1841 user = insert(:user)
1842 other_user = insert(:user)
1846 |> assign(:user, user)
1847 |> post("/api/v1/accounts/#{other_user.id}/mute")
1849 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1851 user = User.get_cached_by_id(user.id)
1855 |> assign(:user, user)
1856 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1858 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1861 test "subscribing / unsubscribing to a user", %{conn: conn} do
1862 user = insert(:user)
1863 subscription_target = insert(:user)
1867 |> assign(:user, user)
1868 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1870 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1874 |> assign(:user, user)
1875 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1877 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1880 test "getting a list of mutes", %{conn: conn} do
1881 user = insert(:user)
1882 other_user = insert(:user)
1884 {:ok, user} = User.mute(user, other_user)
1888 |> assign(:user, user)
1889 |> get("/api/v1/mutes")
1891 other_user_id = to_string(other_user.id)
1892 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1895 test "blocking / unblocking a user", %{conn: conn} do
1896 user = insert(:user)
1897 other_user = insert(:user)
1901 |> assign(:user, user)
1902 |> post("/api/v1/accounts/#{other_user.id}/block")
1904 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1906 user = User.get_cached_by_id(user.id)
1910 |> assign(:user, user)
1911 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1913 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1916 test "getting a list of blocks", %{conn: conn} do
1917 user = insert(:user)
1918 other_user = insert(:user)
1920 {:ok, user} = User.block(user, other_user)
1924 |> assign(:user, user)
1925 |> get("/api/v1/blocks")
1927 other_user_id = to_string(other_user.id)
1928 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1931 test "blocking / unblocking a domain", %{conn: conn} do
1932 user = insert(:user)
1933 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1937 |> assign(:user, user)
1938 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1940 assert %{} = json_response(conn, 200)
1941 user = User.get_cached_by_ap_id(user.ap_id)
1942 assert User.blocks?(user, other_user)
1946 |> assign(:user, user)
1947 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1949 assert %{} = json_response(conn, 200)
1950 user = User.get_cached_by_ap_id(user.ap_id)
1951 refute User.blocks?(user, other_user)
1954 test "getting a list of domain blocks", %{conn: conn} do
1955 user = insert(:user)
1957 {:ok, user} = User.block_domain(user, "bad.site")
1958 {:ok, user} = User.block_domain(user, "even.worse.site")
1962 |> assign(:user, user)
1963 |> get("/api/v1/domain_blocks")
1965 domain_blocks = json_response(conn, 200)
1967 assert "bad.site" in domain_blocks
1968 assert "even.worse.site" in domain_blocks
1971 test "unimplemented follow_requests, blocks, domain blocks" do
1972 user = insert(:user)
1974 ["blocks", "domain_blocks", "follow_requests"]
1975 |> Enum.each(fn endpoint ->
1978 |> assign(:user, user)
1979 |> get("/api/v1/#{endpoint}")
1981 assert [] = json_response(conn, 200)
1985 test "account search", %{conn: conn} do
1986 user = insert(:user)
1987 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1988 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1992 |> assign(:user, user)
1993 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1994 |> json_response(200)
1996 result_ids = for result <- results, do: result["acct"]
1998 assert user_two.nickname in result_ids
1999 assert user_three.nickname in result_ids
2003 |> assign(:user, user)
2004 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
2005 |> json_response(200)
2007 result_ids = for result <- results, do: result["acct"]
2009 assert user_three.nickname in result_ids
2012 test "search", %{conn: conn} do
2013 user = insert(:user)
2014 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2015 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2017 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
2020 CommonAPI.post(user, %{
2021 "status" => "This is about 2hu, but private",
2022 "visibility" => "private"
2025 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
2029 |> get("/api/v1/search", %{"q" => "2hu"})
2031 assert results = json_response(conn, 200)
2033 [account | _] = results["accounts"]
2034 assert account["id"] == to_string(user_three.id)
2036 assert results["hashtags"] == []
2038 [status] = results["statuses"]
2039 assert status["id"] == to_string(activity.id)
2042 test "search fetches remote statuses", %{conn: conn} do
2046 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
2048 assert results = json_response(conn, 200)
2050 [status] = results["statuses"]
2051 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
2055 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
2057 CommonAPI.post(insert(:user), %{
2058 "status" => "This is about 2hu, but private",
2059 "visibility" => "private"
2065 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2067 assert results = json_response(conn, 200)
2069 [] = results["statuses"]
2073 test "search fetches remote accounts", %{conn: conn} do
2076 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2078 assert results = json_response(conn, 200)
2079 [account] = results["accounts"]
2080 assert account["acct"] == "shp@social.heldscal.la"
2083 test "returns the favorites of a user", %{conn: conn} do
2084 user = insert(:user)
2085 other_user = insert(:user)
2087 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2088 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2090 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2094 |> assign(:user, user)
2095 |> get("/api/v1/favourites")
2097 assert [status] = json_response(first_conn, 200)
2098 assert status["id"] == to_string(activity.id)
2100 assert [{"link", _link_header}] =
2101 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2103 # Honours query params
2104 {:ok, second_activity} =
2105 CommonAPI.post(other_user, %{
2107 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2110 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2112 last_like = status["id"]
2116 |> assign(:user, user)
2117 |> get("/api/v1/favourites?since_id=#{last_like}")
2119 assert [second_status] = json_response(second_conn, 200)
2120 assert second_status["id"] == to_string(second_activity.id)
2124 |> assign(:user, user)
2125 |> get("/api/v1/favourites?limit=0")
2127 assert [] = json_response(third_conn, 200)
2130 describe "getting favorites timeline of specified user" do
2132 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2133 [current_user: current_user, user: user]
2136 test "returns list of statuses favorited by specified user", %{
2138 current_user: current_user,
2141 [activity | _] = insert_pair(:note_activity)
2142 CommonAPI.favorite(activity.id, user)
2146 |> assign(:user, current_user)
2147 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2148 |> json_response(:ok)
2152 assert length(response) == 1
2153 assert like["id"] == activity.id
2156 test "returns favorites for specified user_id when user is not logged in", %{
2160 activity = insert(:note_activity)
2161 CommonAPI.favorite(activity.id, user)
2165 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2166 |> json_response(:ok)
2168 assert length(response) == 1
2171 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2173 current_user: current_user,
2177 CommonAPI.post(current_user, %{
2178 "status" => "Hi @#{user.nickname}!",
2179 "visibility" => "direct"
2182 CommonAPI.favorite(direct.id, user)
2186 |> assign(:user, current_user)
2187 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2188 |> json_response(:ok)
2190 assert length(response) == 1
2192 anonymous_response =
2194 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2195 |> json_response(:ok)
2197 assert Enum.empty?(anonymous_response)
2200 test "does not return others' favorited DM when user is not one of recipients", %{
2202 current_user: current_user,
2205 user_two = insert(:user)
2208 CommonAPI.post(user_two, %{
2209 "status" => "Hi @#{user.nickname}!",
2210 "visibility" => "direct"
2213 CommonAPI.favorite(direct.id, user)
2217 |> assign(:user, current_user)
2218 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2219 |> json_response(:ok)
2221 assert Enum.empty?(response)
2224 test "paginates favorites using since_id and max_id", %{
2226 current_user: current_user,
2229 activities = insert_list(10, :note_activity)
2231 Enum.each(activities, fn activity ->
2232 CommonAPI.favorite(activity.id, user)
2235 third_activity = Enum.at(activities, 2)
2236 seventh_activity = Enum.at(activities, 6)
2240 |> assign(:user, current_user)
2241 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2242 since_id: third_activity.id,
2243 max_id: seventh_activity.id
2245 |> json_response(:ok)
2247 assert length(response) == 3
2248 refute third_activity in response
2249 refute seventh_activity in response
2252 test "limits favorites using limit parameter", %{
2254 current_user: current_user,
2258 |> insert_list(:note_activity)
2259 |> Enum.each(fn activity ->
2260 CommonAPI.favorite(activity.id, user)
2265 |> assign(:user, current_user)
2266 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2267 |> json_response(:ok)
2269 assert length(response) == 3
2272 test "returns empty response when user does not have any favorited statuses", %{
2274 current_user: current_user,
2279 |> assign(:user, current_user)
2280 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2281 |> json_response(:ok)
2283 assert Enum.empty?(response)
2286 test "returns 404 error when specified user is not exist", %{conn: conn} do
2287 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2289 assert json_response(conn, 404) == %{"error" => "Record not found"}
2292 test "returns 403 error when user has hidden own favorites", %{
2294 current_user: current_user
2296 user = insert(:user, %{info: %{hide_favorites: true}})
2297 activity = insert(:note_activity)
2298 CommonAPI.favorite(activity.id, user)
2302 |> assign(:user, current_user)
2303 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2305 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2308 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2309 user = insert(:user)
2310 activity = insert(:note_activity)
2311 CommonAPI.favorite(activity.id, user)
2315 |> assign(:user, current_user)
2316 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2318 assert user.info.hide_favorites
2319 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2323 describe "updating credentials" do
2324 test "updates the user's bio", %{conn: conn} do
2325 user = insert(:user)
2326 user2 = insert(:user)
2330 |> assign(:user, user)
2331 |> patch("/api/v1/accounts/update_credentials", %{
2332 "note" => "I drink #cofe with @#{user2.nickname}"
2335 assert user = json_response(conn, 200)
2337 assert user["note"] ==
2338 ~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=") <>
2340 ~s(" class="u-url mention" href=") <>
2341 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2344 test "updates the user's locking status", %{conn: conn} do
2345 user = insert(:user)
2349 |> assign(:user, user)
2350 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2352 assert user = json_response(conn, 200)
2353 assert user["locked"] == true
2356 test "updates the user's default scope", %{conn: conn} do
2357 user = insert(:user)
2361 |> assign(:user, user)
2362 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2364 assert user = json_response(conn, 200)
2365 assert user["source"]["privacy"] == "cofe"
2368 test "updates the user's hide_followers status", %{conn: conn} do
2369 user = insert(:user)
2373 |> assign(:user, user)
2374 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2376 assert user = json_response(conn, 200)
2377 assert user["pleroma"]["hide_followers"] == true
2380 test "updates the user's hide_follows status", %{conn: conn} do
2381 user = insert(:user)
2385 |> assign(:user, user)
2386 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2388 assert user = json_response(conn, 200)
2389 assert user["pleroma"]["hide_follows"] == true
2392 test "updates the user's hide_favorites status", %{conn: conn} do
2393 user = insert(:user)
2397 |> assign(:user, user)
2398 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2400 assert user = json_response(conn, 200)
2401 assert user["pleroma"]["hide_favorites"] == true
2404 test "updates the user's show_role status", %{conn: conn} do
2405 user = insert(:user)
2409 |> assign(:user, user)
2410 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2412 assert user = json_response(conn, 200)
2413 assert user["source"]["pleroma"]["show_role"] == false
2416 test "updates the user's no_rich_text status", %{conn: conn} do
2417 user = insert(:user)
2421 |> assign(:user, user)
2422 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2424 assert user = json_response(conn, 200)
2425 assert user["source"]["pleroma"]["no_rich_text"] == true
2428 test "updates the user's name", %{conn: conn} do
2429 user = insert(:user)
2433 |> assign(:user, user)
2434 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2436 assert user = json_response(conn, 200)
2437 assert user["display_name"] == "markorepairs"
2440 test "updates the user's avatar", %{conn: conn} do
2441 user = insert(:user)
2443 new_avatar = %Plug.Upload{
2444 content_type: "image/jpg",
2445 path: Path.absname("test/fixtures/image.jpg"),
2446 filename: "an_image.jpg"
2451 |> assign(:user, user)
2452 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2454 assert user_response = json_response(conn, 200)
2455 assert user_response["avatar"] != User.avatar_url(user)
2458 test "updates the user's banner", %{conn: conn} do
2459 user = insert(:user)
2461 new_header = %Plug.Upload{
2462 content_type: "image/jpg",
2463 path: Path.absname("test/fixtures/image.jpg"),
2464 filename: "an_image.jpg"
2469 |> assign(:user, user)
2470 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2472 assert user_response = json_response(conn, 200)
2473 assert user_response["header"] != User.banner_url(user)
2476 test "requires 'write' permission", %{conn: conn} do
2477 token1 = insert(:oauth_token, scopes: ["read"])
2478 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2480 for token <- [token1, token2] do
2483 |> put_req_header("authorization", "Bearer #{token.token}")
2484 |> patch("/api/v1/accounts/update_credentials", %{})
2486 if token == token1 do
2487 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2489 assert json_response(conn, 200)
2494 test "updates profile emojos", %{conn: conn} do
2495 user = insert(:user)
2497 note = "*sips :blank:*"
2498 name = "I am :firefox:"
2502 |> assign(:user, user)
2503 |> patch("/api/v1/accounts/update_credentials", %{
2505 "display_name" => name
2508 assert json_response(conn, 200)
2512 |> get("/api/v1/accounts/#{user.id}")
2514 assert user = json_response(conn, 200)
2516 assert user["note"] == note
2517 assert user["display_name"] == name
2518 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2522 test "get instance information", %{conn: conn} do
2523 conn = get(conn, "/api/v1/instance")
2524 assert result = json_response(conn, 200)
2526 email = Pleroma.Config.get([:instance, :email])
2527 # Note: not checking for "max_toot_chars" since it's optional
2533 "email" => from_config_email,
2535 "streaming_api" => _
2540 "registrations" => _
2543 assert email == from_config_email
2546 test "get instance stats", %{conn: conn} do
2547 user = insert(:user, %{local: true})
2549 user2 = insert(:user, %{local: true})
2550 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2552 insert(:user, %{local: false, nickname: "u@peer1.com"})
2553 insert(:user, %{local: false, nickname: "u@peer2.com"})
2555 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2557 # Stats should count users with missing or nil `info.deactivated` value
2558 user = User.get_cached_by_id(user.id)
2559 info_change = Changeset.change(user.info, %{deactivated: nil})
2563 |> Changeset.change()
2564 |> Changeset.put_embed(:info, info_change)
2565 |> User.update_and_set_cache()
2567 Pleroma.Stats.update_stats()
2569 conn = get(conn, "/api/v1/instance")
2571 assert result = json_response(conn, 200)
2573 stats = result["stats"]
2576 assert stats["user_count"] == 1
2577 assert stats["status_count"] == 1
2578 assert stats["domain_count"] == 2
2581 test "get peers", %{conn: conn} do
2582 insert(:user, %{local: false, nickname: "u@peer1.com"})
2583 insert(:user, %{local: false, nickname: "u@peer2.com"})
2585 Pleroma.Stats.update_stats()
2587 conn = get(conn, "/api/v1/instance/peers")
2589 assert result = json_response(conn, 200)
2591 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2594 test "put settings", %{conn: conn} do
2595 user = insert(:user)
2599 |> assign(:user, user)
2600 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2602 assert _result = json_response(conn, 200)
2604 user = User.get_cached_by_ap_id(user.ap_id)
2605 assert user.info.settings == %{"programming" => "socks"}
2608 describe "pinned statuses" do
2610 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2612 user = insert(:user)
2613 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2615 [user: user, activity: activity]
2618 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2619 {:ok, _} = CommonAPI.pin(activity.id, user)
2623 |> assign(:user, user)
2624 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2625 |> json_response(200)
2627 id_str = to_string(activity.id)
2629 assert [%{"id" => ^id_str, "pinned" => true}] = result
2632 test "pin status", %{conn: conn, user: user, activity: activity} do
2633 id_str = to_string(activity.id)
2635 assert %{"id" => ^id_str, "pinned" => true} =
2637 |> assign(:user, user)
2638 |> post("/api/v1/statuses/#{activity.id}/pin")
2639 |> json_response(200)
2641 assert [%{"id" => ^id_str, "pinned" => true}] =
2643 |> assign(:user, user)
2644 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2645 |> json_response(200)
2648 test "unpin status", %{conn: conn, user: user, activity: activity} do
2649 {:ok, _} = CommonAPI.pin(activity.id, user)
2651 id_str = to_string(activity.id)
2652 user = refresh_record(user)
2654 assert %{"id" => ^id_str, "pinned" => false} =
2656 |> assign(:user, user)
2657 |> post("/api/v1/statuses/#{activity.id}/unpin")
2658 |> json_response(200)
2662 |> assign(:user, user)
2663 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2664 |> json_response(200)
2667 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2668 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2670 id_str_one = to_string(activity_one.id)
2672 assert %{"id" => ^id_str_one, "pinned" => true} =
2674 |> assign(:user, user)
2675 |> post("/api/v1/statuses/#{id_str_one}/pin")
2676 |> json_response(200)
2678 user = refresh_record(user)
2680 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2682 |> assign(:user, user)
2683 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2684 |> json_response(400)
2687 test "Status rich-media Card", %{conn: conn, user: user} do
2688 Pleroma.Config.put([:rich_media, :enabled], true)
2689 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2693 |> get("/api/v1/statuses/#{activity.id}/card")
2694 |> json_response(200)
2696 assert response == %{
2697 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2698 "provider_name" => "www.imdb.com",
2699 "provider_url" => "http://www.imdb.com",
2700 "title" => "The Rock",
2702 "url" => "http://www.imdb.com/title/tt0117500/",
2703 "description" => nil,
2706 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2707 "title" => "The Rock",
2708 "type" => "video.movie",
2709 "url" => "http://www.imdb.com/title/tt0117500/"
2714 # works with private posts
2716 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2720 |> assign(:user, user)
2721 |> get("/api/v1/statuses/#{activity.id}/card")
2722 |> json_response(200)
2724 assert response_two == response
2726 Pleroma.Config.put([:rich_media, :enabled], false)
2731 user = insert(:user)
2732 for_user = insert(:user)
2735 CommonAPI.post(user, %{
2736 "status" => "heweoo?"
2740 CommonAPI.post(user, %{
2741 "status" => "heweoo!"
2746 |> assign(:user, for_user)
2747 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2749 assert json_response(response1, 200)["bookmarked"] == true
2753 |> assign(:user, for_user)
2754 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2756 assert json_response(response2, 200)["bookmarked"] == true
2760 |> assign(:user, for_user)
2761 |> get("/api/v1/bookmarks")
2763 assert [json_response(response2, 200), json_response(response1, 200)] ==
2764 json_response(bookmarks, 200)
2768 |> assign(:user, for_user)
2769 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2771 assert json_response(response1, 200)["bookmarked"] == false
2775 |> assign(:user, for_user)
2776 |> get("/api/v1/bookmarks")
2778 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2781 describe "conversation muting" do
2783 user = insert(:user)
2784 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2786 [user: user, activity: activity]
2789 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2790 id_str = to_string(activity.id)
2792 assert %{"id" => ^id_str, "muted" => true} =
2794 |> assign(:user, user)
2795 |> post("/api/v1/statuses/#{activity.id}/mute")
2796 |> json_response(200)
2799 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2800 {:ok, _} = CommonAPI.add_mute(user, activity)
2802 id_str = to_string(activity.id)
2803 user = refresh_record(user)
2805 assert %{"id" => ^id_str, "muted" => false} =
2807 |> assign(:user, user)
2808 |> post("/api/v1/statuses/#{activity.id}/unmute")
2809 |> json_response(200)
2813 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2814 user = insert(:user)
2818 |> assign(:user, user)
2819 |> get("/api/v1/pleroma/flavour")
2821 assert "glitch" == json_response(get_old_flavour, 200)
2825 |> assign(:user, user)
2826 |> post("/api/v1/pleroma/flavour/vanilla")
2828 assert "vanilla" == json_response(set_flavour, 200)
2832 |> assign(:user, user)
2833 |> post("/api/v1/pleroma/flavour/vanilla")
2835 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2838 describe "reports" do
2840 reporter = insert(:user)
2841 target_user = insert(:user)
2843 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2845 [reporter: reporter, target_user: target_user, activity: activity]
2848 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2849 assert %{"action_taken" => false, "id" => _} =
2851 |> assign(:user, reporter)
2852 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2853 |> json_response(200)
2856 test "submit a report with statuses and comment", %{
2859 target_user: target_user,
2862 assert %{"action_taken" => false, "id" => _} =
2864 |> assign(:user, reporter)
2865 |> post("/api/v1/reports", %{
2866 "account_id" => target_user.id,
2867 "status_ids" => [activity.id],
2868 "comment" => "bad status!"
2870 |> json_response(200)
2873 test "account_id is required", %{
2878 assert %{"error" => "Valid `account_id` required"} =
2880 |> assign(:user, reporter)
2881 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2882 |> json_response(400)
2885 test "comment must be up to the size specified in the config", %{
2888 target_user: target_user
2890 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2891 comment = String.pad_trailing("a", max_size + 1, "a")
2893 error = %{"error" => "Comment must be up to #{max_size} characters"}
2897 |> assign(:user, reporter)
2898 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2899 |> json_response(400)
2903 describe "link headers" do
2904 test "preserves parameters in link headers", %{conn: conn} do
2905 user = insert(:user)
2906 other_user = insert(:user)
2909 CommonAPI.post(other_user, %{
2910 "status" => "hi @#{user.nickname}",
2911 "visibility" => "public"
2915 CommonAPI.post(other_user, %{
2916 "status" => "hi @#{user.nickname}",
2917 "visibility" => "public"
2920 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2921 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2925 |> assign(:user, user)
2926 |> get("/api/v1/notifications", %{media_only: true})
2928 assert [link_header] = get_resp_header(conn, "link")
2929 assert link_header =~ ~r/media_only=true/
2930 assert link_header =~ ~r/min_id=#{notification2.id}/
2931 assert link_header =~ ~r/max_id=#{notification1.id}/
2935 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2936 # Need to set an old-style integer ID to reproduce the problem
2937 # (these are no longer assigned to new accounts but were preserved
2938 # for existing accounts during the migration to flakeIDs)
2939 user_one = insert(:user, %{id: 1212})
2940 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2944 |> get("/api/v1/accounts/#{user_one.id}")
2948 |> get("/api/v1/accounts/#{user_two.nickname}")
2952 |> get("/api/v1/accounts/#{user_two.id}")
2954 acc_one = json_response(resp_one, 200)
2955 acc_two = json_response(resp_two, 200)
2956 acc_three = json_response(resp_three, 200)
2957 refute acc_one == acc_two
2958 assert acc_two == acc_three
2961 describe "custom emoji" do
2962 test "with tags", %{conn: conn} do
2965 |> get("/api/v1/custom_emojis")
2966 |> json_response(200)
2968 assert Map.has_key?(emoji, "shortcode")
2969 assert Map.has_key?(emoji, "static_url")
2970 assert Map.has_key?(emoji, "tags")
2971 assert is_list(emoji["tags"])
2972 assert Map.has_key?(emoji, "url")
2973 assert Map.has_key?(emoji, "visible_in_picker")
2977 describe "index/2 redirections" do
2978 setup %{conn: conn} do
2982 signing_salt: "cooldude"
2987 |> Plug.Session.call(Plug.Session.init(session_opts))
2990 test_path = "/web/statuses/test"
2991 %{conn: conn, path: test_path}
2994 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2995 conn = get(conn, path)
2997 assert conn.status == 302
2998 assert redirected_to(conn) == "/web/login"
3001 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3002 token = insert(:oauth_token)
3006 |> assign(:user, token.user)
3007 |> put_session(:oauth_token, token.token)
3010 assert conn.status == 200
3013 test "saves referer path to session", %{conn: conn, path: path} do
3014 conn = get(conn, path)
3015 return_to = Plug.Conn.get_session(conn, :return_to)
3017 assert return_to == path
3020 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3021 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3022 auth = insert(:oauth_authorization, app: app)
3026 |> put_session(:return_to, path)
3027 |> get("/web/login", %{code: auth.token})
3029 assert conn.status == 302
3030 assert redirected_to(conn) == path
3033 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3034 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3035 auth = insert(:oauth_authorization, app: app)
3037 conn = get(conn, "/web/login", %{code: auth.token})
3039 assert conn.status == 302
3040 assert redirected_to(conn) == "/web/getting-started"
3044 describe "scheduled activities" do
3045 test "creates a scheduled activity", %{conn: conn} do
3046 user = insert(:user)
3047 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3051 |> assign(:user, user)
3052 |> post("/api/v1/statuses", %{
3053 "status" => "scheduled",
3054 "scheduled_at" => scheduled_at
3057 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3058 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3059 assert [] == Repo.all(Activity)
3062 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3063 user = insert(:user)
3064 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3066 file = %Plug.Upload{
3067 content_type: "image/jpg",
3068 path: Path.absname("test/fixtures/image.jpg"),
3069 filename: "an_image.jpg"
3072 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3076 |> assign(:user, user)
3077 |> post("/api/v1/statuses", %{
3078 "media_ids" => [to_string(upload.id)],
3079 "status" => "scheduled",
3080 "scheduled_at" => scheduled_at
3083 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3084 assert %{"type" => "image"} = media_attachment
3087 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3089 user = insert(:user)
3092 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3096 |> assign(:user, user)
3097 |> post("/api/v1/statuses", %{
3098 "status" => "not scheduled",
3099 "scheduled_at" => scheduled_at
3102 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3103 assert [] == Repo.all(ScheduledActivity)
3106 test "returns error when daily user limit is exceeded", %{conn: conn} do
3107 user = insert(:user)
3110 NaiveDateTime.utc_now()
3111 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3112 |> NaiveDateTime.to_iso8601()
3114 attrs = %{params: %{}, scheduled_at: today}
3115 {:ok, _} = ScheduledActivity.create(user, attrs)
3116 {:ok, _} = ScheduledActivity.create(user, attrs)
3120 |> assign(:user, user)
3121 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3123 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3126 test "returns error when total user limit is exceeded", %{conn: conn} do
3127 user = insert(:user)
3130 NaiveDateTime.utc_now()
3131 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3132 |> NaiveDateTime.to_iso8601()
3135 NaiveDateTime.utc_now()
3136 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3137 |> NaiveDateTime.to_iso8601()
3139 attrs = %{params: %{}, scheduled_at: today}
3140 {:ok, _} = ScheduledActivity.create(user, attrs)
3141 {:ok, _} = ScheduledActivity.create(user, attrs)
3142 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3146 |> assign(:user, user)
3147 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3149 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3152 test "shows scheduled activities", %{conn: conn} do
3153 user = insert(:user)
3154 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3155 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3156 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3157 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3161 |> assign(:user, user)
3166 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3168 result = json_response(conn_res, 200)
3169 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3174 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3176 result = json_response(conn_res, 200)
3177 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3182 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3184 result = json_response(conn_res, 200)
3185 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3188 test "shows a scheduled activity", %{conn: conn} do
3189 user = insert(:user)
3190 scheduled_activity = insert(:scheduled_activity, user: user)
3194 |> assign(:user, user)
3195 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3197 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3198 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3202 |> assign(:user, user)
3203 |> get("/api/v1/scheduled_statuses/404")
3205 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3208 test "updates a scheduled activity", %{conn: conn} do
3209 user = insert(:user)
3210 scheduled_activity = insert(:scheduled_activity, user: user)
3213 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3217 |> assign(:user, user)
3218 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3219 scheduled_at: new_scheduled_at
3222 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3223 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3227 |> assign(:user, user)
3228 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3230 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3233 test "deletes a scheduled activity", %{conn: conn} do
3234 user = insert(:user)
3235 scheduled_activity = insert(:scheduled_activity, user: user)
3239 |> assign(:user, user)
3240 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3242 assert %{} = json_response(res_conn, 200)
3243 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3247 |> assign(:user, user)
3248 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3250 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3254 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3255 user1 = insert(:user)
3256 user2 = insert(:user)
3257 user3 = insert(:user)
3259 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3261 # Reply to status from another user
3264 |> assign(:user, user2)
3265 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3267 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3269 activity = Activity.get_by_id_with_object(id)
3271 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3272 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3274 # Reblog from the third user
3277 |> assign(:user, user3)
3278 |> post("/api/v1/statuses/#{activity.id}/reblog")
3280 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3281 json_response(conn2, 200)
3283 assert to_string(activity.id) == id
3285 # Getting third user status
3288 |> assign(:user, user3)
3289 |> get("api/v1/timelines/home")
3291 [reblogged_activity] = json_response(conn3, 200)
3293 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3295 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3296 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3299 describe "create account by app" do
3301 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3302 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3303 interval = Pleroma.Config.get([:app_account_creation, :interval])
3305 Pleroma.Config.put([:app_account_creation, :enabled], true)
3306 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3307 Pleroma.Config.put([:app_account_creation, :interval], 1)
3310 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3311 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3312 Pleroma.Config.put([:app_account_creation, :interval], interval)
3318 test "Account registration via Application", %{conn: conn} do
3321 |> post("/api/v1/apps", %{
3322 client_name: "client_name",
3323 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3324 scopes: "read, write, follow"
3328 "client_id" => client_id,
3329 "client_secret" => client_secret,
3331 "name" => "client_name",
3332 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3335 } = json_response(conn, 200)
3339 |> post("/oauth/token", %{
3340 grant_type: "client_credentials",
3341 client_id: client_id,
3342 client_secret: client_secret
3345 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3346 json_response(conn, 200)
3349 token_from_db = Repo.get_by(Token, token: token)
3350 assert token_from_db
3352 assert scope == "read write follow"
3356 |> put_req_header("authorization", "Bearer " <> token)
3357 |> post("/api/v1/accounts", %{
3359 email: "lain@example.org",
3360 password: "PlzDontHackLain",
3365 "access_token" => token,
3366 "created_at" => _created_at,
3368 "token_type" => "Bearer"
3369 } = json_response(conn, 200)
3371 token_from_db = Repo.get_by(Token, token: token)
3372 assert token_from_db
3373 token_from_db = Repo.preload(token_from_db, :user)
3374 assert token_from_db.user
3376 assert token_from_db.user.info.confirmation_pending
3379 test "rate limit", %{conn: conn} do
3380 app_token = insert(:oauth_token, user: nil)
3383 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3384 |> Map.put(:remote_ip, {15, 15, 15, 15})
3389 |> post("/api/v1/accounts", %{
3390 username: "#{i}lain",
3391 email: "#{i}lain@example.org",
3392 password: "PlzDontHackLain",
3397 "access_token" => token,
3398 "created_at" => _created_at,
3400 "token_type" => "Bearer"
3401 } = json_response(conn, 200)
3403 token_from_db = Repo.get_by(Token, token: token)
3404 assert token_from_db
3405 token_from_db = Repo.preload(token_from_db, :user)
3406 assert token_from_db.user
3408 assert token_from_db.user.info.confirmation_pending
3413 |> post("/api/v1/accounts", %{
3415 email: "6lain@example.org",
3416 password: "PlzDontHackLain",
3420 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}