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.OStatus
20 alias Pleroma.Web.Push
21 alias Pleroma.Web.TwitterAPI.TwitterAPI
22 import Pleroma.Factory
23 import ExUnit.CaptureLog
27 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
31 test "the home timeline", %{conn: conn} do
33 following = insert(:user)
35 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
39 |> assign(:user, user)
40 |> get("/api/v1/timelines/home")
42 assert Enum.empty?(json_response(conn, 200))
44 {:ok, user} = User.follow(user, following)
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
51 assert [%{"content" => "test"}] = json_response(conn, 200)
54 test "the public timeline", %{conn: conn} do
55 following = insert(:user)
58 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
61 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
65 |> get("/api/v1/timelines/public", %{"local" => "False"})
67 assert length(json_response(conn, 200)) == 2
71 |> get("/api/v1/timelines/public", %{"local" => "True"})
73 assert [%{"content" => "test"}] = json_response(conn, 200)
77 |> get("/api/v1/timelines/public", %{"local" => "1"})
79 assert [%{"content" => "test"}] = json_response(conn, 200)
83 test "posting a status", %{conn: conn} do
86 idempotency_key = "Pikachu rocks!"
90 |> assign(:user, user)
91 |> put_req_header("idempotency-key", idempotency_key)
92 |> post("/api/v1/statuses", %{
94 "spoiler_text" => "2hu",
95 "sensitive" => "false"
98 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
100 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
102 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
103 json_response(conn_one, 200)
105 assert Activity.get_by_id(id)
109 |> assign(:user, user)
110 |> put_req_header("idempotency-key", idempotency_key)
111 |> post("/api/v1/statuses", %{
113 "spoiler_text" => "2hu",
114 "sensitive" => "false"
117 assert %{"id" => second_id} = json_response(conn_two, 200)
119 assert id == second_id
123 |> assign(:user, user)
124 |> post("/api/v1/statuses", %{
126 "spoiler_text" => "2hu",
127 "sensitive" => "false"
130 assert %{"id" => third_id} = json_response(conn_three, 200)
132 refute id == third_id
135 test "posting a sensitive status", %{conn: conn} do
140 |> assign(:user, user)
141 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
143 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
144 assert Activity.get_by_id(id)
147 test "posting a fake status", %{conn: conn} do
152 |> assign(:user, user)
153 |> post("/api/v1/statuses", %{
155 "\"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"
158 real_status = json_response(real_conn, 200)
161 assert Object.get_by_ap_id(real_status["uri"])
165 |> Map.put("id", nil)
166 |> Map.put("url", nil)
167 |> Map.put("uri", nil)
168 |> Map.put("created_at", nil)
169 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
173 |> assign(:user, user)
174 |> post("/api/v1/statuses", %{
176 "\"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",
180 fake_status = json_response(fake_conn, 200)
183 refute Object.get_by_ap_id(fake_status["uri"])
187 |> Map.put("id", nil)
188 |> Map.put("url", nil)
189 |> Map.put("uri", nil)
190 |> Map.put("created_at", nil)
191 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
193 assert real_status == fake_status
196 test "posting a status with OGP link preview", %{conn: conn} do
197 Pleroma.Config.put([:rich_media, :enabled], true)
202 |> assign(:user, user)
203 |> post("/api/v1/statuses", %{
204 "status" => "http://example.com/ogp"
207 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
208 assert Activity.get_by_id(id)
209 Pleroma.Config.put([:rich_media, :enabled], false)
212 test "posting a direct status", %{conn: conn} do
213 user1 = insert(:user)
214 user2 = insert(:user)
215 content = "direct cofe @#{user2.nickname}"
219 |> assign(:user, user1)
220 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
222 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
223 assert activity = Activity.get_by_id(id)
224 assert activity.recipients == [user2.ap_id, user1.ap_id]
225 assert activity.data["to"] == [user2.ap_id]
226 assert activity.data["cc"] == []
229 test "direct timeline", %{conn: conn} do
230 user_one = insert(:user)
231 user_two = insert(:user)
233 {:ok, user_two} = User.follow(user_two, user_one)
236 CommonAPI.post(user_one, %{
237 "status" => "Hi @#{user_two.nickname}!",
238 "visibility" => "direct"
241 {:ok, _follower_only} =
242 CommonAPI.post(user_one, %{
243 "status" => "Hi @#{user_two.nickname}!",
244 "visibility" => "private"
247 # Only direct should be visible here
250 |> assign(:user, user_two)
251 |> get("api/v1/timelines/direct")
253 [status] = json_response(res_conn, 200)
255 assert %{"visibility" => "direct"} = status
256 assert status["url"] != direct.data["id"]
258 # User should be able to see his own direct message
261 |> assign(:user, user_one)
262 |> get("api/v1/timelines/direct")
264 [status] = json_response(res_conn, 200)
266 assert %{"visibility" => "direct"} = status
268 # Both should be visible here
271 |> assign(:user, user_two)
272 |> get("api/v1/timelines/home")
274 [_s1, _s2] = json_response(res_conn, 200)
277 Enum.each(1..20, fn _ ->
279 CommonAPI.post(user_one, %{
280 "status" => "Hi @#{user_two.nickname}!",
281 "visibility" => "direct"
287 |> assign(:user, user_two)
288 |> get("api/v1/timelines/direct")
290 statuses = json_response(res_conn, 200)
291 assert length(statuses) == 20
295 |> assign(:user, user_two)
296 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
298 [status] = json_response(res_conn, 200)
300 assert status["url"] != direct.data["id"]
303 test "Conversations", %{conn: conn} do
304 user_one = insert(:user)
305 user_two = insert(:user)
307 {:ok, user_two} = User.follow(user_two, user_one)
310 CommonAPI.post(user_one, %{
311 "status" => "Hi @#{user_two.nickname}!",
312 "visibility" => "direct"
315 {:ok, _follower_only} =
316 CommonAPI.post(user_one, %{
317 "status" => "Hi @#{user_two.nickname}!",
318 "visibility" => "private"
323 |> assign(:user, user_one)
324 |> get("/api/v1/conversations")
326 assert response = json_response(res_conn, 200)
331 "accounts" => res_accounts,
332 "last_status" => res_last_status,
337 assert unread == true
338 assert res_last_status == direct.id
340 # Apparently undocumented API endpoint
343 |> assign(:user, user_one)
344 |> post("/api/v1/conversations/#{res_id}/read")
346 assert response = json_response(res_conn, 200)
347 assert response["unread"] == false
349 # (vanilla) Mastodon frontend behaviour
352 |> assign(:user, user_one)
353 |> get("/api/v1/statuses/#{res_last_status}/context")
355 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
358 test "doesn't include DMs from blocked users", %{conn: conn} do
359 blocker = insert(:user)
360 blocked = insert(:user)
362 {:ok, blocker} = User.block(blocker, blocked)
364 {:ok, _blocked_direct} =
365 CommonAPI.post(blocked, %{
366 "status" => "Hi @#{blocker.nickname}!",
367 "visibility" => "direct"
371 CommonAPI.post(user, %{
372 "status" => "Hi @#{blocker.nickname}!",
373 "visibility" => "direct"
378 |> assign(:user, user)
379 |> get("api/v1/timelines/direct")
381 [status] = json_response(res_conn, 200)
382 assert status["id"] == direct.id
385 test "replying to a status", %{conn: conn} do
388 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
392 |> assign(:user, user)
393 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
395 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
397 activity = Activity.get_by_id(id)
399 assert activity.data["context"] == replied_to.data["context"]
400 assert activity.data["object"]["inReplyToStatusId"] == replied_to.id
403 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
408 |> assign(:user, user)
409 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
411 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
413 activity = Activity.get_by_id(id)
418 test "verify_credentials", %{conn: conn} do
423 |> assign(:user, user)
424 |> get("/api/v1/accounts/verify_credentials")
426 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
427 assert id == to_string(user.id)
430 test "verify_credentials default scope unlisted", %{conn: conn} do
431 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "unlisted"}})
435 |> assign(:user, user)
436 |> get("/api/v1/accounts/verify_credentials")
438 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
439 assert id == to_string(user.id)
442 test "apps/verify_credentials", %{conn: conn} do
443 token = insert(:oauth_token)
447 |> assign(:user, token.user)
448 |> assign(:token, token)
449 |> get("/api/v1/apps/verify_credentials")
451 app = Repo.preload(token, :app).app
454 "name" => app.client_name,
455 "website" => app.website,
456 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
459 assert expected == json_response(conn, 200)
462 test "creates an oauth app", %{conn: conn} do
464 app_attrs = build(:oauth_app)
468 |> assign(:user, user)
469 |> post("/api/v1/apps", %{
470 client_name: app_attrs.client_name,
471 redirect_uris: app_attrs.redirect_uris
474 [app] = Repo.all(App)
477 "name" => app.client_name,
478 "website" => app.website,
479 "client_id" => app.client_id,
480 "client_secret" => app.client_secret,
481 "id" => app.id |> to_string(),
482 "redirect_uri" => app.redirect_uris,
483 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
486 assert expected == json_response(conn, 200)
489 test "get a status", %{conn: conn} do
490 activity = insert(:note_activity)
494 |> get("/api/v1/statuses/#{activity.id}")
496 assert %{"id" => id} = json_response(conn, 200)
497 assert id == to_string(activity.id)
500 describe "deleting a status" do
501 test "when you created it", %{conn: conn} do
502 activity = insert(:note_activity)
503 author = User.get_by_ap_id(activity.data["actor"])
507 |> assign(:user, author)
508 |> delete("/api/v1/statuses/#{activity.id}")
510 assert %{} = json_response(conn, 200)
512 refute Activity.get_by_id(activity.id)
515 test "when you didn't create it", %{conn: conn} do
516 activity = insert(:note_activity)
521 |> assign(:user, user)
522 |> delete("/api/v1/statuses/#{activity.id}")
524 assert %{"error" => _} = json_response(conn, 403)
526 assert Activity.get_by_id(activity.id) == activity
529 test "when you're an admin or moderator", %{conn: conn} do
530 activity1 = insert(:note_activity)
531 activity2 = insert(:note_activity)
532 admin = insert(:user, info: %{is_admin: true})
533 moderator = insert(:user, info: %{is_moderator: true})
537 |> assign(:user, admin)
538 |> delete("/api/v1/statuses/#{activity1.id}")
540 assert %{} = json_response(res_conn, 200)
544 |> assign(:user, moderator)
545 |> delete("/api/v1/statuses/#{activity2.id}")
547 assert %{} = json_response(res_conn, 200)
549 refute Activity.get_by_id(activity1.id)
550 refute Activity.get_by_id(activity2.id)
554 describe "filters" do
555 test "creating a filter", %{conn: conn} do
558 filter = %Pleroma.Filter{
565 |> assign(:user, user)
566 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
568 assert response = json_response(conn, 200)
569 assert response["phrase"] == filter.phrase
570 assert response["context"] == filter.context
571 assert response["id"] != nil
572 assert response["id"] != ""
575 test "fetching a list of filters", %{conn: conn} do
578 query_one = %Pleroma.Filter{
585 query_two = %Pleroma.Filter{
592 {:ok, filter_one} = Pleroma.Filter.create(query_one)
593 {:ok, filter_two} = Pleroma.Filter.create(query_two)
597 |> assign(:user, user)
598 |> get("/api/v1/filters")
599 |> json_response(200)
605 filters: [filter_two, filter_one]
609 test "get a filter", %{conn: conn} do
612 query = %Pleroma.Filter{
619 {:ok, filter} = Pleroma.Filter.create(query)
623 |> assign(:user, user)
624 |> get("/api/v1/filters/#{filter.filter_id}")
626 assert _response = json_response(conn, 200)
629 test "update a filter", %{conn: conn} do
632 query = %Pleroma.Filter{
639 {:ok, _filter} = Pleroma.Filter.create(query)
641 new = %Pleroma.Filter{
648 |> assign(:user, user)
649 |> put("/api/v1/filters/#{query.filter_id}", %{
654 assert response = json_response(conn, 200)
655 assert response["phrase"] == new.phrase
656 assert response["context"] == new.context
659 test "delete a filter", %{conn: conn} do
662 query = %Pleroma.Filter{
669 {:ok, filter} = Pleroma.Filter.create(query)
673 |> assign(:user, user)
674 |> delete("/api/v1/filters/#{filter.filter_id}")
676 assert response = json_response(conn, 200)
677 assert response == %{}
682 test "creating a list", %{conn: conn} do
687 |> assign(:user, user)
688 |> post("/api/v1/lists", %{"title" => "cuties"})
690 assert %{"title" => title} = json_response(conn, 200)
691 assert title == "cuties"
694 test "adding users to a list", %{conn: conn} do
696 other_user = insert(:user)
697 {:ok, list} = Pleroma.List.create("name", user)
701 |> assign(:user, user)
702 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
704 assert %{} == json_response(conn, 200)
705 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
706 assert following == [other_user.follower_address]
709 test "removing users from a list", %{conn: conn} do
711 other_user = insert(:user)
712 third_user = insert(:user)
713 {:ok, list} = Pleroma.List.create("name", user)
714 {:ok, list} = Pleroma.List.follow(list, other_user)
715 {:ok, list} = Pleroma.List.follow(list, third_user)
719 |> assign(:user, user)
720 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
722 assert %{} == json_response(conn, 200)
723 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
724 assert following == [third_user.follower_address]
727 test "listing users in a list", %{conn: conn} do
729 other_user = insert(:user)
730 {:ok, list} = Pleroma.List.create("name", user)
731 {:ok, list} = Pleroma.List.follow(list, other_user)
735 |> assign(:user, user)
736 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
738 assert [%{"id" => id}] = json_response(conn, 200)
739 assert id == to_string(other_user.id)
742 test "retrieving a list", %{conn: conn} do
744 {:ok, list} = Pleroma.List.create("name", user)
748 |> assign(:user, user)
749 |> get("/api/v1/lists/#{list.id}")
751 assert %{"id" => id} = json_response(conn, 200)
752 assert id == to_string(list.id)
755 test "renaming a list", %{conn: conn} do
757 {:ok, list} = Pleroma.List.create("name", user)
761 |> assign(:user, user)
762 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
764 assert %{"title" => name} = json_response(conn, 200)
765 assert name == "newname"
768 test "deleting a list", %{conn: conn} do
770 {:ok, list} = Pleroma.List.create("name", user)
774 |> assign(:user, user)
775 |> delete("/api/v1/lists/#{list.id}")
777 assert %{} = json_response(conn, 200)
778 assert is_nil(Repo.get(Pleroma.List, list.id))
781 test "list timeline", %{conn: conn} do
783 other_user = insert(:user)
784 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
785 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
786 {:ok, list} = Pleroma.List.create("name", user)
787 {:ok, list} = Pleroma.List.follow(list, other_user)
791 |> assign(:user, user)
792 |> get("/api/v1/timelines/list/#{list.id}")
794 assert [%{"id" => id}] = json_response(conn, 200)
796 assert id == to_string(activity_two.id)
799 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
801 other_user = insert(:user)
802 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
804 {:ok, _activity_two} =
805 TwitterAPI.create_status(other_user, %{
806 "status" => "Marisa is cute.",
807 "visibility" => "private"
810 {:ok, list} = Pleroma.List.create("name", user)
811 {:ok, list} = Pleroma.List.follow(list, other_user)
815 |> assign(:user, user)
816 |> get("/api/v1/timelines/list/#{list.id}")
818 assert [%{"id" => id}] = json_response(conn, 200)
820 assert id == to_string(activity_one.id)
824 describe "notifications" do
825 test "list of notifications", %{conn: conn} do
827 other_user = insert(:user)
830 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
832 {:ok, [_notification]} = Notification.create_notifications(activity)
836 |> assign(:user, user)
837 |> get("/api/v1/notifications")
840 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
842 }\">@<span>#{user.nickname}</span></a></span>"
844 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
845 assert response == expected_response
848 test "getting a single notification", %{conn: conn} do
850 other_user = insert(:user)
853 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
855 {:ok, [notification]} = Notification.create_notifications(activity)
859 |> assign(:user, user)
860 |> get("/api/v1/notifications/#{notification.id}")
863 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
865 }\">@<span>#{user.nickname}</span></a></span>"
867 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
868 assert response == expected_response
871 test "dismissing a single notification", %{conn: conn} do
873 other_user = insert(:user)
876 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
878 {:ok, [notification]} = Notification.create_notifications(activity)
882 |> assign(:user, user)
883 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
885 assert %{} = json_response(conn, 200)
888 test "clearing all notifications", %{conn: conn} do
890 other_user = insert(:user)
893 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
895 {:ok, [_notification]} = Notification.create_notifications(activity)
899 |> assign(:user, user)
900 |> post("/api/v1/notifications/clear")
902 assert %{} = json_response(conn, 200)
906 |> assign(:user, user)
907 |> get("/api/v1/notifications")
909 assert all = json_response(conn, 200)
913 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
915 other_user = insert(:user)
917 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
918 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
919 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
920 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
922 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
923 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
924 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
925 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
929 |> assign(:user, user)
934 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
936 result = json_response(conn_res, 200)
937 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
942 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
944 result = json_response(conn_res, 200)
945 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
950 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
952 result = json_response(conn_res, 200)
953 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
956 test "filters notifications using exclude_types", %{conn: conn} do
958 other_user = insert(:user)
960 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
961 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
962 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
963 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
964 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
966 mention_notification_id =
967 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
969 favorite_notification_id =
970 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
972 reblog_notification_id =
973 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
975 follow_notification_id =
976 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
980 |> assign(:user, user)
983 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
985 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
988 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
990 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
993 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
995 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
998 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1000 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1004 describe "reblogging" do
1005 test "reblogs and returns the reblogged status", %{conn: conn} do
1006 activity = insert(:note_activity)
1007 user = insert(:user)
1011 |> assign(:user, user)
1012 |> post("/api/v1/statuses/#{activity.id}/reblog")
1014 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
1015 json_response(conn, 200)
1017 assert to_string(activity.id) == id
1021 describe "unreblogging" do
1022 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1023 activity = insert(:note_activity)
1024 user = insert(:user)
1026 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1030 |> assign(:user, user)
1031 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1033 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1035 assert to_string(activity.id) == id
1039 describe "favoriting" do
1040 test "favs a status and returns it", %{conn: conn} do
1041 activity = insert(:note_activity)
1042 user = insert(:user)
1046 |> assign(:user, user)
1047 |> post("/api/v1/statuses/#{activity.id}/favourite")
1049 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1050 json_response(conn, 200)
1052 assert to_string(activity.id) == id
1055 test "returns 500 for a wrong id", %{conn: conn} do
1056 user = insert(:user)
1060 |> assign(:user, user)
1061 |> post("/api/v1/statuses/1/favourite")
1062 |> json_response(500)
1064 assert resp == "Something went wrong"
1068 describe "unfavoriting" do
1069 test "unfavorites a status and returns it", %{conn: conn} do
1070 activity = insert(:note_activity)
1071 user = insert(:user)
1073 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1077 |> assign(:user, user)
1078 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1080 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1081 json_response(conn, 200)
1083 assert to_string(activity.id) == id
1087 describe "user timelines" do
1088 test "gets a users statuses", %{conn: conn} do
1089 user_one = insert(:user)
1090 user_two = insert(:user)
1091 user_three = insert(:user)
1093 {:ok, user_three} = User.follow(user_three, user_one)
1095 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1097 {:ok, direct_activity} =
1098 CommonAPI.post(user_one, %{
1099 "status" => "Hi, @#{user_two.nickname}.",
1100 "visibility" => "direct"
1103 {:ok, private_activity} =
1104 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1108 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1110 assert [%{"id" => id}] = json_response(resp, 200)
1111 assert id == to_string(activity.id)
1115 |> assign(:user, user_two)
1116 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1118 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1119 assert id_one == to_string(direct_activity.id)
1120 assert id_two == to_string(activity.id)
1124 |> assign(:user, user_three)
1125 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1127 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1128 assert id_one == to_string(private_activity.id)
1129 assert id_two == to_string(activity.id)
1132 test "unimplemented pinned statuses feature", %{conn: conn} do
1133 note = insert(:note_activity)
1134 user = User.get_by_ap_id(note.data["actor"])
1138 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1140 assert json_response(conn, 200) == []
1143 test "gets an users media", %{conn: conn} do
1144 note = insert(:note_activity)
1145 user = User.get_by_ap_id(note.data["actor"])
1147 file = %Plug.Upload{
1148 content_type: "image/jpg",
1149 path: Path.absname("test/fixtures/image.jpg"),
1150 filename: "an_image.jpg"
1154 TwitterAPI.upload(file, user, "json")
1158 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1162 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1164 assert [%{"id" => id}] = json_response(conn, 200)
1165 assert id == to_string(image_post.id)
1169 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1171 assert [%{"id" => id}] = json_response(conn, 200)
1172 assert id == to_string(image_post.id)
1175 test "gets a user's statuses without reblogs", %{conn: conn} do
1176 user = insert(:user)
1177 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1178 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1182 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1184 assert [%{"id" => id}] = json_response(conn, 200)
1185 assert id == to_string(post.id)
1189 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1191 assert [%{"id" => id}] = json_response(conn, 200)
1192 assert id == to_string(post.id)
1196 describe "user relationships" do
1197 test "returns the relationships for the current user", %{conn: conn} do
1198 user = insert(:user)
1199 other_user = insert(:user)
1200 {:ok, user} = User.follow(user, other_user)
1204 |> assign(:user, user)
1205 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1207 assert [relationship] = json_response(conn, 200)
1209 assert to_string(other_user.id) == relationship["id"]
1213 describe "locked accounts" do
1214 test "/api/v1/follow_requests works" do
1215 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1216 other_user = insert(:user)
1218 {:ok, _activity} = ActivityPub.follow(other_user, user)
1220 user = User.get_by_id(user.id)
1221 other_user = User.get_by_id(other_user.id)
1223 assert User.following?(other_user, user) == false
1227 |> assign(:user, user)
1228 |> get("/api/v1/follow_requests")
1230 assert [relationship] = json_response(conn, 200)
1231 assert to_string(other_user.id) == relationship["id"]
1234 test "/api/v1/follow_requests/:id/authorize works" do
1235 user = insert(:user, %{info: %User.Info{locked: true}})
1236 other_user = insert(:user)
1238 {:ok, _activity} = ActivityPub.follow(other_user, user)
1240 user = User.get_by_id(user.id)
1241 other_user = User.get_by_id(other_user.id)
1243 assert User.following?(other_user, user) == false
1247 |> assign(:user, user)
1248 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1250 assert relationship = json_response(conn, 200)
1251 assert to_string(other_user.id) == relationship["id"]
1253 user = User.get_by_id(user.id)
1254 other_user = User.get_by_id(other_user.id)
1256 assert User.following?(other_user, user) == true
1259 test "verify_credentials", %{conn: conn} do
1260 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "private"}})
1264 |> assign(:user, user)
1265 |> get("/api/v1/accounts/verify_credentials")
1267 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1268 assert id == to_string(user.id)
1271 test "/api/v1/follow_requests/:id/reject works" do
1272 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1273 other_user = insert(:user)
1275 {:ok, _activity} = ActivityPub.follow(other_user, user)
1277 user = User.get_by_id(user.id)
1281 |> assign(:user, user)
1282 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1284 assert relationship = json_response(conn, 200)
1285 assert to_string(other_user.id) == relationship["id"]
1287 user = User.get_by_id(user.id)
1288 other_user = User.get_by_id(other_user.id)
1290 assert User.following?(other_user, user) == false
1294 test "account fetching", %{conn: conn} do
1295 user = insert(:user)
1299 |> get("/api/v1/accounts/#{user.id}")
1301 assert %{"id" => id} = json_response(conn, 200)
1302 assert id == to_string(user.id)
1306 |> get("/api/v1/accounts/-1")
1308 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1311 test "account fetching also works nickname", %{conn: conn} do
1312 user = insert(:user)
1316 |> get("/api/v1/accounts/#{user.nickname}")
1318 assert %{"id" => id} = json_response(conn, 200)
1319 assert id == user.id
1322 test "media upload", %{conn: conn} do
1323 file = %Plug.Upload{
1324 content_type: "image/jpg",
1325 path: Path.absname("test/fixtures/image.jpg"),
1326 filename: "an_image.jpg"
1329 desc = "Description of the image"
1331 user = insert(:user)
1335 |> assign(:user, user)
1336 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1338 assert media = json_response(conn, 200)
1340 assert media["type"] == "image"
1341 assert media["description"] == desc
1344 object = Repo.get(Object, media["id"])
1345 assert object.data["actor"] == User.ap_id(user)
1348 test "hashtag timeline", %{conn: conn} do
1349 following = insert(:user)
1352 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1354 {:ok, [_activity]} =
1355 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1359 |> get("/api/v1/timelines/tag/2hu")
1361 assert [%{"id" => id}] = json_response(nconn, 200)
1363 assert id == to_string(activity.id)
1365 # works for different capitalization too
1368 |> get("/api/v1/timelines/tag/2HU")
1370 assert [%{"id" => id}] = json_response(nconn, 200)
1372 assert id == to_string(activity.id)
1376 test "multi-hashtag timeline", %{conn: conn} do
1377 user = insert(:user)
1379 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1380 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1381 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1385 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1387 [status_none, status_test1, status_test] = json_response(any_test, 200)
1389 assert to_string(activity_test.id) == status_test["id"]
1390 assert to_string(activity_test1.id) == status_test1["id"]
1391 assert to_string(activity_none.id) == status_none["id"]
1395 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1397 assert [status_test1] == json_response(restricted_test, 200)
1399 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1401 assert [status_none] == json_response(all_test, 200)
1404 test "getting followers", %{conn: conn} do
1405 user = insert(:user)
1406 other_user = insert(:user)
1407 {:ok, user} = User.follow(user, other_user)
1411 |> get("/api/v1/accounts/#{other_user.id}/followers")
1413 assert [%{"id" => id}] = json_response(conn, 200)
1414 assert id == to_string(user.id)
1417 test "getting followers, hide_followers", %{conn: conn} do
1418 user = insert(:user)
1419 other_user = insert(:user, %{info: %{hide_followers: true}})
1420 {:ok, _user} = User.follow(user, other_user)
1424 |> get("/api/v1/accounts/#{other_user.id}/followers")
1426 assert [] == json_response(conn, 200)
1429 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1430 user = insert(:user)
1431 other_user = insert(:user, %{info: %{hide_followers: true}})
1432 {:ok, _user} = User.follow(user, other_user)
1436 |> assign(:user, other_user)
1437 |> get("/api/v1/accounts/#{other_user.id}/followers")
1439 refute [] == json_response(conn, 200)
1442 test "getting followers, pagination", %{conn: conn} do
1443 user = insert(:user)
1444 follower1 = insert(:user)
1445 follower2 = insert(:user)
1446 follower3 = insert(:user)
1447 {:ok, _} = User.follow(follower1, user)
1448 {:ok, _} = User.follow(follower2, user)
1449 {:ok, _} = User.follow(follower3, user)
1453 |> assign(:user, user)
1457 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1459 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1460 assert id3 == follower3.id
1461 assert id2 == follower2.id
1465 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1467 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1468 assert id2 == follower2.id
1469 assert id1 == follower1.id
1473 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1475 assert [%{"id" => id2}] = json_response(res_conn, 200)
1476 assert id2 == follower2.id
1478 assert [link_header] = get_resp_header(res_conn, "link")
1479 assert link_header =~ ~r/since_id=#{follower2.id}/
1480 assert link_header =~ ~r/max_id=#{follower2.id}/
1483 test "getting following", %{conn: conn} do
1484 user = insert(:user)
1485 other_user = insert(:user)
1486 {:ok, user} = User.follow(user, other_user)
1490 |> get("/api/v1/accounts/#{user.id}/following")
1492 assert [%{"id" => id}] = json_response(conn, 200)
1493 assert id == to_string(other_user.id)
1496 test "getting following, hide_follows", %{conn: conn} do
1497 user = insert(:user, %{info: %{hide_follows: true}})
1498 other_user = insert(:user)
1499 {:ok, user} = User.follow(user, other_user)
1503 |> get("/api/v1/accounts/#{user.id}/following")
1505 assert [] == json_response(conn, 200)
1508 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1509 user = insert(:user, %{info: %{hide_follows: true}})
1510 other_user = insert(:user)
1511 {:ok, user} = User.follow(user, other_user)
1515 |> assign(:user, user)
1516 |> get("/api/v1/accounts/#{user.id}/following")
1518 refute [] == json_response(conn, 200)
1521 test "getting following, pagination", %{conn: conn} do
1522 user = insert(:user)
1523 following1 = insert(:user)
1524 following2 = insert(:user)
1525 following3 = insert(:user)
1526 {:ok, _} = User.follow(user, following1)
1527 {:ok, _} = User.follow(user, following2)
1528 {:ok, _} = User.follow(user, following3)
1532 |> assign(:user, user)
1536 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1538 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1539 assert id3 == following3.id
1540 assert id2 == following2.id
1544 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1546 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1547 assert id2 == following2.id
1548 assert id1 == following1.id
1552 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1554 assert [%{"id" => id2}] = json_response(res_conn, 200)
1555 assert id2 == following2.id
1557 assert [link_header] = get_resp_header(res_conn, "link")
1558 assert link_header =~ ~r/since_id=#{following2.id}/
1559 assert link_header =~ ~r/max_id=#{following2.id}/
1562 test "following / unfollowing a user", %{conn: conn} do
1563 user = insert(:user)
1564 other_user = insert(:user)
1568 |> assign(:user, user)
1569 |> post("/api/v1/accounts/#{other_user.id}/follow")
1571 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1573 user = User.get_by_id(user.id)
1577 |> assign(:user, user)
1578 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1580 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1582 user = User.get_by_id(user.id)
1586 |> assign(:user, user)
1587 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1589 assert %{"id" => id} = json_response(conn, 200)
1590 assert id == to_string(other_user.id)
1593 test "muting / unmuting a user", %{conn: conn} do
1594 user = insert(:user)
1595 other_user = insert(:user)
1599 |> assign(:user, user)
1600 |> post("/api/v1/accounts/#{other_user.id}/mute")
1602 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1604 user = User.get_by_id(user.id)
1608 |> assign(:user, user)
1609 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1611 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1614 test "getting a list of mutes", %{conn: conn} do
1615 user = insert(:user)
1616 other_user = insert(:user)
1618 {:ok, user} = User.mute(user, other_user)
1622 |> assign(:user, user)
1623 |> get("/api/v1/mutes")
1625 other_user_id = to_string(other_user.id)
1626 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1629 test "blocking / unblocking a user", %{conn: conn} do
1630 user = insert(:user)
1631 other_user = insert(:user)
1635 |> assign(:user, user)
1636 |> post("/api/v1/accounts/#{other_user.id}/block")
1638 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1640 user = User.get_by_id(user.id)
1644 |> assign(:user, user)
1645 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1647 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1650 test "getting a list of blocks", %{conn: conn} do
1651 user = insert(:user)
1652 other_user = insert(:user)
1654 {:ok, user} = User.block(user, other_user)
1658 |> assign(:user, user)
1659 |> get("/api/v1/blocks")
1661 other_user_id = to_string(other_user.id)
1662 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1665 test "blocking / unblocking a domain", %{conn: conn} do
1666 user = insert(:user)
1667 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1671 |> assign(:user, user)
1672 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1674 assert %{} = json_response(conn, 200)
1675 user = User.get_cached_by_ap_id(user.ap_id)
1676 assert User.blocks?(user, other_user)
1680 |> assign(:user, user)
1681 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1683 assert %{} = json_response(conn, 200)
1684 user = User.get_cached_by_ap_id(user.ap_id)
1685 refute User.blocks?(user, other_user)
1688 test "getting a list of domain blocks", %{conn: conn} do
1689 user = insert(:user)
1691 {:ok, user} = User.block_domain(user, "bad.site")
1692 {:ok, user} = User.block_domain(user, "even.worse.site")
1696 |> assign(:user, user)
1697 |> get("/api/v1/domain_blocks")
1699 domain_blocks = json_response(conn, 200)
1701 assert "bad.site" in domain_blocks
1702 assert "even.worse.site" in domain_blocks
1705 test "unimplemented follow_requests, blocks, domain blocks" do
1706 user = insert(:user)
1708 ["blocks", "domain_blocks", "follow_requests"]
1709 |> Enum.each(fn endpoint ->
1712 |> assign(:user, user)
1713 |> get("/api/v1/#{endpoint}")
1715 assert [] = json_response(conn, 200)
1719 test "account search", %{conn: conn} do
1720 user = insert(:user)
1721 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1722 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1726 |> assign(:user, user)
1727 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1728 |> json_response(200)
1730 result_ids = for result <- results, do: result["acct"]
1732 assert user_two.nickname in result_ids
1733 assert user_three.nickname in result_ids
1737 |> assign(:user, user)
1738 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
1739 |> json_response(200)
1741 result_ids = for result <- results, do: result["acct"]
1743 assert user_three.nickname in result_ids
1746 test "search", %{conn: conn} do
1747 user = insert(:user)
1748 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1749 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1751 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
1754 CommonAPI.post(user, %{
1755 "status" => "This is about 2hu, but private",
1756 "visibility" => "private"
1759 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
1763 |> get("/api/v1/search", %{"q" => "2hu"})
1765 assert results = json_response(conn, 200)
1767 [account | _] = results["accounts"]
1768 assert account["id"] == to_string(user_three.id)
1770 assert results["hashtags"] == []
1772 [status] = results["statuses"]
1773 assert status["id"] == to_string(activity.id)
1776 test "search fetches remote statuses", %{conn: conn} do
1780 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
1782 assert results = json_response(conn, 200)
1784 [status] = results["statuses"]
1785 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1789 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
1791 CommonAPI.post(insert(:user), %{
1792 "status" => "This is about 2hu, but private",
1793 "visibility" => "private"
1799 |> get("/api/v1/search", %{"q" => activity.data["object"]["id"]})
1801 assert results = json_response(conn, 200)
1803 [] = results["statuses"]
1807 test "search fetches remote accounts", %{conn: conn} do
1810 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
1812 assert results = json_response(conn, 200)
1813 [account] = results["accounts"]
1814 assert account["acct"] == "shp@social.heldscal.la"
1817 test "returns the favorites of a user", %{conn: conn} do
1818 user = insert(:user)
1819 other_user = insert(:user)
1821 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1822 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1824 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1828 |> assign(:user, user)
1829 |> get("/api/v1/favourites")
1831 assert [status] = json_response(first_conn, 200)
1832 assert status["id"] == to_string(activity.id)
1834 assert [{"link", _link_header}] =
1835 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1837 # Honours query params
1838 {:ok, second_activity} =
1839 CommonAPI.post(other_user, %{
1841 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1844 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1846 last_like = status["id"]
1850 |> assign(:user, user)
1851 |> get("/api/v1/favourites?since_id=#{last_like}")
1853 assert [second_status] = json_response(second_conn, 200)
1854 assert second_status["id"] == to_string(second_activity.id)
1858 |> assign(:user, user)
1859 |> get("/api/v1/favourites?limit=0")
1861 assert [] = json_response(third_conn, 200)
1864 describe "updating credentials" do
1865 test "updates the user's bio", %{conn: conn} do
1866 user = insert(:user)
1867 user2 = insert(:user)
1871 |> assign(:user, user)
1872 |> patch("/api/v1/accounts/update_credentials", %{
1873 "note" => "I drink #cofe with @#{user2.nickname}"
1876 assert user = json_response(conn, 200)
1878 assert user["note"] ==
1879 ~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=") <>
1881 ~s(" class="u-url mention" href=") <>
1882 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
1885 test "updates the user's locking status", %{conn: conn} do
1886 user = insert(:user)
1890 |> assign(:user, user)
1891 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
1893 assert user = json_response(conn, 200)
1894 assert user["locked"] == true
1897 test "updates the user's name", %{conn: conn} do
1898 user = insert(:user)
1902 |> assign(:user, user)
1903 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
1905 assert user = json_response(conn, 200)
1906 assert user["display_name"] == "markorepairs"
1909 test "updates the user's avatar", %{conn: conn} do
1910 user = insert(:user)
1912 new_avatar = %Plug.Upload{
1913 content_type: "image/jpg",
1914 path: Path.absname("test/fixtures/image.jpg"),
1915 filename: "an_image.jpg"
1920 |> assign(:user, user)
1921 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
1923 assert user_response = json_response(conn, 200)
1924 assert user_response["avatar"] != User.avatar_url(user)
1927 test "updates the user's banner", %{conn: conn} do
1928 user = insert(:user)
1930 new_header = %Plug.Upload{
1931 content_type: "image/jpg",
1932 path: Path.absname("test/fixtures/image.jpg"),
1933 filename: "an_image.jpg"
1938 |> assign(:user, user)
1939 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
1941 assert user_response = json_response(conn, 200)
1942 assert user_response["header"] != User.banner_url(user)
1945 test "requires 'write' permission", %{conn: conn} do
1946 token1 = insert(:oauth_token, scopes: ["read"])
1947 token2 = insert(:oauth_token, scopes: ["write", "follow"])
1949 for token <- [token1, token2] do
1952 |> put_req_header("authorization", "Bearer #{token.token}")
1953 |> patch("/api/v1/accounts/update_credentials", %{})
1955 if token == token1 do
1956 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
1958 assert json_response(conn, 200)
1964 test "get instance information", %{conn: conn} do
1965 conn = get(conn, "/api/v1/instance")
1966 assert result = json_response(conn, 200)
1968 # Note: not checking for "max_toot_chars" since it's optional
1976 "streaming_api" => _
1981 "registrations" => _
1985 test "get instance stats", %{conn: conn} do
1986 user = insert(:user, %{local: true})
1988 user2 = insert(:user, %{local: true})
1989 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1991 insert(:user, %{local: false, nickname: "u@peer1.com"})
1992 insert(:user, %{local: false, nickname: "u@peer2.com"})
1994 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
1996 # Stats should count users with missing or nil `info.deactivated` value
1997 user = User.get_by_id(user.id)
1998 info_change = Changeset.change(user.info, %{deactivated: nil})
2002 |> Changeset.change()
2003 |> Changeset.put_embed(:info, info_change)
2004 |> User.update_and_set_cache()
2006 Pleroma.Stats.update_stats()
2008 conn = get(conn, "/api/v1/instance")
2010 assert result = json_response(conn, 200)
2012 stats = result["stats"]
2015 assert stats["user_count"] == 1
2016 assert stats["status_count"] == 1
2017 assert stats["domain_count"] == 2
2020 test "get peers", %{conn: conn} do
2021 insert(:user, %{local: false, nickname: "u@peer1.com"})
2022 insert(:user, %{local: false, nickname: "u@peer2.com"})
2024 Pleroma.Stats.update_stats()
2026 conn = get(conn, "/api/v1/instance/peers")
2028 assert result = json_response(conn, 200)
2030 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2033 test "put settings", %{conn: conn} do
2034 user = insert(:user)
2038 |> assign(:user, user)
2039 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2041 assert _result = json_response(conn, 200)
2043 user = User.get_cached_by_ap_id(user.ap_id)
2044 assert user.info.settings == %{"programming" => "socks"}
2047 describe "pinned statuses" do
2049 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2051 user = insert(:user)
2052 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2054 [user: user, activity: activity]
2057 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2058 {:ok, _} = CommonAPI.pin(activity.id, user)
2062 |> assign(:user, user)
2063 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2064 |> json_response(200)
2066 id_str = to_string(activity.id)
2068 assert [%{"id" => ^id_str, "pinned" => true}] = result
2071 test "pin status", %{conn: conn, user: user, activity: activity} do
2072 id_str = to_string(activity.id)
2074 assert %{"id" => ^id_str, "pinned" => true} =
2076 |> assign(:user, user)
2077 |> post("/api/v1/statuses/#{activity.id}/pin")
2078 |> json_response(200)
2080 assert [%{"id" => ^id_str, "pinned" => true}] =
2082 |> assign(:user, user)
2083 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2084 |> json_response(200)
2087 test "unpin status", %{conn: conn, user: user, activity: activity} do
2088 {:ok, _} = CommonAPI.pin(activity.id, user)
2090 id_str = to_string(activity.id)
2091 user = refresh_record(user)
2093 assert %{"id" => ^id_str, "pinned" => false} =
2095 |> assign(:user, user)
2096 |> post("/api/v1/statuses/#{activity.id}/unpin")
2097 |> json_response(200)
2101 |> assign(:user, user)
2102 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2103 |> json_response(200)
2106 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2107 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2109 id_str_one = to_string(activity_one.id)
2111 assert %{"id" => ^id_str_one, "pinned" => true} =
2113 |> assign(:user, user)
2114 |> post("/api/v1/statuses/#{id_str_one}/pin")
2115 |> json_response(200)
2117 user = refresh_record(user)
2119 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2121 |> assign(:user, user)
2122 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2123 |> json_response(400)
2126 test "Status rich-media Card", %{conn: conn, user: user} do
2127 Pleroma.Config.put([:rich_media, :enabled], true)
2128 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2132 |> get("/api/v1/statuses/#{activity.id}/card")
2133 |> json_response(200)
2135 assert response == %{
2136 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2137 "provider_name" => "www.imdb.com",
2138 "provider_url" => "http://www.imdb.com",
2139 "title" => "The Rock",
2141 "url" => "http://www.imdb.com/title/tt0117500/",
2142 "description" => nil,
2145 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2146 "title" => "The Rock",
2147 "type" => "video.movie",
2148 "url" => "http://www.imdb.com/title/tt0117500/"
2153 # works with private posts
2155 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2159 |> assign(:user, user)
2160 |> get("/api/v1/statuses/#{activity.id}/card")
2161 |> json_response(200)
2163 assert response_two == response
2165 Pleroma.Config.put([:rich_media, :enabled], false)
2170 user = insert(:user)
2171 for_user = insert(:user)
2174 CommonAPI.post(user, %{
2175 "status" => "heweoo?"
2179 CommonAPI.post(user, %{
2180 "status" => "heweoo!"
2185 |> assign(:user, for_user)
2186 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2188 assert json_response(response1, 200)["bookmarked"] == true
2192 |> assign(:user, for_user)
2193 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2195 assert json_response(response2, 200)["bookmarked"] == true
2199 |> assign(:user, for_user)
2200 |> get("/api/v1/bookmarks")
2202 assert [json_response(response2, 200), json_response(response1, 200)] ==
2203 json_response(bookmarks, 200)
2207 |> assign(:user, for_user)
2208 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2210 assert json_response(response1, 200)["bookmarked"] == false
2214 |> assign(:user, for_user)
2215 |> get("/api/v1/bookmarks")
2217 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2220 describe "conversation muting" do
2222 user = insert(:user)
2223 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2225 [user: user, activity: activity]
2228 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2229 id_str = to_string(activity.id)
2231 assert %{"id" => ^id_str, "muted" => true} =
2233 |> assign(:user, user)
2234 |> post("/api/v1/statuses/#{activity.id}/mute")
2235 |> json_response(200)
2238 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2239 {:ok, _} = CommonAPI.add_mute(user, activity)
2241 id_str = to_string(activity.id)
2242 user = refresh_record(user)
2244 assert %{"id" => ^id_str, "muted" => false} =
2246 |> assign(:user, user)
2247 |> post("/api/v1/statuses/#{activity.id}/unmute")
2248 |> json_response(200)
2252 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2253 user = insert(:user)
2257 |> assign(:user, user)
2258 |> get("/api/v1/pleroma/flavour")
2260 assert "glitch" == json_response(get_old_flavour, 200)
2264 |> assign(:user, user)
2265 |> post("/api/v1/pleroma/flavour/vanilla")
2267 assert "vanilla" == json_response(set_flavour, 200)
2271 |> assign(:user, user)
2272 |> post("/api/v1/pleroma/flavour/vanilla")
2274 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2277 describe "reports" do
2279 reporter = insert(:user)
2280 target_user = insert(:user)
2282 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2284 [reporter: reporter, target_user: target_user, activity: activity]
2287 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2288 assert %{"action_taken" => false, "id" => _} =
2290 |> assign(:user, reporter)
2291 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2292 |> json_response(200)
2295 test "submit a report with statuses and comment", %{
2298 target_user: target_user,
2301 assert %{"action_taken" => false, "id" => _} =
2303 |> assign(:user, reporter)
2304 |> post("/api/v1/reports", %{
2305 "account_id" => target_user.id,
2306 "status_ids" => [activity.id],
2307 "comment" => "bad status!"
2309 |> json_response(200)
2312 test "account_id is required", %{
2317 assert %{"error" => "Valid `account_id` required"} =
2319 |> assign(:user, reporter)
2320 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2321 |> json_response(400)
2324 test "comment must be up to the size specified in the config", %{
2327 target_user: target_user
2329 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2330 comment = String.pad_trailing("a", max_size + 1, "a")
2332 error = %{"error" => "Comment must be up to #{max_size} characters"}
2336 |> assign(:user, reporter)
2337 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2338 |> json_response(400)
2342 describe "link headers" do
2343 test "preserves parameters in link headers", %{conn: conn} do
2344 user = insert(:user)
2345 other_user = insert(:user)
2348 CommonAPI.post(other_user, %{
2349 "status" => "hi @#{user.nickname}",
2350 "visibility" => "public"
2354 CommonAPI.post(other_user, %{
2355 "status" => "hi @#{user.nickname}",
2356 "visibility" => "public"
2359 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2360 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2364 |> assign(:user, user)
2365 |> get("/api/v1/notifications", %{media_only: true})
2367 assert [link_header] = get_resp_header(conn, "link")
2368 assert link_header =~ ~r/media_only=true/
2369 assert link_header =~ ~r/since_id=#{notification2.id}/
2370 assert link_header =~ ~r/max_id=#{notification1.id}/
2374 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2375 # Need to set an old-style integer ID to reproduce the problem
2376 # (these are no longer assigned to new accounts but were preserved
2377 # for existing accounts during the migration to flakeIDs)
2378 user_one = insert(:user, %{id: 1212})
2379 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2383 |> get("/api/v1/accounts/#{user_one.id}")
2387 |> get("/api/v1/accounts/#{user_two.nickname}")
2391 |> get("/api/v1/accounts/#{user_two.id}")
2393 acc_one = json_response(resp_one, 200)
2394 acc_two = json_response(resp_two, 200)
2395 acc_three = json_response(resp_three, 200)
2396 refute acc_one == acc_two
2397 assert acc_two == acc_three
2400 describe "custom emoji" do
2401 test "with tags", %{conn: conn} do
2404 |> get("/api/v1/custom_emojis")
2405 |> json_response(200)
2407 assert Map.has_key?(emoji, "shortcode")
2408 assert Map.has_key?(emoji, "static_url")
2409 assert Map.has_key?(emoji, "tags")
2410 assert is_list(emoji["tags"])
2411 assert Map.has_key?(emoji, "url")
2412 assert Map.has_key?(emoji, "visible_in_picker")
2416 describe "index/2 redirections" do
2417 setup %{conn: conn} do
2421 signing_salt: "cooldude"
2426 |> Plug.Session.call(Plug.Session.init(session_opts))
2429 test_path = "/web/statuses/test"
2430 %{conn: conn, path: test_path}
2433 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2434 conn = get(conn, path)
2436 assert conn.status == 302
2437 assert redirected_to(conn) == "/web/login"
2440 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2441 token = insert(:oauth_token)
2445 |> assign(:user, token.user)
2446 |> put_session(:oauth_token, token.token)
2449 assert conn.status == 200
2452 test "saves referer path to session", %{conn: conn, path: path} do
2453 conn = get(conn, path)
2454 return_to = Plug.Conn.get_session(conn, :return_to)
2456 assert return_to == path
2459 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2460 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2461 auth = insert(:oauth_authorization, app: app)
2465 |> put_session(:return_to, path)
2466 |> get("/web/login", %{code: auth.token})
2468 assert conn.status == 302
2469 assert redirected_to(conn) == path
2472 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2473 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2474 auth = insert(:oauth_authorization, app: app)
2476 conn = get(conn, "/web/login", %{code: auth.token})
2478 assert conn.status == 302
2479 assert redirected_to(conn) == "/web/getting-started"
2483 describe "scheduled activities" do
2484 test "creates a scheduled activity", %{conn: conn} do
2485 user = insert(:user)
2486 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2490 |> assign(:user, user)
2491 |> post("/api/v1/statuses", %{
2492 "status" => "scheduled",
2493 "scheduled_at" => scheduled_at
2496 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2497 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2498 assert [] == Repo.all(Activity)
2501 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2502 user = insert(:user)
2503 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2505 file = %Plug.Upload{
2506 content_type: "image/jpg",
2507 path: Path.absname("test/fixtures/image.jpg"),
2508 filename: "an_image.jpg"
2511 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
2515 |> assign(:user, user)
2516 |> post("/api/v1/statuses", %{
2517 "media_ids" => [to_string(upload.id)],
2518 "status" => "scheduled",
2519 "scheduled_at" => scheduled_at
2522 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
2523 assert %{"type" => "image"} = media_attachment
2526 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
2528 user = insert(:user)
2531 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
2535 |> assign(:user, user)
2536 |> post("/api/v1/statuses", %{
2537 "status" => "not scheduled",
2538 "scheduled_at" => scheduled_at
2541 assert %{"content" => "not scheduled"} = json_response(conn, 200)
2542 assert [] == Repo.all(ScheduledActivity)
2545 test "returns error when daily user limit is exceeded", %{conn: conn} do
2546 user = insert(:user)
2549 NaiveDateTime.utc_now()
2550 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2551 |> NaiveDateTime.to_iso8601()
2553 attrs = %{params: %{}, scheduled_at: today}
2554 {:ok, _} = ScheduledActivity.create(user, attrs)
2555 {:ok, _} = ScheduledActivity.create(user, attrs)
2559 |> assign(:user, user)
2560 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
2562 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
2565 test "returns error when total user limit is exceeded", %{conn: conn} do
2566 user = insert(:user)
2569 NaiveDateTime.utc_now()
2570 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2571 |> NaiveDateTime.to_iso8601()
2574 NaiveDateTime.utc_now()
2575 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
2576 |> NaiveDateTime.to_iso8601()
2578 attrs = %{params: %{}, scheduled_at: today}
2579 {:ok, _} = ScheduledActivity.create(user, attrs)
2580 {:ok, _} = ScheduledActivity.create(user, attrs)
2581 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
2585 |> assign(:user, user)
2586 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
2588 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
2591 test "shows scheduled activities", %{conn: conn} do
2592 user = insert(:user)
2593 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
2594 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
2595 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
2596 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
2600 |> assign(:user, user)
2605 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
2607 result = json_response(conn_res, 200)
2608 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2613 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
2615 result = json_response(conn_res, 200)
2616 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
2621 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
2623 result = json_response(conn_res, 200)
2624 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2627 test "shows a scheduled activity", %{conn: conn} do
2628 user = insert(:user)
2629 scheduled_activity = insert(:scheduled_activity, user: user)
2633 |> assign(:user, user)
2634 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2636 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
2637 assert scheduled_activity_id == scheduled_activity.id |> to_string()
2641 |> assign(:user, user)
2642 |> get("/api/v1/scheduled_statuses/404")
2644 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2647 test "updates a scheduled activity", %{conn: conn} do
2648 user = insert(:user)
2649 scheduled_activity = insert(:scheduled_activity, user: user)
2652 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2656 |> assign(:user, user)
2657 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
2658 scheduled_at: new_scheduled_at
2661 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
2662 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
2666 |> assign(:user, user)
2667 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
2669 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2672 test "deletes a scheduled activity", %{conn: conn} do
2673 user = insert(:user)
2674 scheduled_activity = insert(:scheduled_activity, user: user)
2678 |> assign(:user, user)
2679 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2681 assert %{} = json_response(res_conn, 200)
2682 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
2686 |> assign(:user, user)
2687 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2689 assert %{"error" => "Record not found"} = json_response(res_conn, 404)