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 "doesn't include DMs from blocked users", %{conn: conn} do
304 blocker = insert(:user)
305 blocked = insert(:user)
307 {:ok, blocker} = User.block(blocker, blocked)
309 {:ok, _blocked_direct} =
310 CommonAPI.post(blocked, %{
311 "status" => "Hi @#{blocker.nickname}!",
312 "visibility" => "direct"
316 CommonAPI.post(user, %{
317 "status" => "Hi @#{blocker.nickname}!",
318 "visibility" => "direct"
323 |> assign(:user, user)
324 |> get("api/v1/timelines/direct")
326 [status] = json_response(res_conn, 200)
327 assert status["id"] == direct.id
330 test "replying to a status", %{conn: conn} do
333 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
337 |> assign(:user, user)
338 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
340 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
342 activity = Activity.get_by_id(id)
344 assert activity.data["context"] == replied_to.data["context"]
345 assert activity.data["object"]["inReplyToStatusId"] == replied_to.id
348 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
353 |> assign(:user, user)
354 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
356 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
358 activity = Activity.get_by_id(id)
363 test "verify_credentials", %{conn: conn} do
368 |> assign(:user, user)
369 |> get("/api/v1/accounts/verify_credentials")
371 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
372 assert id == to_string(user.id)
375 test "verify_credentials default scope unlisted", %{conn: conn} do
376 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "unlisted"}})
380 |> assign(:user, user)
381 |> get("/api/v1/accounts/verify_credentials")
383 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
384 assert id == to_string(user.id)
387 test "apps/verify_credentials", %{conn: conn} do
388 token = insert(:oauth_token)
392 |> assign(:user, token.user)
393 |> assign(:token, token)
394 |> get("/api/v1/apps/verify_credentials")
396 app = Repo.preload(token, :app).app
399 "name" => app.client_name,
400 "website" => app.website,
401 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
404 assert expected == json_response(conn, 200)
407 test "creates an oauth app", %{conn: conn} do
409 app_attrs = build(:oauth_app)
413 |> assign(:user, user)
414 |> post("/api/v1/apps", %{
415 client_name: app_attrs.client_name,
416 redirect_uris: app_attrs.redirect_uris
419 [app] = Repo.all(App)
422 "name" => app.client_name,
423 "website" => app.website,
424 "client_id" => app.client_id,
425 "client_secret" => app.client_secret,
426 "id" => app.id |> to_string(),
427 "redirect_uri" => app.redirect_uris,
428 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
431 assert expected == json_response(conn, 200)
434 test "get a status", %{conn: conn} do
435 activity = insert(:note_activity)
439 |> get("/api/v1/statuses/#{activity.id}")
441 assert %{"id" => id} = json_response(conn, 200)
442 assert id == to_string(activity.id)
445 describe "deleting a status" do
446 test "when you created it", %{conn: conn} do
447 activity = insert(:note_activity)
448 author = User.get_by_ap_id(activity.data["actor"])
452 |> assign(:user, author)
453 |> delete("/api/v1/statuses/#{activity.id}")
455 assert %{} = json_response(conn, 200)
457 refute Activity.get_by_id(activity.id)
460 test "when you didn't create it", %{conn: conn} do
461 activity = insert(:note_activity)
466 |> assign(:user, user)
467 |> delete("/api/v1/statuses/#{activity.id}")
469 assert %{"error" => _} = json_response(conn, 403)
471 assert Activity.get_by_id(activity.id) == activity
474 test "when you're an admin or moderator", %{conn: conn} do
475 activity1 = insert(:note_activity)
476 activity2 = insert(:note_activity)
477 admin = insert(:user, info: %{is_admin: true})
478 moderator = insert(:user, info: %{is_moderator: true})
482 |> assign(:user, admin)
483 |> delete("/api/v1/statuses/#{activity1.id}")
485 assert %{} = json_response(res_conn, 200)
489 |> assign(:user, moderator)
490 |> delete("/api/v1/statuses/#{activity2.id}")
492 assert %{} = json_response(res_conn, 200)
494 refute Activity.get_by_id(activity1.id)
495 refute Activity.get_by_id(activity2.id)
499 describe "filters" do
500 test "creating a filter", %{conn: conn} do
503 filter = %Pleroma.Filter{
510 |> assign(:user, user)
511 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
513 assert response = json_response(conn, 200)
514 assert response["phrase"] == filter.phrase
515 assert response["context"] == filter.context
516 assert response["id"] != nil
517 assert response["id"] != ""
520 test "fetching a list of filters", %{conn: conn} do
523 query_one = %Pleroma.Filter{
530 query_two = %Pleroma.Filter{
537 {:ok, filter_one} = Pleroma.Filter.create(query_one)
538 {:ok, filter_two} = Pleroma.Filter.create(query_two)
542 |> assign(:user, user)
543 |> get("/api/v1/filters")
544 |> json_response(200)
550 filters: [filter_two, filter_one]
554 test "get a filter", %{conn: conn} do
557 query = %Pleroma.Filter{
564 {:ok, filter} = Pleroma.Filter.create(query)
568 |> assign(:user, user)
569 |> get("/api/v1/filters/#{filter.filter_id}")
571 assert _response = json_response(conn, 200)
574 test "update a filter", %{conn: conn} do
577 query = %Pleroma.Filter{
584 {:ok, _filter} = Pleroma.Filter.create(query)
586 new = %Pleroma.Filter{
593 |> assign(:user, user)
594 |> put("/api/v1/filters/#{query.filter_id}", %{
599 assert response = json_response(conn, 200)
600 assert response["phrase"] == new.phrase
601 assert response["context"] == new.context
604 test "delete a filter", %{conn: conn} do
607 query = %Pleroma.Filter{
614 {:ok, filter} = Pleroma.Filter.create(query)
618 |> assign(:user, user)
619 |> delete("/api/v1/filters/#{filter.filter_id}")
621 assert response = json_response(conn, 200)
622 assert response == %{}
627 test "creating a list", %{conn: conn} do
632 |> assign(:user, user)
633 |> post("/api/v1/lists", %{"title" => "cuties"})
635 assert %{"title" => title} = json_response(conn, 200)
636 assert title == "cuties"
639 test "adding users to a list", %{conn: conn} do
641 other_user = insert(:user)
642 {:ok, list} = Pleroma.List.create("name", user)
646 |> assign(:user, user)
647 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
649 assert %{} == json_response(conn, 200)
650 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
651 assert following == [other_user.follower_address]
654 test "removing users from a list", %{conn: conn} do
656 other_user = insert(:user)
657 third_user = insert(:user)
658 {:ok, list} = Pleroma.List.create("name", user)
659 {:ok, list} = Pleroma.List.follow(list, other_user)
660 {:ok, list} = Pleroma.List.follow(list, third_user)
664 |> assign(:user, user)
665 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
667 assert %{} == json_response(conn, 200)
668 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
669 assert following == [third_user.follower_address]
672 test "listing users in a list", %{conn: conn} do
674 other_user = insert(:user)
675 {:ok, list} = Pleroma.List.create("name", user)
676 {:ok, list} = Pleroma.List.follow(list, other_user)
680 |> assign(:user, user)
681 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
683 assert [%{"id" => id}] = json_response(conn, 200)
684 assert id == to_string(other_user.id)
687 test "retrieving a list", %{conn: conn} do
689 {:ok, list} = Pleroma.List.create("name", user)
693 |> assign(:user, user)
694 |> get("/api/v1/lists/#{list.id}")
696 assert %{"id" => id} = json_response(conn, 200)
697 assert id == to_string(list.id)
700 test "renaming a list", %{conn: conn} do
702 {:ok, list} = Pleroma.List.create("name", user)
706 |> assign(:user, user)
707 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
709 assert %{"title" => name} = json_response(conn, 200)
710 assert name == "newname"
713 test "deleting a list", %{conn: conn} do
715 {:ok, list} = Pleroma.List.create("name", user)
719 |> assign(:user, user)
720 |> delete("/api/v1/lists/#{list.id}")
722 assert %{} = json_response(conn, 200)
723 assert is_nil(Repo.get(Pleroma.List, list.id))
726 test "list timeline", %{conn: conn} do
728 other_user = insert(:user)
729 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
730 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
731 {:ok, list} = Pleroma.List.create("name", user)
732 {:ok, list} = Pleroma.List.follow(list, other_user)
736 |> assign(:user, user)
737 |> get("/api/v1/timelines/list/#{list.id}")
739 assert [%{"id" => id}] = json_response(conn, 200)
741 assert id == to_string(activity_two.id)
744 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
746 other_user = insert(:user)
747 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
749 {:ok, _activity_two} =
750 TwitterAPI.create_status(other_user, %{
751 "status" => "Marisa is cute.",
752 "visibility" => "private"
755 {:ok, list} = Pleroma.List.create("name", user)
756 {:ok, list} = Pleroma.List.follow(list, other_user)
760 |> assign(:user, user)
761 |> get("/api/v1/timelines/list/#{list.id}")
763 assert [%{"id" => id}] = json_response(conn, 200)
765 assert id == to_string(activity_one.id)
769 describe "notifications" do
770 test "list of notifications", %{conn: conn} do
772 other_user = insert(:user)
775 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
777 {:ok, [_notification]} = Notification.create_notifications(activity)
781 |> assign(:user, user)
782 |> get("/api/v1/notifications")
785 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
787 }\">@<span>#{user.nickname}</span></a></span>"
789 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
790 assert response == expected_response
793 test "getting a single notification", %{conn: conn} do
795 other_user = insert(:user)
798 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
800 {:ok, [notification]} = Notification.create_notifications(activity)
804 |> assign(:user, user)
805 |> get("/api/v1/notifications/#{notification.id}")
808 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
810 }\">@<span>#{user.nickname}</span></a></span>"
812 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
813 assert response == expected_response
816 test "dismissing a single notification", %{conn: conn} do
818 other_user = insert(:user)
821 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
823 {:ok, [notification]} = Notification.create_notifications(activity)
827 |> assign(:user, user)
828 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
830 assert %{} = json_response(conn, 200)
833 test "clearing all notifications", %{conn: conn} do
835 other_user = insert(:user)
838 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
840 {:ok, [_notification]} = Notification.create_notifications(activity)
844 |> assign(:user, user)
845 |> post("/api/v1/notifications/clear")
847 assert %{} = json_response(conn, 200)
851 |> assign(:user, user)
852 |> get("/api/v1/notifications")
854 assert all = json_response(conn, 200)
858 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
860 other_user = insert(:user)
862 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
863 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
864 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
865 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
867 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
868 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
869 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
870 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
874 |> assign(:user, user)
879 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
881 result = json_response(conn_res, 200)
882 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
887 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
889 result = json_response(conn_res, 200)
890 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
895 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
897 result = json_response(conn_res, 200)
898 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
901 test "filters notifications using exclude_types", %{conn: conn} do
903 other_user = insert(:user)
905 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
906 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
907 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
908 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
909 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
911 mention_notification_id =
912 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
914 favorite_notification_id =
915 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
917 reblog_notification_id =
918 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
920 follow_notification_id =
921 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
925 |> assign(:user, user)
928 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
930 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
933 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
935 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
938 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
940 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
943 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
945 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
949 describe "reblogging" do
950 test "reblogs and returns the reblogged status", %{conn: conn} do
951 activity = insert(:note_activity)
956 |> assign(:user, user)
957 |> post("/api/v1/statuses/#{activity.id}/reblog")
959 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
960 json_response(conn, 200)
962 assert to_string(activity.id) == id
966 describe "unreblogging" do
967 test "unreblogs and returns the unreblogged status", %{conn: conn} do
968 activity = insert(:note_activity)
971 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
975 |> assign(:user, user)
976 |> post("/api/v1/statuses/#{activity.id}/unreblog")
978 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
980 assert to_string(activity.id) == id
984 describe "favoriting" do
985 test "favs a status and returns it", %{conn: conn} do
986 activity = insert(:note_activity)
991 |> assign(:user, user)
992 |> post("/api/v1/statuses/#{activity.id}/favourite")
994 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
995 json_response(conn, 200)
997 assert to_string(activity.id) == id
1000 test "returns 500 for a wrong id", %{conn: conn} do
1001 user = insert(:user)
1005 |> assign(:user, user)
1006 |> post("/api/v1/statuses/1/favourite")
1007 |> json_response(500)
1009 assert resp == "Something went wrong"
1013 describe "unfavoriting" do
1014 test "unfavorites a status and returns it", %{conn: conn} do
1015 activity = insert(:note_activity)
1016 user = insert(:user)
1018 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1022 |> assign(:user, user)
1023 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1025 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1026 json_response(conn, 200)
1028 assert to_string(activity.id) == id
1032 describe "user timelines" do
1033 test "gets a users statuses", %{conn: conn} do
1034 user_one = insert(:user)
1035 user_two = insert(:user)
1036 user_three = insert(:user)
1038 {:ok, user_three} = User.follow(user_three, user_one)
1040 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1042 {:ok, direct_activity} =
1043 CommonAPI.post(user_one, %{
1044 "status" => "Hi, @#{user_two.nickname}.",
1045 "visibility" => "direct"
1048 {:ok, private_activity} =
1049 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1053 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1055 assert [%{"id" => id}] = json_response(resp, 200)
1056 assert id == to_string(activity.id)
1060 |> assign(:user, user_two)
1061 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1063 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1064 assert id_one == to_string(direct_activity.id)
1065 assert id_two == to_string(activity.id)
1069 |> assign(:user, user_three)
1070 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1072 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1073 assert id_one == to_string(private_activity.id)
1074 assert id_two == to_string(activity.id)
1077 test "unimplemented pinned statuses feature", %{conn: conn} do
1078 note = insert(:note_activity)
1079 user = User.get_by_ap_id(note.data["actor"])
1083 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1085 assert json_response(conn, 200) == []
1088 test "gets an users media", %{conn: conn} do
1089 note = insert(:note_activity)
1090 user = User.get_by_ap_id(note.data["actor"])
1092 file = %Plug.Upload{
1093 content_type: "image/jpg",
1094 path: Path.absname("test/fixtures/image.jpg"),
1095 filename: "an_image.jpg"
1099 TwitterAPI.upload(file, user, "json")
1103 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1107 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1109 assert [%{"id" => id}] = json_response(conn, 200)
1110 assert id == to_string(image_post.id)
1114 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1116 assert [%{"id" => id}] = json_response(conn, 200)
1117 assert id == to_string(image_post.id)
1120 test "gets a user's statuses without reblogs", %{conn: conn} do
1121 user = insert(:user)
1122 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1123 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1127 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1129 assert [%{"id" => id}] = json_response(conn, 200)
1130 assert id == to_string(post.id)
1134 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1136 assert [%{"id" => id}] = json_response(conn, 200)
1137 assert id == to_string(post.id)
1141 describe "user relationships" do
1142 test "returns the relationships for the current user", %{conn: conn} do
1143 user = insert(:user)
1144 other_user = insert(:user)
1145 {:ok, user} = User.follow(user, other_user)
1149 |> assign(:user, user)
1150 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1152 assert [relationship] = json_response(conn, 200)
1154 assert to_string(other_user.id) == relationship["id"]
1158 describe "locked accounts" do
1159 test "/api/v1/follow_requests works" do
1160 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1161 other_user = insert(:user)
1163 {:ok, _activity} = ActivityPub.follow(other_user, user)
1165 user = User.get_by_id(user.id)
1166 other_user = User.get_by_id(other_user.id)
1168 assert User.following?(other_user, user) == false
1172 |> assign(:user, user)
1173 |> get("/api/v1/follow_requests")
1175 assert [relationship] = json_response(conn, 200)
1176 assert to_string(other_user.id) == relationship["id"]
1179 test "/api/v1/follow_requests/:id/authorize works" do
1180 user = insert(:user, %{info: %User.Info{locked: true}})
1181 other_user = insert(:user)
1183 {:ok, _activity} = ActivityPub.follow(other_user, user)
1185 user = User.get_by_id(user.id)
1186 other_user = User.get_by_id(other_user.id)
1188 assert User.following?(other_user, user) == false
1192 |> assign(:user, user)
1193 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1195 assert relationship = json_response(conn, 200)
1196 assert to_string(other_user.id) == relationship["id"]
1198 user = User.get_by_id(user.id)
1199 other_user = User.get_by_id(other_user.id)
1201 assert User.following?(other_user, user) == true
1204 test "verify_credentials", %{conn: conn} do
1205 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "private"}})
1209 |> assign(:user, user)
1210 |> get("/api/v1/accounts/verify_credentials")
1212 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1213 assert id == to_string(user.id)
1216 test "/api/v1/follow_requests/:id/reject works" do
1217 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1218 other_user = insert(:user)
1220 {:ok, _activity} = ActivityPub.follow(other_user, user)
1222 user = User.get_by_id(user.id)
1226 |> assign(:user, user)
1227 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1229 assert relationship = json_response(conn, 200)
1230 assert to_string(other_user.id) == relationship["id"]
1232 user = User.get_by_id(user.id)
1233 other_user = User.get_by_id(other_user.id)
1235 assert User.following?(other_user, user) == false
1239 test "account fetching", %{conn: conn} do
1240 user = insert(:user)
1244 |> get("/api/v1/accounts/#{user.id}")
1246 assert %{"id" => id} = json_response(conn, 200)
1247 assert id == to_string(user.id)
1251 |> get("/api/v1/accounts/-1")
1253 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1256 test "account fetching also works nickname", %{conn: conn} do
1257 user = insert(:user)
1261 |> get("/api/v1/accounts/#{user.nickname}")
1263 assert %{"id" => id} = json_response(conn, 200)
1264 assert id == user.id
1267 test "media upload", %{conn: conn} do
1268 file = %Plug.Upload{
1269 content_type: "image/jpg",
1270 path: Path.absname("test/fixtures/image.jpg"),
1271 filename: "an_image.jpg"
1274 desc = "Description of the image"
1276 user = insert(:user)
1280 |> assign(:user, user)
1281 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1283 assert media = json_response(conn, 200)
1285 assert media["type"] == "image"
1286 assert media["description"] == desc
1289 object = Repo.get(Object, media["id"])
1290 assert object.data["actor"] == User.ap_id(user)
1293 test "hashtag timeline", %{conn: conn} do
1294 following = insert(:user)
1297 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1299 {:ok, [_activity]} =
1300 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1304 |> get("/api/v1/timelines/tag/2hu")
1306 assert [%{"id" => id}] = json_response(nconn, 200)
1308 assert id == to_string(activity.id)
1310 # works for different capitalization too
1313 |> get("/api/v1/timelines/tag/2HU")
1315 assert [%{"id" => id}] = json_response(nconn, 200)
1317 assert id == to_string(activity.id)
1321 test "multi-hashtag timeline", %{conn: conn} do
1322 user = insert(:user)
1324 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1325 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1326 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1330 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1332 [status_none, status_test1, status_test] = json_response(any_test, 200)
1334 assert to_string(activity_test.id) == status_test["id"]
1335 assert to_string(activity_test1.id) == status_test1["id"]
1336 assert to_string(activity_none.id) == status_none["id"]
1340 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1342 assert [status_test1] == json_response(restricted_test, 200)
1344 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1346 assert [status_none] == json_response(all_test, 200)
1349 test "getting followers", %{conn: conn} do
1350 user = insert(:user)
1351 other_user = insert(:user)
1352 {:ok, user} = User.follow(user, other_user)
1356 |> get("/api/v1/accounts/#{other_user.id}/followers")
1358 assert [%{"id" => id}] = json_response(conn, 200)
1359 assert id == to_string(user.id)
1362 test "getting followers, hide_followers", %{conn: conn} do
1363 user = insert(:user)
1364 other_user = insert(:user, %{info: %{hide_followers: true}})
1365 {:ok, _user} = User.follow(user, other_user)
1369 |> get("/api/v1/accounts/#{other_user.id}/followers")
1371 assert [] == json_response(conn, 200)
1374 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1375 user = insert(:user)
1376 other_user = insert(:user, %{info: %{hide_followers: true}})
1377 {:ok, _user} = User.follow(user, other_user)
1381 |> assign(:user, other_user)
1382 |> get("/api/v1/accounts/#{other_user.id}/followers")
1384 refute [] == json_response(conn, 200)
1387 test "getting followers, pagination", %{conn: conn} do
1388 user = insert(:user)
1389 follower1 = insert(:user)
1390 follower2 = insert(:user)
1391 follower3 = insert(:user)
1392 {:ok, _} = User.follow(follower1, user)
1393 {:ok, _} = User.follow(follower2, user)
1394 {:ok, _} = User.follow(follower3, user)
1398 |> assign(:user, user)
1402 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1404 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1405 assert id3 == follower3.id
1406 assert id2 == follower2.id
1410 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1412 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1413 assert id2 == follower2.id
1414 assert id1 == follower1.id
1418 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1420 assert [%{"id" => id2}] = json_response(res_conn, 200)
1421 assert id2 == follower2.id
1423 assert [link_header] = get_resp_header(res_conn, "link")
1424 assert link_header =~ ~r/since_id=#{follower2.id}/
1425 assert link_header =~ ~r/max_id=#{follower2.id}/
1428 test "getting following", %{conn: conn} do
1429 user = insert(:user)
1430 other_user = insert(:user)
1431 {:ok, user} = User.follow(user, other_user)
1435 |> get("/api/v1/accounts/#{user.id}/following")
1437 assert [%{"id" => id}] = json_response(conn, 200)
1438 assert id == to_string(other_user.id)
1441 test "getting following, hide_follows", %{conn: conn} do
1442 user = insert(:user, %{info: %{hide_follows: true}})
1443 other_user = insert(:user)
1444 {:ok, user} = User.follow(user, other_user)
1448 |> get("/api/v1/accounts/#{user.id}/following")
1450 assert [] == json_response(conn, 200)
1453 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1454 user = insert(:user, %{info: %{hide_follows: true}})
1455 other_user = insert(:user)
1456 {:ok, user} = User.follow(user, other_user)
1460 |> assign(:user, user)
1461 |> get("/api/v1/accounts/#{user.id}/following")
1463 refute [] == json_response(conn, 200)
1466 test "getting following, pagination", %{conn: conn} do
1467 user = insert(:user)
1468 following1 = insert(:user)
1469 following2 = insert(:user)
1470 following3 = insert(:user)
1471 {:ok, _} = User.follow(user, following1)
1472 {:ok, _} = User.follow(user, following2)
1473 {:ok, _} = User.follow(user, following3)
1477 |> assign(:user, user)
1481 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1483 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1484 assert id3 == following3.id
1485 assert id2 == following2.id
1489 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1491 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1492 assert id2 == following2.id
1493 assert id1 == following1.id
1497 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1499 assert [%{"id" => id2}] = json_response(res_conn, 200)
1500 assert id2 == following2.id
1502 assert [link_header] = get_resp_header(res_conn, "link")
1503 assert link_header =~ ~r/since_id=#{following2.id}/
1504 assert link_header =~ ~r/max_id=#{following2.id}/
1507 test "following / unfollowing a user", %{conn: conn} do
1508 user = insert(:user)
1509 other_user = insert(:user)
1513 |> assign(:user, user)
1514 |> post("/api/v1/accounts/#{other_user.id}/follow")
1516 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1518 user = User.get_by_id(user.id)
1522 |> assign(:user, user)
1523 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1525 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1527 user = User.get_by_id(user.id)
1531 |> assign(:user, user)
1532 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1534 assert %{"id" => id} = json_response(conn, 200)
1535 assert id == to_string(other_user.id)
1538 test "muting / unmuting a user", %{conn: conn} do
1539 user = insert(:user)
1540 other_user = insert(:user)
1544 |> assign(:user, user)
1545 |> post("/api/v1/accounts/#{other_user.id}/mute")
1547 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1549 user = User.get_by_id(user.id)
1553 |> assign(:user, user)
1554 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1556 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1559 test "subscribing / unsubscribing to a user", %{conn: conn} do
1560 user = insert(:user)
1561 subscription_target = insert(:user)
1565 |> assign(:user, user)
1566 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1568 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1572 |> assign(:user, user)
1573 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1575 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1578 test "getting a list of mutes", %{conn: conn} do
1579 user = insert(:user)
1580 other_user = insert(:user)
1582 {:ok, user} = User.mute(user, other_user)
1586 |> assign(:user, user)
1587 |> get("/api/v1/mutes")
1589 other_user_id = to_string(other_user.id)
1590 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1593 test "blocking / unblocking 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}/block")
1602 assert %{"id" => _id, "blocking" => 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}/unblock")
1611 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1614 test "getting a list of blocks", %{conn: conn} do
1615 user = insert(:user)
1616 other_user = insert(:user)
1618 {:ok, user} = User.block(user, other_user)
1622 |> assign(:user, user)
1623 |> get("/api/v1/blocks")
1625 other_user_id = to_string(other_user.id)
1626 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1629 test "blocking / unblocking a domain", %{conn: conn} do
1630 user = insert(:user)
1631 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1635 |> assign(:user, user)
1636 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1638 assert %{} = json_response(conn, 200)
1639 user = User.get_cached_by_ap_id(user.ap_id)
1640 assert User.blocks?(user, other_user)
1644 |> assign(:user, user)
1645 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1647 assert %{} = json_response(conn, 200)
1648 user = User.get_cached_by_ap_id(user.ap_id)
1649 refute User.blocks?(user, other_user)
1652 test "getting a list of domain blocks", %{conn: conn} do
1653 user = insert(:user)
1655 {:ok, user} = User.block_domain(user, "bad.site")
1656 {:ok, user} = User.block_domain(user, "even.worse.site")
1660 |> assign(:user, user)
1661 |> get("/api/v1/domain_blocks")
1663 domain_blocks = json_response(conn, 200)
1665 assert "bad.site" in domain_blocks
1666 assert "even.worse.site" in domain_blocks
1669 test "unimplemented follow_requests, blocks, domain blocks" do
1670 user = insert(:user)
1672 ["blocks", "domain_blocks", "follow_requests"]
1673 |> Enum.each(fn endpoint ->
1676 |> assign(:user, user)
1677 |> get("/api/v1/#{endpoint}")
1679 assert [] = json_response(conn, 200)
1683 test "account search", %{conn: conn} do
1684 user = insert(:user)
1685 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1686 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1690 |> assign(:user, user)
1691 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1692 |> json_response(200)
1694 result_ids = for result <- results, do: result["acct"]
1696 assert user_two.nickname in result_ids
1697 assert user_three.nickname in result_ids
1701 |> assign(:user, user)
1702 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
1703 |> json_response(200)
1705 result_ids = for result <- results, do: result["acct"]
1707 assert user_three.nickname in result_ids
1710 test "search", %{conn: conn} do
1711 user = insert(:user)
1712 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1713 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1715 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
1718 CommonAPI.post(user, %{
1719 "status" => "This is about 2hu, but private",
1720 "visibility" => "private"
1723 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
1727 |> get("/api/v1/search", %{"q" => "2hu"})
1729 assert results = json_response(conn, 200)
1731 [account | _] = results["accounts"]
1732 assert account["id"] == to_string(user_three.id)
1734 assert results["hashtags"] == []
1736 [status] = results["statuses"]
1737 assert status["id"] == to_string(activity.id)
1740 test "search fetches remote statuses", %{conn: conn} do
1744 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
1746 assert results = json_response(conn, 200)
1748 [status] = results["statuses"]
1749 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1753 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
1755 CommonAPI.post(insert(:user), %{
1756 "status" => "This is about 2hu, but private",
1757 "visibility" => "private"
1763 |> get("/api/v1/search", %{"q" => activity.data["object"]["id"]})
1765 assert results = json_response(conn, 200)
1767 [] = results["statuses"]
1771 test "search fetches remote accounts", %{conn: conn} do
1774 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
1776 assert results = json_response(conn, 200)
1777 [account] = results["accounts"]
1778 assert account["acct"] == "shp@social.heldscal.la"
1781 test "returns the favorites of a user", %{conn: conn} do
1782 user = insert(:user)
1783 other_user = insert(:user)
1785 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1786 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1788 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1792 |> assign(:user, user)
1793 |> get("/api/v1/favourites")
1795 assert [status] = json_response(first_conn, 200)
1796 assert status["id"] == to_string(activity.id)
1798 assert [{"link", _link_header}] =
1799 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1801 # Honours query params
1802 {:ok, second_activity} =
1803 CommonAPI.post(other_user, %{
1805 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1808 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1810 last_like = status["id"]
1814 |> assign(:user, user)
1815 |> get("/api/v1/favourites?since_id=#{last_like}")
1817 assert [second_status] = json_response(second_conn, 200)
1818 assert second_status["id"] == to_string(second_activity.id)
1822 |> assign(:user, user)
1823 |> get("/api/v1/favourites?limit=0")
1825 assert [] = json_response(third_conn, 200)
1828 describe "updating credentials" do
1829 test "updates the user's bio", %{conn: conn} do
1830 user = insert(:user)
1831 user2 = insert(:user)
1835 |> assign(:user, user)
1836 |> patch("/api/v1/accounts/update_credentials", %{
1837 "note" => "I drink #cofe with @#{user2.nickname}"
1840 assert user = json_response(conn, 200)
1842 assert user["note"] ==
1843 ~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=") <>
1845 ~s(" class="u-url mention" href=") <>
1846 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
1849 test "updates the user's locking status", %{conn: conn} do
1850 user = insert(:user)
1854 |> assign(:user, user)
1855 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
1857 assert user = json_response(conn, 200)
1858 assert user["locked"] == true
1861 test "updates the user's name", %{conn: conn} do
1862 user = insert(:user)
1866 |> assign(:user, user)
1867 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
1869 assert user = json_response(conn, 200)
1870 assert user["display_name"] == "markorepairs"
1873 test "updates the user's avatar", %{conn: conn} do
1874 user = insert(:user)
1876 new_avatar = %Plug.Upload{
1877 content_type: "image/jpg",
1878 path: Path.absname("test/fixtures/image.jpg"),
1879 filename: "an_image.jpg"
1884 |> assign(:user, user)
1885 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
1887 assert user_response = json_response(conn, 200)
1888 assert user_response["avatar"] != User.avatar_url(user)
1891 test "updates the user's banner", %{conn: conn} do
1892 user = insert(:user)
1894 new_header = %Plug.Upload{
1895 content_type: "image/jpg",
1896 path: Path.absname("test/fixtures/image.jpg"),
1897 filename: "an_image.jpg"
1902 |> assign(:user, user)
1903 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
1905 assert user_response = json_response(conn, 200)
1906 assert user_response["header"] != User.banner_url(user)
1909 test "requires 'write' permission", %{conn: conn} do
1910 token1 = insert(:oauth_token, scopes: ["read"])
1911 token2 = insert(:oauth_token, scopes: ["write", "follow"])
1913 for token <- [token1, token2] do
1916 |> put_req_header("authorization", "Bearer #{token.token}")
1917 |> patch("/api/v1/accounts/update_credentials", %{})
1919 if token == token1 do
1920 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
1922 assert json_response(conn, 200)
1928 test "get instance information", %{conn: conn} do
1929 conn = get(conn, "/api/v1/instance")
1930 assert result = json_response(conn, 200)
1932 email = Pleroma.Config.get([:instance, :email])
1933 # Note: not checking for "max_toot_chars" since it's optional
1939 "email" => from_config_email,
1941 "streaming_api" => _
1946 "registrations" => _
1949 assert email == from_config_email
1952 test "get instance stats", %{conn: conn} do
1953 user = insert(:user, %{local: true})
1955 user2 = insert(:user, %{local: true})
1956 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1958 insert(:user, %{local: false, nickname: "u@peer1.com"})
1959 insert(:user, %{local: false, nickname: "u@peer2.com"})
1961 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
1963 # Stats should count users with missing or nil `info.deactivated` value
1964 user = User.get_by_id(user.id)
1965 info_change = Changeset.change(user.info, %{deactivated: nil})
1969 |> Changeset.change()
1970 |> Changeset.put_embed(:info, info_change)
1971 |> User.update_and_set_cache()
1973 Pleroma.Stats.update_stats()
1975 conn = get(conn, "/api/v1/instance")
1977 assert result = json_response(conn, 200)
1979 stats = result["stats"]
1982 assert stats["user_count"] == 1
1983 assert stats["status_count"] == 1
1984 assert stats["domain_count"] == 2
1987 test "get peers", %{conn: conn} do
1988 insert(:user, %{local: false, nickname: "u@peer1.com"})
1989 insert(:user, %{local: false, nickname: "u@peer2.com"})
1991 Pleroma.Stats.update_stats()
1993 conn = get(conn, "/api/v1/instance/peers")
1995 assert result = json_response(conn, 200)
1997 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2000 test "put settings", %{conn: conn} do
2001 user = insert(:user)
2005 |> assign(:user, user)
2006 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2008 assert _result = json_response(conn, 200)
2010 user = User.get_cached_by_ap_id(user.ap_id)
2011 assert user.info.settings == %{"programming" => "socks"}
2014 describe "pinned statuses" do
2016 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2018 user = insert(:user)
2019 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2021 [user: user, activity: activity]
2024 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2025 {:ok, _} = CommonAPI.pin(activity.id, user)
2029 |> assign(:user, user)
2030 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2031 |> json_response(200)
2033 id_str = to_string(activity.id)
2035 assert [%{"id" => ^id_str, "pinned" => true}] = result
2038 test "pin status", %{conn: conn, user: user, activity: activity} do
2039 id_str = to_string(activity.id)
2041 assert %{"id" => ^id_str, "pinned" => true} =
2043 |> assign(:user, user)
2044 |> post("/api/v1/statuses/#{activity.id}/pin")
2045 |> json_response(200)
2047 assert [%{"id" => ^id_str, "pinned" => true}] =
2049 |> assign(:user, user)
2050 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2051 |> json_response(200)
2054 test "unpin status", %{conn: conn, user: user, activity: activity} do
2055 {:ok, _} = CommonAPI.pin(activity.id, user)
2057 id_str = to_string(activity.id)
2058 user = refresh_record(user)
2060 assert %{"id" => ^id_str, "pinned" => false} =
2062 |> assign(:user, user)
2063 |> post("/api/v1/statuses/#{activity.id}/unpin")
2064 |> json_response(200)
2068 |> assign(:user, user)
2069 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2070 |> json_response(200)
2073 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2074 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2076 id_str_one = to_string(activity_one.id)
2078 assert %{"id" => ^id_str_one, "pinned" => true} =
2080 |> assign(:user, user)
2081 |> post("/api/v1/statuses/#{id_str_one}/pin")
2082 |> json_response(200)
2084 user = refresh_record(user)
2086 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2088 |> assign(:user, user)
2089 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2090 |> json_response(400)
2093 test "Status rich-media Card", %{conn: conn, user: user} do
2094 Pleroma.Config.put([:rich_media, :enabled], true)
2095 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2099 |> get("/api/v1/statuses/#{activity.id}/card")
2100 |> json_response(200)
2102 assert response == %{
2103 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2104 "provider_name" => "www.imdb.com",
2105 "provider_url" => "http://www.imdb.com",
2106 "title" => "The Rock",
2108 "url" => "http://www.imdb.com/title/tt0117500/",
2109 "description" => nil,
2112 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2113 "title" => "The Rock",
2114 "type" => "video.movie",
2115 "url" => "http://www.imdb.com/title/tt0117500/"
2120 # works with private posts
2122 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2126 |> assign(:user, user)
2127 |> get("/api/v1/statuses/#{activity.id}/card")
2128 |> json_response(200)
2130 assert response_two == response
2132 Pleroma.Config.put([:rich_media, :enabled], false)
2137 user = insert(:user)
2138 for_user = insert(:user)
2141 CommonAPI.post(user, %{
2142 "status" => "heweoo?"
2146 CommonAPI.post(user, %{
2147 "status" => "heweoo!"
2152 |> assign(:user, for_user)
2153 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2155 assert json_response(response1, 200)["bookmarked"] == true
2159 |> assign(:user, for_user)
2160 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2162 assert json_response(response2, 200)["bookmarked"] == true
2166 |> assign(:user, for_user)
2167 |> get("/api/v1/bookmarks")
2169 assert [json_response(response2, 200), json_response(response1, 200)] ==
2170 json_response(bookmarks, 200)
2174 |> assign(:user, for_user)
2175 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2177 assert json_response(response1, 200)["bookmarked"] == false
2181 |> assign(:user, for_user)
2182 |> get("/api/v1/bookmarks")
2184 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2187 describe "conversation muting" do
2189 user = insert(:user)
2190 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2192 [user: user, activity: activity]
2195 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2196 id_str = to_string(activity.id)
2198 assert %{"id" => ^id_str, "muted" => true} =
2200 |> assign(:user, user)
2201 |> post("/api/v1/statuses/#{activity.id}/mute")
2202 |> json_response(200)
2205 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2206 {:ok, _} = CommonAPI.add_mute(user, activity)
2208 id_str = to_string(activity.id)
2209 user = refresh_record(user)
2211 assert %{"id" => ^id_str, "muted" => false} =
2213 |> assign(:user, user)
2214 |> post("/api/v1/statuses/#{activity.id}/unmute")
2215 |> json_response(200)
2219 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2220 user = insert(:user)
2224 |> assign(:user, user)
2225 |> get("/api/v1/pleroma/flavour")
2227 assert "glitch" == json_response(get_old_flavour, 200)
2231 |> assign(:user, user)
2232 |> post("/api/v1/pleroma/flavour/vanilla")
2234 assert "vanilla" == json_response(set_flavour, 200)
2238 |> assign(:user, user)
2239 |> post("/api/v1/pleroma/flavour/vanilla")
2241 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2244 describe "reports" do
2246 reporter = insert(:user)
2247 target_user = insert(:user)
2249 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2251 [reporter: reporter, target_user: target_user, activity: activity]
2254 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2255 assert %{"action_taken" => false, "id" => _} =
2257 |> assign(:user, reporter)
2258 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2259 |> json_response(200)
2262 test "submit a report with statuses and comment", %{
2265 target_user: target_user,
2268 assert %{"action_taken" => false, "id" => _} =
2270 |> assign(:user, reporter)
2271 |> post("/api/v1/reports", %{
2272 "account_id" => target_user.id,
2273 "status_ids" => [activity.id],
2274 "comment" => "bad status!"
2276 |> json_response(200)
2279 test "account_id is required", %{
2284 assert %{"error" => "Valid `account_id` required"} =
2286 |> assign(:user, reporter)
2287 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2288 |> json_response(400)
2291 test "comment must be up to the size specified in the config", %{
2294 target_user: target_user
2296 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2297 comment = String.pad_trailing("a", max_size + 1, "a")
2299 error = %{"error" => "Comment must be up to #{max_size} characters"}
2303 |> assign(:user, reporter)
2304 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2305 |> json_response(400)
2309 describe "link headers" do
2310 test "preserves parameters in link headers", %{conn: conn} do
2311 user = insert(:user)
2312 other_user = insert(:user)
2315 CommonAPI.post(other_user, %{
2316 "status" => "hi @#{user.nickname}",
2317 "visibility" => "public"
2321 CommonAPI.post(other_user, %{
2322 "status" => "hi @#{user.nickname}",
2323 "visibility" => "public"
2326 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2327 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2331 |> assign(:user, user)
2332 |> get("/api/v1/notifications", %{media_only: true})
2334 assert [link_header] = get_resp_header(conn, "link")
2335 assert link_header =~ ~r/media_only=true/
2336 assert link_header =~ ~r/since_id=#{notification2.id}/
2337 assert link_header =~ ~r/max_id=#{notification1.id}/
2341 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2342 # Need to set an old-style integer ID to reproduce the problem
2343 # (these are no longer assigned to new accounts but were preserved
2344 # for existing accounts during the migration to flakeIDs)
2345 user_one = insert(:user, %{id: 1212})
2346 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2350 |> get("/api/v1/accounts/#{user_one.id}")
2354 |> get("/api/v1/accounts/#{user_two.nickname}")
2358 |> get("/api/v1/accounts/#{user_two.id}")
2360 acc_one = json_response(resp_one, 200)
2361 acc_two = json_response(resp_two, 200)
2362 acc_three = json_response(resp_three, 200)
2363 refute acc_one == acc_two
2364 assert acc_two == acc_three
2367 describe "custom emoji" do
2368 test "with tags", %{conn: conn} do
2371 |> get("/api/v1/custom_emojis")
2372 |> json_response(200)
2374 assert Map.has_key?(emoji, "shortcode")
2375 assert Map.has_key?(emoji, "static_url")
2376 assert Map.has_key?(emoji, "tags")
2377 assert is_list(emoji["tags"])
2378 assert Map.has_key?(emoji, "url")
2379 assert Map.has_key?(emoji, "visible_in_picker")
2383 describe "index/2 redirections" do
2384 setup %{conn: conn} do
2388 signing_salt: "cooldude"
2393 |> Plug.Session.call(Plug.Session.init(session_opts))
2396 test_path = "/web/statuses/test"
2397 %{conn: conn, path: test_path}
2400 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2401 conn = get(conn, path)
2403 assert conn.status == 302
2404 assert redirected_to(conn) == "/web/login"
2407 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2408 token = insert(:oauth_token)
2412 |> assign(:user, token.user)
2413 |> put_session(:oauth_token, token.token)
2416 assert conn.status == 200
2419 test "saves referer path to session", %{conn: conn, path: path} do
2420 conn = get(conn, path)
2421 return_to = Plug.Conn.get_session(conn, :return_to)
2423 assert return_to == path
2426 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2427 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2428 auth = insert(:oauth_authorization, app: app)
2432 |> put_session(:return_to, path)
2433 |> get("/web/login", %{code: auth.token})
2435 assert conn.status == 302
2436 assert redirected_to(conn) == path
2439 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2440 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2441 auth = insert(:oauth_authorization, app: app)
2443 conn = get(conn, "/web/login", %{code: auth.token})
2445 assert conn.status == 302
2446 assert redirected_to(conn) == "/web/getting-started"
2450 describe "scheduled activities" do
2451 test "creates a scheduled activity", %{conn: conn} do
2452 user = insert(:user)
2453 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2457 |> assign(:user, user)
2458 |> post("/api/v1/statuses", %{
2459 "status" => "scheduled",
2460 "scheduled_at" => scheduled_at
2463 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2464 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2465 assert [] == Repo.all(Activity)
2468 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2469 user = insert(:user)
2470 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2472 file = %Plug.Upload{
2473 content_type: "image/jpg",
2474 path: Path.absname("test/fixtures/image.jpg"),
2475 filename: "an_image.jpg"
2478 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
2482 |> assign(:user, user)
2483 |> post("/api/v1/statuses", %{
2484 "media_ids" => [to_string(upload.id)],
2485 "status" => "scheduled",
2486 "scheduled_at" => scheduled_at
2489 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
2490 assert %{"type" => "image"} = media_attachment
2493 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
2495 user = insert(:user)
2498 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
2502 |> assign(:user, user)
2503 |> post("/api/v1/statuses", %{
2504 "status" => "not scheduled",
2505 "scheduled_at" => scheduled_at
2508 assert %{"content" => "not scheduled"} = json_response(conn, 200)
2509 assert [] == Repo.all(ScheduledActivity)
2512 test "returns error when daily user limit is exceeded", %{conn: conn} do
2513 user = insert(:user)
2516 NaiveDateTime.utc_now()
2517 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2518 |> NaiveDateTime.to_iso8601()
2520 attrs = %{params: %{}, scheduled_at: today}
2521 {:ok, _} = ScheduledActivity.create(user, attrs)
2522 {:ok, _} = ScheduledActivity.create(user, attrs)
2526 |> assign(:user, user)
2527 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
2529 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
2532 test "returns error when total user limit is exceeded", %{conn: conn} do
2533 user = insert(:user)
2536 NaiveDateTime.utc_now()
2537 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2538 |> NaiveDateTime.to_iso8601()
2541 NaiveDateTime.utc_now()
2542 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
2543 |> NaiveDateTime.to_iso8601()
2545 attrs = %{params: %{}, scheduled_at: today}
2546 {:ok, _} = ScheduledActivity.create(user, attrs)
2547 {:ok, _} = ScheduledActivity.create(user, attrs)
2548 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
2552 |> assign(:user, user)
2553 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
2555 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
2558 test "shows scheduled activities", %{conn: conn} do
2559 user = insert(:user)
2560 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
2561 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
2562 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
2563 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
2567 |> assign(:user, user)
2572 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
2574 result = json_response(conn_res, 200)
2575 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2580 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
2582 result = json_response(conn_res, 200)
2583 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
2588 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
2590 result = json_response(conn_res, 200)
2591 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2594 test "shows a scheduled activity", %{conn: conn} do
2595 user = insert(:user)
2596 scheduled_activity = insert(:scheduled_activity, user: user)
2600 |> assign(:user, user)
2601 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2603 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
2604 assert scheduled_activity_id == scheduled_activity.id |> to_string()
2608 |> assign(:user, user)
2609 |> get("/api/v1/scheduled_statuses/404")
2611 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2614 test "updates a scheduled activity", %{conn: conn} do
2615 user = insert(:user)
2616 scheduled_activity = insert(:scheduled_activity, user: user)
2619 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2623 |> assign(:user, user)
2624 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
2625 scheduled_at: new_scheduled_at
2628 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
2629 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
2633 |> assign(:user, user)
2634 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
2636 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2639 test "deletes a scheduled activity", %{conn: conn} do
2640 user = insert(:user)
2641 scheduled_activity = insert(:scheduled_activity, user: user)
2645 |> assign(:user, user)
2646 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2648 assert %{} = json_response(res_conn, 200)
2649 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
2653 |> assign(:user, user)
2654 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2656 assert %{"error" => "Record not found"} = json_response(res_conn, 404)