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 is_binary(res_id)
338 assert unread == true
339 assert res_last_status["id"] == direct.id
341 # Apparently undocumented API endpoint
344 |> assign(:user, user_one)
345 |> post("/api/v1/conversations/#{res_id}/read")
347 assert response = json_response(res_conn, 200)
348 assert response["unread"] == false
350 # (vanilla) Mastodon frontend behaviour
353 |> assign(:user, user_one)
354 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
356 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
359 test "doesn't include DMs from blocked users", %{conn: conn} do
360 blocker = insert(:user)
361 blocked = insert(:user)
363 {:ok, blocker} = User.block(blocker, blocked)
365 {:ok, _blocked_direct} =
366 CommonAPI.post(blocked, %{
367 "status" => "Hi @#{blocker.nickname}!",
368 "visibility" => "direct"
372 CommonAPI.post(user, %{
373 "status" => "Hi @#{blocker.nickname}!",
374 "visibility" => "direct"
379 |> assign(:user, user)
380 |> get("api/v1/timelines/direct")
382 [status] = json_response(res_conn, 200)
383 assert status["id"] == direct.id
386 test "replying to a status", %{conn: conn} do
389 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
393 |> assign(:user, user)
394 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
396 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
398 activity = Activity.get_by_id(id)
400 assert activity.data["context"] == replied_to.data["context"]
401 assert activity.data["object"]["inReplyToStatusId"] == replied_to.id
404 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
409 |> assign(:user, user)
410 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
412 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
414 activity = Activity.get_by_id(id)
419 test "verify_credentials", %{conn: conn} do
424 |> assign(:user, user)
425 |> get("/api/v1/accounts/verify_credentials")
427 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
428 assert id == to_string(user.id)
431 test "verify_credentials default scope unlisted", %{conn: conn} do
432 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "unlisted"}})
436 |> assign(:user, user)
437 |> get("/api/v1/accounts/verify_credentials")
439 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
440 assert id == to_string(user.id)
443 test "apps/verify_credentials", %{conn: conn} do
444 token = insert(:oauth_token)
448 |> assign(:user, token.user)
449 |> assign(:token, token)
450 |> get("/api/v1/apps/verify_credentials")
452 app = Repo.preload(token, :app).app
455 "name" => app.client_name,
456 "website" => app.website,
457 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
460 assert expected == json_response(conn, 200)
463 test "creates an oauth app", %{conn: conn} do
465 app_attrs = build(:oauth_app)
469 |> assign(:user, user)
470 |> post("/api/v1/apps", %{
471 client_name: app_attrs.client_name,
472 redirect_uris: app_attrs.redirect_uris
475 [app] = Repo.all(App)
478 "name" => app.client_name,
479 "website" => app.website,
480 "client_id" => app.client_id,
481 "client_secret" => app.client_secret,
482 "id" => app.id |> to_string(),
483 "redirect_uri" => app.redirect_uris,
484 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
487 assert expected == json_response(conn, 200)
490 test "get a status", %{conn: conn} do
491 activity = insert(:note_activity)
495 |> get("/api/v1/statuses/#{activity.id}")
497 assert %{"id" => id} = json_response(conn, 200)
498 assert id == to_string(activity.id)
501 describe "deleting a status" do
502 test "when you created it", %{conn: conn} do
503 activity = insert(:note_activity)
504 author = User.get_by_ap_id(activity.data["actor"])
508 |> assign(:user, author)
509 |> delete("/api/v1/statuses/#{activity.id}")
511 assert %{} = json_response(conn, 200)
513 refute Activity.get_by_id(activity.id)
516 test "when you didn't create it", %{conn: conn} do
517 activity = insert(:note_activity)
522 |> assign(:user, user)
523 |> delete("/api/v1/statuses/#{activity.id}")
525 assert %{"error" => _} = json_response(conn, 403)
527 assert Activity.get_by_id(activity.id) == activity
530 test "when you're an admin or moderator", %{conn: conn} do
531 activity1 = insert(:note_activity)
532 activity2 = insert(:note_activity)
533 admin = insert(:user, info: %{is_admin: true})
534 moderator = insert(:user, info: %{is_moderator: true})
538 |> assign(:user, admin)
539 |> delete("/api/v1/statuses/#{activity1.id}")
541 assert %{} = json_response(res_conn, 200)
545 |> assign(:user, moderator)
546 |> delete("/api/v1/statuses/#{activity2.id}")
548 assert %{} = json_response(res_conn, 200)
550 refute Activity.get_by_id(activity1.id)
551 refute Activity.get_by_id(activity2.id)
555 describe "filters" do
556 test "creating a filter", %{conn: conn} do
559 filter = %Pleroma.Filter{
566 |> assign(:user, user)
567 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
569 assert response = json_response(conn, 200)
570 assert response["phrase"] == filter.phrase
571 assert response["context"] == filter.context
572 assert response["id"] != nil
573 assert response["id"] != ""
576 test "fetching a list of filters", %{conn: conn} do
579 query_one = %Pleroma.Filter{
586 query_two = %Pleroma.Filter{
593 {:ok, filter_one} = Pleroma.Filter.create(query_one)
594 {:ok, filter_two} = Pleroma.Filter.create(query_two)
598 |> assign(:user, user)
599 |> get("/api/v1/filters")
600 |> json_response(200)
606 filters: [filter_two, filter_one]
610 test "get a filter", %{conn: conn} do
613 query = %Pleroma.Filter{
620 {:ok, filter} = Pleroma.Filter.create(query)
624 |> assign(:user, user)
625 |> get("/api/v1/filters/#{filter.filter_id}")
627 assert _response = json_response(conn, 200)
630 test "update a filter", %{conn: conn} do
633 query = %Pleroma.Filter{
640 {:ok, _filter} = Pleroma.Filter.create(query)
642 new = %Pleroma.Filter{
649 |> assign(:user, user)
650 |> put("/api/v1/filters/#{query.filter_id}", %{
655 assert response = json_response(conn, 200)
656 assert response["phrase"] == new.phrase
657 assert response["context"] == new.context
660 test "delete a filter", %{conn: conn} do
663 query = %Pleroma.Filter{
670 {:ok, filter} = Pleroma.Filter.create(query)
674 |> assign(:user, user)
675 |> delete("/api/v1/filters/#{filter.filter_id}")
677 assert response = json_response(conn, 200)
678 assert response == %{}
683 test "creating a list", %{conn: conn} do
688 |> assign(:user, user)
689 |> post("/api/v1/lists", %{"title" => "cuties"})
691 assert %{"title" => title} = json_response(conn, 200)
692 assert title == "cuties"
695 test "adding users to a list", %{conn: conn} do
697 other_user = insert(:user)
698 {:ok, list} = Pleroma.List.create("name", user)
702 |> assign(:user, user)
703 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
705 assert %{} == json_response(conn, 200)
706 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
707 assert following == [other_user.follower_address]
710 test "removing users from a list", %{conn: conn} do
712 other_user = insert(:user)
713 third_user = insert(:user)
714 {:ok, list} = Pleroma.List.create("name", user)
715 {:ok, list} = Pleroma.List.follow(list, other_user)
716 {:ok, list} = Pleroma.List.follow(list, third_user)
720 |> assign(:user, user)
721 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
723 assert %{} == json_response(conn, 200)
724 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
725 assert following == [third_user.follower_address]
728 test "listing users in a list", %{conn: conn} do
730 other_user = insert(:user)
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/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
739 assert [%{"id" => id}] = json_response(conn, 200)
740 assert id == to_string(other_user.id)
743 test "retrieving a list", %{conn: conn} do
745 {:ok, list} = Pleroma.List.create("name", user)
749 |> assign(:user, user)
750 |> get("/api/v1/lists/#{list.id}")
752 assert %{"id" => id} = json_response(conn, 200)
753 assert id == to_string(list.id)
756 test "renaming a list", %{conn: conn} do
758 {:ok, list} = Pleroma.List.create("name", user)
762 |> assign(:user, user)
763 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
765 assert %{"title" => name} = json_response(conn, 200)
766 assert name == "newname"
769 test "deleting a list", %{conn: conn} do
771 {:ok, list} = Pleroma.List.create("name", user)
775 |> assign(:user, user)
776 |> delete("/api/v1/lists/#{list.id}")
778 assert %{} = json_response(conn, 200)
779 assert is_nil(Repo.get(Pleroma.List, list.id))
782 test "list timeline", %{conn: conn} do
784 other_user = insert(:user)
785 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
786 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
787 {:ok, list} = Pleroma.List.create("name", user)
788 {:ok, list} = Pleroma.List.follow(list, other_user)
792 |> assign(:user, user)
793 |> get("/api/v1/timelines/list/#{list.id}")
795 assert [%{"id" => id}] = json_response(conn, 200)
797 assert id == to_string(activity_two.id)
800 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
802 other_user = insert(:user)
803 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
805 {:ok, _activity_two} =
806 TwitterAPI.create_status(other_user, %{
807 "status" => "Marisa is cute.",
808 "visibility" => "private"
811 {:ok, list} = Pleroma.List.create("name", user)
812 {:ok, list} = Pleroma.List.follow(list, other_user)
816 |> assign(:user, user)
817 |> get("/api/v1/timelines/list/#{list.id}")
819 assert [%{"id" => id}] = json_response(conn, 200)
821 assert id == to_string(activity_one.id)
825 describe "notifications" do
826 test "list of notifications", %{conn: conn} do
828 other_user = insert(:user)
831 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
833 {:ok, [_notification]} = Notification.create_notifications(activity)
837 |> assign(:user, user)
838 |> get("/api/v1/notifications")
841 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
843 }\">@<span>#{user.nickname}</span></a></span>"
845 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
846 assert response == expected_response
849 test "getting a single notification", %{conn: conn} do
851 other_user = insert(:user)
854 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
856 {:ok, [notification]} = Notification.create_notifications(activity)
860 |> assign(:user, user)
861 |> get("/api/v1/notifications/#{notification.id}")
864 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
866 }\">@<span>#{user.nickname}</span></a></span>"
868 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
869 assert response == expected_response
872 test "dismissing a single notification", %{conn: conn} do
874 other_user = insert(:user)
877 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
879 {:ok, [notification]} = Notification.create_notifications(activity)
883 |> assign(:user, user)
884 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
886 assert %{} = json_response(conn, 200)
889 test "clearing all notifications", %{conn: conn} do
891 other_user = insert(:user)
894 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
896 {:ok, [_notification]} = Notification.create_notifications(activity)
900 |> assign(:user, user)
901 |> post("/api/v1/notifications/clear")
903 assert %{} = json_response(conn, 200)
907 |> assign(:user, user)
908 |> get("/api/v1/notifications")
910 assert all = json_response(conn, 200)
914 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
916 other_user = insert(:user)
918 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
919 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
920 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
921 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
923 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
924 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
925 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
926 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
930 |> assign(:user, user)
935 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
937 result = json_response(conn_res, 200)
938 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
943 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
945 result = json_response(conn_res, 200)
946 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
951 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
953 result = json_response(conn_res, 200)
954 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
957 test "filters notifications using exclude_types", %{conn: conn} do
959 other_user = insert(:user)
961 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
962 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
963 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
964 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
965 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
967 mention_notification_id =
968 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
970 favorite_notification_id =
971 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
973 reblog_notification_id =
974 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
976 follow_notification_id =
977 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
981 |> assign(:user, user)
984 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
986 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
989 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
991 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
994 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
996 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
999 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1001 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1005 describe "reblogging" do
1006 test "reblogs and returns the reblogged status", %{conn: conn} do
1007 activity = insert(:note_activity)
1008 user = insert(:user)
1012 |> assign(:user, user)
1013 |> post("/api/v1/statuses/#{activity.id}/reblog")
1015 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
1016 json_response(conn, 200)
1018 assert to_string(activity.id) == id
1022 describe "unreblogging" do
1023 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1024 activity = insert(:note_activity)
1025 user = insert(:user)
1027 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1031 |> assign(:user, user)
1032 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1034 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1036 assert to_string(activity.id) == id
1040 describe "favoriting" do
1041 test "favs a status and returns it", %{conn: conn} do
1042 activity = insert(:note_activity)
1043 user = insert(:user)
1047 |> assign(:user, user)
1048 |> post("/api/v1/statuses/#{activity.id}/favourite")
1050 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1051 json_response(conn, 200)
1053 assert to_string(activity.id) == id
1056 test "returns 500 for a wrong id", %{conn: conn} do
1057 user = insert(:user)
1061 |> assign(:user, user)
1062 |> post("/api/v1/statuses/1/favourite")
1063 |> json_response(500)
1065 assert resp == "Something went wrong"
1069 describe "unfavoriting" do
1070 test "unfavorites a status and returns it", %{conn: conn} do
1071 activity = insert(:note_activity)
1072 user = insert(:user)
1074 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1078 |> assign(:user, user)
1079 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1081 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1082 json_response(conn, 200)
1084 assert to_string(activity.id) == id
1088 describe "user timelines" do
1089 test "gets a users statuses", %{conn: conn} do
1090 user_one = insert(:user)
1091 user_two = insert(:user)
1092 user_three = insert(:user)
1094 {:ok, user_three} = User.follow(user_three, user_one)
1096 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1098 {:ok, direct_activity} =
1099 CommonAPI.post(user_one, %{
1100 "status" => "Hi, @#{user_two.nickname}.",
1101 "visibility" => "direct"
1104 {:ok, private_activity} =
1105 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1109 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1111 assert [%{"id" => id}] = json_response(resp, 200)
1112 assert id == to_string(activity.id)
1116 |> assign(:user, user_two)
1117 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1119 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1120 assert id_one == to_string(direct_activity.id)
1121 assert id_two == to_string(activity.id)
1125 |> assign(:user, user_three)
1126 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1128 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1129 assert id_one == to_string(private_activity.id)
1130 assert id_two == to_string(activity.id)
1133 test "unimplemented pinned statuses feature", %{conn: conn} do
1134 note = insert(:note_activity)
1135 user = User.get_by_ap_id(note.data["actor"])
1139 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1141 assert json_response(conn, 200) == []
1144 test "gets an users media", %{conn: conn} do
1145 note = insert(:note_activity)
1146 user = User.get_by_ap_id(note.data["actor"])
1148 file = %Plug.Upload{
1149 content_type: "image/jpg",
1150 path: Path.absname("test/fixtures/image.jpg"),
1151 filename: "an_image.jpg"
1155 TwitterAPI.upload(file, user, "json")
1159 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1163 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1165 assert [%{"id" => id}] = json_response(conn, 200)
1166 assert id == to_string(image_post.id)
1170 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1172 assert [%{"id" => id}] = json_response(conn, 200)
1173 assert id == to_string(image_post.id)
1176 test "gets a user's statuses without reblogs", %{conn: conn} do
1177 user = insert(:user)
1178 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1179 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1183 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1185 assert [%{"id" => id}] = json_response(conn, 200)
1186 assert id == to_string(post.id)
1190 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1192 assert [%{"id" => id}] = json_response(conn, 200)
1193 assert id == to_string(post.id)
1197 describe "user relationships" do
1198 test "returns the relationships for the current user", %{conn: conn} do
1199 user = insert(:user)
1200 other_user = insert(:user)
1201 {:ok, user} = User.follow(user, other_user)
1205 |> assign(:user, user)
1206 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1208 assert [relationship] = json_response(conn, 200)
1210 assert to_string(other_user.id) == relationship["id"]
1214 describe "locked accounts" do
1215 test "/api/v1/follow_requests works" do
1216 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1217 other_user = insert(:user)
1219 {:ok, _activity} = ActivityPub.follow(other_user, user)
1221 user = User.get_by_id(user.id)
1222 other_user = User.get_by_id(other_user.id)
1224 assert User.following?(other_user, user) == false
1228 |> assign(:user, user)
1229 |> get("/api/v1/follow_requests")
1231 assert [relationship] = json_response(conn, 200)
1232 assert to_string(other_user.id) == relationship["id"]
1235 test "/api/v1/follow_requests/:id/authorize works" do
1236 user = insert(:user, %{info: %User.Info{locked: true}})
1237 other_user = insert(:user)
1239 {:ok, _activity} = ActivityPub.follow(other_user, user)
1241 user = User.get_by_id(user.id)
1242 other_user = User.get_by_id(other_user.id)
1244 assert User.following?(other_user, user) == false
1248 |> assign(:user, user)
1249 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1251 assert relationship = json_response(conn, 200)
1252 assert to_string(other_user.id) == relationship["id"]
1254 user = User.get_by_id(user.id)
1255 other_user = User.get_by_id(other_user.id)
1257 assert User.following?(other_user, user) == true
1260 test "verify_credentials", %{conn: conn} do
1261 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "private"}})
1265 |> assign(:user, user)
1266 |> get("/api/v1/accounts/verify_credentials")
1268 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1269 assert id == to_string(user.id)
1272 test "/api/v1/follow_requests/:id/reject works" do
1273 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1274 other_user = insert(:user)
1276 {:ok, _activity} = ActivityPub.follow(other_user, user)
1278 user = User.get_by_id(user.id)
1282 |> assign(:user, user)
1283 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1285 assert relationship = json_response(conn, 200)
1286 assert to_string(other_user.id) == relationship["id"]
1288 user = User.get_by_id(user.id)
1289 other_user = User.get_by_id(other_user.id)
1291 assert User.following?(other_user, user) == false
1295 test "account fetching", %{conn: conn} do
1296 user = insert(:user)
1300 |> get("/api/v1/accounts/#{user.id}")
1302 assert %{"id" => id} = json_response(conn, 200)
1303 assert id == to_string(user.id)
1307 |> get("/api/v1/accounts/-1")
1309 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1312 test "account fetching also works nickname", %{conn: conn} do
1313 user = insert(:user)
1317 |> get("/api/v1/accounts/#{user.nickname}")
1319 assert %{"id" => id} = json_response(conn, 200)
1320 assert id == user.id
1323 test "media upload", %{conn: conn} do
1324 file = %Plug.Upload{
1325 content_type: "image/jpg",
1326 path: Path.absname("test/fixtures/image.jpg"),
1327 filename: "an_image.jpg"
1330 desc = "Description of the image"
1332 user = insert(:user)
1336 |> assign(:user, user)
1337 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1339 assert media = json_response(conn, 200)
1341 assert media["type"] == "image"
1342 assert media["description"] == desc
1345 object = Repo.get(Object, media["id"])
1346 assert object.data["actor"] == User.ap_id(user)
1349 test "hashtag timeline", %{conn: conn} do
1350 following = insert(:user)
1353 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1355 {:ok, [_activity]} =
1356 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1360 |> get("/api/v1/timelines/tag/2hu")
1362 assert [%{"id" => id}] = json_response(nconn, 200)
1364 assert id == to_string(activity.id)
1366 # works for different capitalization too
1369 |> get("/api/v1/timelines/tag/2HU")
1371 assert [%{"id" => id}] = json_response(nconn, 200)
1373 assert id == to_string(activity.id)
1377 test "multi-hashtag timeline", %{conn: conn} do
1378 user = insert(:user)
1380 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1381 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1382 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1386 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1388 [status_none, status_test1, status_test] = json_response(any_test, 200)
1390 assert to_string(activity_test.id) == status_test["id"]
1391 assert to_string(activity_test1.id) == status_test1["id"]
1392 assert to_string(activity_none.id) == status_none["id"]
1396 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1398 assert [status_test1] == json_response(restricted_test, 200)
1400 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1402 assert [status_none] == json_response(all_test, 200)
1405 test "getting followers", %{conn: conn} do
1406 user = insert(:user)
1407 other_user = insert(:user)
1408 {:ok, user} = User.follow(user, other_user)
1412 |> get("/api/v1/accounts/#{other_user.id}/followers")
1414 assert [%{"id" => id}] = json_response(conn, 200)
1415 assert id == to_string(user.id)
1418 test "getting followers, hide_followers", %{conn: conn} do
1419 user = insert(:user)
1420 other_user = insert(:user, %{info: %{hide_followers: true}})
1421 {:ok, _user} = User.follow(user, other_user)
1425 |> get("/api/v1/accounts/#{other_user.id}/followers")
1427 assert [] == json_response(conn, 200)
1430 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1431 user = insert(:user)
1432 other_user = insert(:user, %{info: %{hide_followers: true}})
1433 {:ok, _user} = User.follow(user, other_user)
1437 |> assign(:user, other_user)
1438 |> get("/api/v1/accounts/#{other_user.id}/followers")
1440 refute [] == json_response(conn, 200)
1443 test "getting followers, pagination", %{conn: conn} do
1444 user = insert(:user)
1445 follower1 = insert(:user)
1446 follower2 = insert(:user)
1447 follower3 = insert(:user)
1448 {:ok, _} = User.follow(follower1, user)
1449 {:ok, _} = User.follow(follower2, user)
1450 {:ok, _} = User.follow(follower3, user)
1454 |> assign(:user, user)
1458 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1460 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1461 assert id3 == follower3.id
1462 assert id2 == follower2.id
1466 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1468 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1469 assert id2 == follower2.id
1470 assert id1 == follower1.id
1474 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1476 assert [%{"id" => id2}] = json_response(res_conn, 200)
1477 assert id2 == follower2.id
1479 assert [link_header] = get_resp_header(res_conn, "link")
1480 assert link_header =~ ~r/since_id=#{follower2.id}/
1481 assert link_header =~ ~r/max_id=#{follower2.id}/
1484 test "getting following", %{conn: conn} do
1485 user = insert(:user)
1486 other_user = insert(:user)
1487 {:ok, user} = User.follow(user, other_user)
1491 |> get("/api/v1/accounts/#{user.id}/following")
1493 assert [%{"id" => id}] = json_response(conn, 200)
1494 assert id == to_string(other_user.id)
1497 test "getting following, hide_follows", %{conn: conn} do
1498 user = insert(:user, %{info: %{hide_follows: true}})
1499 other_user = insert(:user)
1500 {:ok, user} = User.follow(user, other_user)
1504 |> get("/api/v1/accounts/#{user.id}/following")
1506 assert [] == json_response(conn, 200)
1509 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1510 user = insert(:user, %{info: %{hide_follows: true}})
1511 other_user = insert(:user)
1512 {:ok, user} = User.follow(user, other_user)
1516 |> assign(:user, user)
1517 |> get("/api/v1/accounts/#{user.id}/following")
1519 refute [] == json_response(conn, 200)
1522 test "getting following, pagination", %{conn: conn} do
1523 user = insert(:user)
1524 following1 = insert(:user)
1525 following2 = insert(:user)
1526 following3 = insert(:user)
1527 {:ok, _} = User.follow(user, following1)
1528 {:ok, _} = User.follow(user, following2)
1529 {:ok, _} = User.follow(user, following3)
1533 |> assign(:user, user)
1537 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1539 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1540 assert id3 == following3.id
1541 assert id2 == following2.id
1545 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1547 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1548 assert id2 == following2.id
1549 assert id1 == following1.id
1553 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1555 assert [%{"id" => id2}] = json_response(res_conn, 200)
1556 assert id2 == following2.id
1558 assert [link_header] = get_resp_header(res_conn, "link")
1559 assert link_header =~ ~r/since_id=#{following2.id}/
1560 assert link_header =~ ~r/max_id=#{following2.id}/
1563 test "following / unfollowing a user", %{conn: conn} do
1564 user = insert(:user)
1565 other_user = insert(:user)
1569 |> assign(:user, user)
1570 |> post("/api/v1/accounts/#{other_user.id}/follow")
1572 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1574 user = User.get_by_id(user.id)
1578 |> assign(:user, user)
1579 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1581 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1583 user = User.get_by_id(user.id)
1587 |> assign(:user, user)
1588 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1590 assert %{"id" => id} = json_response(conn, 200)
1591 assert id == to_string(other_user.id)
1594 test "muting / unmuting a user", %{conn: conn} do
1595 user = insert(:user)
1596 other_user = insert(:user)
1600 |> assign(:user, user)
1601 |> post("/api/v1/accounts/#{other_user.id}/mute")
1603 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1605 user = User.get_by_id(user.id)
1609 |> assign(:user, user)
1610 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1612 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1615 test "getting a list of mutes", %{conn: conn} do
1616 user = insert(:user)
1617 other_user = insert(:user)
1619 {:ok, user} = User.mute(user, other_user)
1623 |> assign(:user, user)
1624 |> get("/api/v1/mutes")
1626 other_user_id = to_string(other_user.id)
1627 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1630 test "blocking / unblocking a user", %{conn: conn} do
1631 user = insert(:user)
1632 other_user = insert(:user)
1636 |> assign(:user, user)
1637 |> post("/api/v1/accounts/#{other_user.id}/block")
1639 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1641 user = User.get_by_id(user.id)
1645 |> assign(:user, user)
1646 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1648 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1651 test "getting a list of blocks", %{conn: conn} do
1652 user = insert(:user)
1653 other_user = insert(:user)
1655 {:ok, user} = User.block(user, other_user)
1659 |> assign(:user, user)
1660 |> get("/api/v1/blocks")
1662 other_user_id = to_string(other_user.id)
1663 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1666 test "blocking / unblocking a domain", %{conn: conn} do
1667 user = insert(:user)
1668 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1672 |> assign(:user, user)
1673 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1675 assert %{} = json_response(conn, 200)
1676 user = User.get_cached_by_ap_id(user.ap_id)
1677 assert User.blocks?(user, other_user)
1681 |> assign(:user, user)
1682 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1684 assert %{} = json_response(conn, 200)
1685 user = User.get_cached_by_ap_id(user.ap_id)
1686 refute User.blocks?(user, other_user)
1689 test "getting a list of domain blocks", %{conn: conn} do
1690 user = insert(:user)
1692 {:ok, user} = User.block_domain(user, "bad.site")
1693 {:ok, user} = User.block_domain(user, "even.worse.site")
1697 |> assign(:user, user)
1698 |> get("/api/v1/domain_blocks")
1700 domain_blocks = json_response(conn, 200)
1702 assert "bad.site" in domain_blocks
1703 assert "even.worse.site" in domain_blocks
1706 test "unimplemented follow_requests, blocks, domain blocks" do
1707 user = insert(:user)
1709 ["blocks", "domain_blocks", "follow_requests"]
1710 |> Enum.each(fn endpoint ->
1713 |> assign(:user, user)
1714 |> get("/api/v1/#{endpoint}")
1716 assert [] = json_response(conn, 200)
1720 test "account search", %{conn: conn} do
1721 user = insert(:user)
1722 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1723 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1727 |> assign(:user, user)
1728 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1729 |> json_response(200)
1731 result_ids = for result <- results, do: result["acct"]
1733 assert user_two.nickname in result_ids
1734 assert user_three.nickname in result_ids
1738 |> assign(:user, user)
1739 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
1740 |> json_response(200)
1742 result_ids = for result <- results, do: result["acct"]
1744 assert user_three.nickname in result_ids
1747 test "search", %{conn: conn} do
1748 user = insert(:user)
1749 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1750 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1752 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
1755 CommonAPI.post(user, %{
1756 "status" => "This is about 2hu, but private",
1757 "visibility" => "private"
1760 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
1764 |> get("/api/v1/search", %{"q" => "2hu"})
1766 assert results = json_response(conn, 200)
1768 [account | _] = results["accounts"]
1769 assert account["id"] == to_string(user_three.id)
1771 assert results["hashtags"] == []
1773 [status] = results["statuses"]
1774 assert status["id"] == to_string(activity.id)
1777 test "search fetches remote statuses", %{conn: conn} do
1781 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
1783 assert results = json_response(conn, 200)
1785 [status] = results["statuses"]
1786 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1790 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
1792 CommonAPI.post(insert(:user), %{
1793 "status" => "This is about 2hu, but private",
1794 "visibility" => "private"
1800 |> get("/api/v1/search", %{"q" => activity.data["object"]["id"]})
1802 assert results = json_response(conn, 200)
1804 [] = results["statuses"]
1808 test "search fetches remote accounts", %{conn: conn} do
1811 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
1813 assert results = json_response(conn, 200)
1814 [account] = results["accounts"]
1815 assert account["acct"] == "shp@social.heldscal.la"
1818 test "returns the favorites of a user", %{conn: conn} do
1819 user = insert(:user)
1820 other_user = insert(:user)
1822 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1823 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1825 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1829 |> assign(:user, user)
1830 |> get("/api/v1/favourites")
1832 assert [status] = json_response(first_conn, 200)
1833 assert status["id"] == to_string(activity.id)
1835 assert [{"link", _link_header}] =
1836 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1838 # Honours query params
1839 {:ok, second_activity} =
1840 CommonAPI.post(other_user, %{
1842 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1845 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1847 last_like = status["id"]
1851 |> assign(:user, user)
1852 |> get("/api/v1/favourites?since_id=#{last_like}")
1854 assert [second_status] = json_response(second_conn, 200)
1855 assert second_status["id"] == to_string(second_activity.id)
1859 |> assign(:user, user)
1860 |> get("/api/v1/favourites?limit=0")
1862 assert [] = json_response(third_conn, 200)
1865 describe "updating credentials" do
1866 test "updates the user's bio", %{conn: conn} do
1867 user = insert(:user)
1868 user2 = insert(:user)
1872 |> assign(:user, user)
1873 |> patch("/api/v1/accounts/update_credentials", %{
1874 "note" => "I drink #cofe with @#{user2.nickname}"
1877 assert user = json_response(conn, 200)
1879 assert user["note"] ==
1880 ~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=") <>
1882 ~s(" class="u-url mention" href=") <>
1883 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
1886 test "updates the user's locking status", %{conn: conn} do
1887 user = insert(:user)
1891 |> assign(:user, user)
1892 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
1894 assert user = json_response(conn, 200)
1895 assert user["locked"] == true
1898 test "updates the user's name", %{conn: conn} do
1899 user = insert(:user)
1903 |> assign(:user, user)
1904 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
1906 assert user = json_response(conn, 200)
1907 assert user["display_name"] == "markorepairs"
1910 test "updates the user's avatar", %{conn: conn} do
1911 user = insert(:user)
1913 new_avatar = %Plug.Upload{
1914 content_type: "image/jpg",
1915 path: Path.absname("test/fixtures/image.jpg"),
1916 filename: "an_image.jpg"
1921 |> assign(:user, user)
1922 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
1924 assert user_response = json_response(conn, 200)
1925 assert user_response["avatar"] != User.avatar_url(user)
1928 test "updates the user's banner", %{conn: conn} do
1929 user = insert(:user)
1931 new_header = %Plug.Upload{
1932 content_type: "image/jpg",
1933 path: Path.absname("test/fixtures/image.jpg"),
1934 filename: "an_image.jpg"
1939 |> assign(:user, user)
1940 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
1942 assert user_response = json_response(conn, 200)
1943 assert user_response["header"] != User.banner_url(user)
1946 test "requires 'write' permission", %{conn: conn} do
1947 token1 = insert(:oauth_token, scopes: ["read"])
1948 token2 = insert(:oauth_token, scopes: ["write", "follow"])
1950 for token <- [token1, token2] do
1953 |> put_req_header("authorization", "Bearer #{token.token}")
1954 |> patch("/api/v1/accounts/update_credentials", %{})
1956 if token == token1 do
1957 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
1959 assert json_response(conn, 200)
1965 test "get instance information", %{conn: conn} do
1966 conn = get(conn, "/api/v1/instance")
1967 assert result = json_response(conn, 200)
1969 # Note: not checking for "max_toot_chars" since it's optional
1977 "streaming_api" => _
1982 "registrations" => _
1986 test "get instance stats", %{conn: conn} do
1987 user = insert(:user, %{local: true})
1989 user2 = insert(:user, %{local: true})
1990 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1992 insert(:user, %{local: false, nickname: "u@peer1.com"})
1993 insert(:user, %{local: false, nickname: "u@peer2.com"})
1995 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
1997 # Stats should count users with missing or nil `info.deactivated` value
1998 user = User.get_by_id(user.id)
1999 info_change = Changeset.change(user.info, %{deactivated: nil})
2003 |> Changeset.change()
2004 |> Changeset.put_embed(:info, info_change)
2005 |> User.update_and_set_cache()
2007 Pleroma.Stats.update_stats()
2009 conn = get(conn, "/api/v1/instance")
2011 assert result = json_response(conn, 200)
2013 stats = result["stats"]
2016 assert stats["user_count"] == 1
2017 assert stats["status_count"] == 1
2018 assert stats["domain_count"] == 2
2021 test "get peers", %{conn: conn} do
2022 insert(:user, %{local: false, nickname: "u@peer1.com"})
2023 insert(:user, %{local: false, nickname: "u@peer2.com"})
2025 Pleroma.Stats.update_stats()
2027 conn = get(conn, "/api/v1/instance/peers")
2029 assert result = json_response(conn, 200)
2031 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2034 test "put settings", %{conn: conn} do
2035 user = insert(:user)
2039 |> assign(:user, user)
2040 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2042 assert _result = json_response(conn, 200)
2044 user = User.get_cached_by_ap_id(user.ap_id)
2045 assert user.info.settings == %{"programming" => "socks"}
2048 describe "pinned statuses" do
2050 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2052 user = insert(:user)
2053 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2055 [user: user, activity: activity]
2058 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2059 {:ok, _} = CommonAPI.pin(activity.id, user)
2063 |> assign(:user, user)
2064 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2065 |> json_response(200)
2067 id_str = to_string(activity.id)
2069 assert [%{"id" => ^id_str, "pinned" => true}] = result
2072 test "pin status", %{conn: conn, user: user, activity: activity} do
2073 id_str = to_string(activity.id)
2075 assert %{"id" => ^id_str, "pinned" => true} =
2077 |> assign(:user, user)
2078 |> post("/api/v1/statuses/#{activity.id}/pin")
2079 |> json_response(200)
2081 assert [%{"id" => ^id_str, "pinned" => true}] =
2083 |> assign(:user, user)
2084 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2085 |> json_response(200)
2088 test "unpin status", %{conn: conn, user: user, activity: activity} do
2089 {:ok, _} = CommonAPI.pin(activity.id, user)
2091 id_str = to_string(activity.id)
2092 user = refresh_record(user)
2094 assert %{"id" => ^id_str, "pinned" => false} =
2096 |> assign(:user, user)
2097 |> post("/api/v1/statuses/#{activity.id}/unpin")
2098 |> json_response(200)
2102 |> assign(:user, user)
2103 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2104 |> json_response(200)
2107 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2108 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2110 id_str_one = to_string(activity_one.id)
2112 assert %{"id" => ^id_str_one, "pinned" => true} =
2114 |> assign(:user, user)
2115 |> post("/api/v1/statuses/#{id_str_one}/pin")
2116 |> json_response(200)
2118 user = refresh_record(user)
2120 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2122 |> assign(:user, user)
2123 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2124 |> json_response(400)
2127 test "Status rich-media Card", %{conn: conn, user: user} do
2128 Pleroma.Config.put([:rich_media, :enabled], true)
2129 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2133 |> get("/api/v1/statuses/#{activity.id}/card")
2134 |> json_response(200)
2136 assert response == %{
2137 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2138 "provider_name" => "www.imdb.com",
2139 "provider_url" => "http://www.imdb.com",
2140 "title" => "The Rock",
2142 "url" => "http://www.imdb.com/title/tt0117500/",
2143 "description" => nil,
2146 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2147 "title" => "The Rock",
2148 "type" => "video.movie",
2149 "url" => "http://www.imdb.com/title/tt0117500/"
2154 # works with private posts
2156 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2160 |> assign(:user, user)
2161 |> get("/api/v1/statuses/#{activity.id}/card")
2162 |> json_response(200)
2164 assert response_two == response
2166 Pleroma.Config.put([:rich_media, :enabled], false)
2171 user = insert(:user)
2172 for_user = insert(:user)
2175 CommonAPI.post(user, %{
2176 "status" => "heweoo?"
2180 CommonAPI.post(user, %{
2181 "status" => "heweoo!"
2186 |> assign(:user, for_user)
2187 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2189 assert json_response(response1, 200)["bookmarked"] == true
2193 |> assign(:user, for_user)
2194 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2196 assert json_response(response2, 200)["bookmarked"] == true
2200 |> assign(:user, for_user)
2201 |> get("/api/v1/bookmarks")
2203 assert [json_response(response2, 200), json_response(response1, 200)] ==
2204 json_response(bookmarks, 200)
2208 |> assign(:user, for_user)
2209 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2211 assert json_response(response1, 200)["bookmarked"] == false
2215 |> assign(:user, for_user)
2216 |> get("/api/v1/bookmarks")
2218 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2221 describe "conversation muting" do
2223 user = insert(:user)
2224 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2226 [user: user, activity: activity]
2229 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2230 id_str = to_string(activity.id)
2232 assert %{"id" => ^id_str, "muted" => true} =
2234 |> assign(:user, user)
2235 |> post("/api/v1/statuses/#{activity.id}/mute")
2236 |> json_response(200)
2239 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2240 {:ok, _} = CommonAPI.add_mute(user, activity)
2242 id_str = to_string(activity.id)
2243 user = refresh_record(user)
2245 assert %{"id" => ^id_str, "muted" => false} =
2247 |> assign(:user, user)
2248 |> post("/api/v1/statuses/#{activity.id}/unmute")
2249 |> json_response(200)
2253 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2254 user = insert(:user)
2258 |> assign(:user, user)
2259 |> get("/api/v1/pleroma/flavour")
2261 assert "glitch" == json_response(get_old_flavour, 200)
2265 |> assign(:user, user)
2266 |> post("/api/v1/pleroma/flavour/vanilla")
2268 assert "vanilla" == json_response(set_flavour, 200)
2272 |> assign(:user, user)
2273 |> post("/api/v1/pleroma/flavour/vanilla")
2275 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2278 describe "reports" do
2280 reporter = insert(:user)
2281 target_user = insert(:user)
2283 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2285 [reporter: reporter, target_user: target_user, activity: activity]
2288 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2289 assert %{"action_taken" => false, "id" => _} =
2291 |> assign(:user, reporter)
2292 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2293 |> json_response(200)
2296 test "submit a report with statuses and comment", %{
2299 target_user: target_user,
2302 assert %{"action_taken" => false, "id" => _} =
2304 |> assign(:user, reporter)
2305 |> post("/api/v1/reports", %{
2306 "account_id" => target_user.id,
2307 "status_ids" => [activity.id],
2308 "comment" => "bad status!"
2310 |> json_response(200)
2313 test "account_id is required", %{
2318 assert %{"error" => "Valid `account_id` required"} =
2320 |> assign(:user, reporter)
2321 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2322 |> json_response(400)
2325 test "comment must be up to the size specified in the config", %{
2328 target_user: target_user
2330 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2331 comment = String.pad_trailing("a", max_size + 1, "a")
2333 error = %{"error" => "Comment must be up to #{max_size} characters"}
2337 |> assign(:user, reporter)
2338 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2339 |> json_response(400)
2343 describe "link headers" do
2344 test "preserves parameters in link headers", %{conn: conn} do
2345 user = insert(:user)
2346 other_user = insert(:user)
2349 CommonAPI.post(other_user, %{
2350 "status" => "hi @#{user.nickname}",
2351 "visibility" => "public"
2355 CommonAPI.post(other_user, %{
2356 "status" => "hi @#{user.nickname}",
2357 "visibility" => "public"
2360 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2361 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2365 |> assign(:user, user)
2366 |> get("/api/v1/notifications", %{media_only: true})
2368 assert [link_header] = get_resp_header(conn, "link")
2369 assert link_header =~ ~r/media_only=true/
2370 assert link_header =~ ~r/since_id=#{notification2.id}/
2371 assert link_header =~ ~r/max_id=#{notification1.id}/
2375 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2376 # Need to set an old-style integer ID to reproduce the problem
2377 # (these are no longer assigned to new accounts but were preserved
2378 # for existing accounts during the migration to flakeIDs)
2379 user_one = insert(:user, %{id: 1212})
2380 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2384 |> get("/api/v1/accounts/#{user_one.id}")
2388 |> get("/api/v1/accounts/#{user_two.nickname}")
2392 |> get("/api/v1/accounts/#{user_two.id}")
2394 acc_one = json_response(resp_one, 200)
2395 acc_two = json_response(resp_two, 200)
2396 acc_three = json_response(resp_three, 200)
2397 refute acc_one == acc_two
2398 assert acc_two == acc_three
2401 describe "custom emoji" do
2402 test "with tags", %{conn: conn} do
2405 |> get("/api/v1/custom_emojis")
2406 |> json_response(200)
2408 assert Map.has_key?(emoji, "shortcode")
2409 assert Map.has_key?(emoji, "static_url")
2410 assert Map.has_key?(emoji, "tags")
2411 assert is_list(emoji["tags"])
2412 assert Map.has_key?(emoji, "url")
2413 assert Map.has_key?(emoji, "visible_in_picker")
2417 describe "index/2 redirections" do
2418 setup %{conn: conn} do
2422 signing_salt: "cooldude"
2427 |> Plug.Session.call(Plug.Session.init(session_opts))
2430 test_path = "/web/statuses/test"
2431 %{conn: conn, path: test_path}
2434 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2435 conn = get(conn, path)
2437 assert conn.status == 302
2438 assert redirected_to(conn) == "/web/login"
2441 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2442 token = insert(:oauth_token)
2446 |> assign(:user, token.user)
2447 |> put_session(:oauth_token, token.token)
2450 assert conn.status == 200
2453 test "saves referer path to session", %{conn: conn, path: path} do
2454 conn = get(conn, path)
2455 return_to = Plug.Conn.get_session(conn, :return_to)
2457 assert return_to == path
2460 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2461 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2462 auth = insert(:oauth_authorization, app: app)
2466 |> put_session(:return_to, path)
2467 |> get("/web/login", %{code: auth.token})
2469 assert conn.status == 302
2470 assert redirected_to(conn) == path
2473 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2474 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2475 auth = insert(:oauth_authorization, app: app)
2477 conn = get(conn, "/web/login", %{code: auth.token})
2479 assert conn.status == 302
2480 assert redirected_to(conn) == "/web/getting-started"
2484 describe "scheduled activities" do
2485 test "creates a scheduled activity", %{conn: conn} do
2486 user = insert(:user)
2487 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2491 |> assign(:user, user)
2492 |> post("/api/v1/statuses", %{
2493 "status" => "scheduled",
2494 "scheduled_at" => scheduled_at
2497 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2498 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2499 assert [] == Repo.all(Activity)
2502 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2503 user = insert(:user)
2504 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2506 file = %Plug.Upload{
2507 content_type: "image/jpg",
2508 path: Path.absname("test/fixtures/image.jpg"),
2509 filename: "an_image.jpg"
2512 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
2516 |> assign(:user, user)
2517 |> post("/api/v1/statuses", %{
2518 "media_ids" => [to_string(upload.id)],
2519 "status" => "scheduled",
2520 "scheduled_at" => scheduled_at
2523 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
2524 assert %{"type" => "image"} = media_attachment
2527 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
2529 user = insert(:user)
2532 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
2536 |> assign(:user, user)
2537 |> post("/api/v1/statuses", %{
2538 "status" => "not scheduled",
2539 "scheduled_at" => scheduled_at
2542 assert %{"content" => "not scheduled"} = json_response(conn, 200)
2543 assert [] == Repo.all(ScheduledActivity)
2546 test "returns error when daily user limit is exceeded", %{conn: conn} do
2547 user = insert(:user)
2550 NaiveDateTime.utc_now()
2551 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2552 |> NaiveDateTime.to_iso8601()
2554 attrs = %{params: %{}, scheduled_at: today}
2555 {:ok, _} = ScheduledActivity.create(user, attrs)
2556 {:ok, _} = ScheduledActivity.create(user, attrs)
2560 |> assign(:user, user)
2561 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
2563 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
2566 test "returns error when total user limit is exceeded", %{conn: conn} do
2567 user = insert(:user)
2570 NaiveDateTime.utc_now()
2571 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2572 |> NaiveDateTime.to_iso8601()
2575 NaiveDateTime.utc_now()
2576 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
2577 |> NaiveDateTime.to_iso8601()
2579 attrs = %{params: %{}, scheduled_at: today}
2580 {:ok, _} = ScheduledActivity.create(user, attrs)
2581 {:ok, _} = ScheduledActivity.create(user, attrs)
2582 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
2586 |> assign(:user, user)
2587 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
2589 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
2592 test "shows scheduled activities", %{conn: conn} do
2593 user = insert(:user)
2594 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
2595 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
2596 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
2597 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
2601 |> assign(:user, user)
2606 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
2608 result = json_response(conn_res, 200)
2609 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2614 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
2616 result = json_response(conn_res, 200)
2617 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
2622 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
2624 result = json_response(conn_res, 200)
2625 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2628 test "shows a scheduled activity", %{conn: conn} do
2629 user = insert(:user)
2630 scheduled_activity = insert(:scheduled_activity, user: user)
2634 |> assign(:user, user)
2635 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2637 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
2638 assert scheduled_activity_id == scheduled_activity.id |> to_string()
2642 |> assign(:user, user)
2643 |> get("/api/v1/scheduled_statuses/404")
2645 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2648 test "updates a scheduled activity", %{conn: conn} do
2649 user = insert(:user)
2650 scheduled_activity = insert(:scheduled_activity, user: user)
2653 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2657 |> assign(:user, user)
2658 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
2659 scheduled_at: new_scheduled_at
2662 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
2663 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
2667 |> assign(:user, user)
2668 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
2670 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2673 test "deletes a scheduled activity", %{conn: conn} do
2674 user = insert(:user)
2675 scheduled_activity = insert(:scheduled_activity, user: user)
2679 |> assign(:user, user)
2680 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2682 assert %{} = json_response(res_conn, 200)
2683 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
2687 |> assign(:user, user)
2688 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2690 assert %{"error" => "Record not found"} = json_response(res_conn, 404)