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)
330 "accounts" => res_accounts,
331 "last_status" => res_last_status,
335 assert unread == false
337 # Apparently undocumented API endpoint
340 |> assign(:user, user_one)
341 |> post("/api/v1/conversations/#{res_id}/read")
343 assert response == json_response(res_conn, 200)
345 # (vanilla) Mastodon frontend behaviour
348 |> assign(:user, user_one)
349 |> get("/api/v1/statuses/#{res_last_status.id}/context")
351 assert %{ancestors: [], descendants: []} == json_response(res_conn, 200)
354 test "doesn't include DMs from blocked users", %{conn: conn} do
355 blocker = insert(:user)
356 blocked = insert(:user)
358 {:ok, blocker} = User.block(blocker, blocked)
360 {:ok, _blocked_direct} =
361 CommonAPI.post(blocked, %{
362 "status" => "Hi @#{blocker.nickname}!",
363 "visibility" => "direct"
367 CommonAPI.post(user, %{
368 "status" => "Hi @#{blocker.nickname}!",
369 "visibility" => "direct"
374 |> assign(:user, user)
375 |> get("api/v1/timelines/direct")
377 [status] = json_response(res_conn, 200)
378 assert status["id"] == direct.id
381 test "replying to a status", %{conn: conn} do
384 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
388 |> assign(:user, user)
389 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
391 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
393 activity = Activity.get_by_id(id)
395 assert activity.data["context"] == replied_to.data["context"]
396 assert activity.data["object"]["inReplyToStatusId"] == replied_to.id
399 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
404 |> assign(:user, user)
405 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
407 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
409 activity = Activity.get_by_id(id)
414 test "verify_credentials", %{conn: conn} do
419 |> assign(:user, user)
420 |> get("/api/v1/accounts/verify_credentials")
422 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
423 assert id == to_string(user.id)
426 test "verify_credentials default scope unlisted", %{conn: conn} do
427 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "unlisted"}})
431 |> assign(:user, user)
432 |> get("/api/v1/accounts/verify_credentials")
434 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
435 assert id == to_string(user.id)
438 test "apps/verify_credentials", %{conn: conn} do
439 token = insert(:oauth_token)
443 |> assign(:user, token.user)
444 |> assign(:token, token)
445 |> get("/api/v1/apps/verify_credentials")
447 app = Repo.preload(token, :app).app
450 "name" => app.client_name,
451 "website" => app.website,
452 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
455 assert expected == json_response(conn, 200)
458 test "creates an oauth app", %{conn: conn} do
460 app_attrs = build(:oauth_app)
464 |> assign(:user, user)
465 |> post("/api/v1/apps", %{
466 client_name: app_attrs.client_name,
467 redirect_uris: app_attrs.redirect_uris
470 [app] = Repo.all(App)
473 "name" => app.client_name,
474 "website" => app.website,
475 "client_id" => app.client_id,
476 "client_secret" => app.client_secret,
477 "id" => app.id |> to_string(),
478 "redirect_uri" => app.redirect_uris,
479 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
482 assert expected == json_response(conn, 200)
485 test "get a status", %{conn: conn} do
486 activity = insert(:note_activity)
490 |> get("/api/v1/statuses/#{activity.id}")
492 assert %{"id" => id} = json_response(conn, 200)
493 assert id == to_string(activity.id)
496 describe "deleting a status" do
497 test "when you created it", %{conn: conn} do
498 activity = insert(:note_activity)
499 author = User.get_by_ap_id(activity.data["actor"])
503 |> assign(:user, author)
504 |> delete("/api/v1/statuses/#{activity.id}")
506 assert %{} = json_response(conn, 200)
508 refute Activity.get_by_id(activity.id)
511 test "when you didn't create it", %{conn: conn} do
512 activity = insert(:note_activity)
517 |> assign(:user, user)
518 |> delete("/api/v1/statuses/#{activity.id}")
520 assert %{"error" => _} = json_response(conn, 403)
522 assert Activity.get_by_id(activity.id) == activity
525 test "when you're an admin or moderator", %{conn: conn} do
526 activity1 = insert(:note_activity)
527 activity2 = insert(:note_activity)
528 admin = insert(:user, info: %{is_admin: true})
529 moderator = insert(:user, info: %{is_moderator: true})
533 |> assign(:user, admin)
534 |> delete("/api/v1/statuses/#{activity1.id}")
536 assert %{} = json_response(res_conn, 200)
540 |> assign(:user, moderator)
541 |> delete("/api/v1/statuses/#{activity2.id}")
543 assert %{} = json_response(res_conn, 200)
545 refute Activity.get_by_id(activity1.id)
546 refute Activity.get_by_id(activity2.id)
550 describe "filters" do
551 test "creating a filter", %{conn: conn} do
554 filter = %Pleroma.Filter{
561 |> assign(:user, user)
562 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
564 assert response = json_response(conn, 200)
565 assert response["phrase"] == filter.phrase
566 assert response["context"] == filter.context
567 assert response["id"] != nil
568 assert response["id"] != ""
571 test "fetching a list of filters", %{conn: conn} do
574 query_one = %Pleroma.Filter{
581 query_two = %Pleroma.Filter{
588 {:ok, filter_one} = Pleroma.Filter.create(query_one)
589 {:ok, filter_two} = Pleroma.Filter.create(query_two)
593 |> assign(:user, user)
594 |> get("/api/v1/filters")
595 |> json_response(200)
601 filters: [filter_two, filter_one]
605 test "get a filter", %{conn: conn} do
608 query = %Pleroma.Filter{
615 {:ok, filter} = Pleroma.Filter.create(query)
619 |> assign(:user, user)
620 |> get("/api/v1/filters/#{filter.filter_id}")
622 assert _response = json_response(conn, 200)
625 test "update a filter", %{conn: conn} do
628 query = %Pleroma.Filter{
635 {:ok, _filter} = Pleroma.Filter.create(query)
637 new = %Pleroma.Filter{
644 |> assign(:user, user)
645 |> put("/api/v1/filters/#{query.filter_id}", %{
650 assert response = json_response(conn, 200)
651 assert response["phrase"] == new.phrase
652 assert response["context"] == new.context
655 test "delete a filter", %{conn: conn} do
658 query = %Pleroma.Filter{
665 {:ok, filter} = Pleroma.Filter.create(query)
669 |> assign(:user, user)
670 |> delete("/api/v1/filters/#{filter.filter_id}")
672 assert response = json_response(conn, 200)
673 assert response == %{}
678 test "creating a list", %{conn: conn} do
683 |> assign(:user, user)
684 |> post("/api/v1/lists", %{"title" => "cuties"})
686 assert %{"title" => title} = json_response(conn, 200)
687 assert title == "cuties"
690 test "adding users to a list", %{conn: conn} do
692 other_user = insert(:user)
693 {:ok, list} = Pleroma.List.create("name", user)
697 |> assign(:user, user)
698 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
700 assert %{} == json_response(conn, 200)
701 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
702 assert following == [other_user.follower_address]
705 test "removing users from a list", %{conn: conn} do
707 other_user = insert(:user)
708 third_user = insert(:user)
709 {:ok, list} = Pleroma.List.create("name", user)
710 {:ok, list} = Pleroma.List.follow(list, other_user)
711 {:ok, list} = Pleroma.List.follow(list, third_user)
715 |> assign(:user, user)
716 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
718 assert %{} == json_response(conn, 200)
719 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
720 assert following == [third_user.follower_address]
723 test "listing users in a list", %{conn: conn} do
725 other_user = insert(:user)
726 {:ok, list} = Pleroma.List.create("name", user)
727 {:ok, list} = Pleroma.List.follow(list, other_user)
731 |> assign(:user, user)
732 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
734 assert [%{"id" => id}] = json_response(conn, 200)
735 assert id == to_string(other_user.id)
738 test "retrieving a list", %{conn: conn} do
740 {:ok, list} = Pleroma.List.create("name", user)
744 |> assign(:user, user)
745 |> get("/api/v1/lists/#{list.id}")
747 assert %{"id" => id} = json_response(conn, 200)
748 assert id == to_string(list.id)
751 test "renaming a list", %{conn: conn} do
753 {:ok, list} = Pleroma.List.create("name", user)
757 |> assign(:user, user)
758 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
760 assert %{"title" => name} = json_response(conn, 200)
761 assert name == "newname"
764 test "deleting a list", %{conn: conn} do
766 {:ok, list} = Pleroma.List.create("name", user)
770 |> assign(:user, user)
771 |> delete("/api/v1/lists/#{list.id}")
773 assert %{} = json_response(conn, 200)
774 assert is_nil(Repo.get(Pleroma.List, list.id))
777 test "list timeline", %{conn: conn} do
779 other_user = insert(:user)
780 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
781 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
782 {:ok, list} = Pleroma.List.create("name", user)
783 {:ok, list} = Pleroma.List.follow(list, other_user)
787 |> assign(:user, user)
788 |> get("/api/v1/timelines/list/#{list.id}")
790 assert [%{"id" => id}] = json_response(conn, 200)
792 assert id == to_string(activity_two.id)
795 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
797 other_user = insert(:user)
798 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
800 {:ok, _activity_two} =
801 TwitterAPI.create_status(other_user, %{
802 "status" => "Marisa is cute.",
803 "visibility" => "private"
806 {:ok, list} = Pleroma.List.create("name", user)
807 {:ok, list} = Pleroma.List.follow(list, other_user)
811 |> assign(:user, user)
812 |> get("/api/v1/timelines/list/#{list.id}")
814 assert [%{"id" => id}] = json_response(conn, 200)
816 assert id == to_string(activity_one.id)
820 describe "notifications" do
821 test "list of notifications", %{conn: conn} do
823 other_user = insert(:user)
826 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
828 {:ok, [_notification]} = Notification.create_notifications(activity)
832 |> assign(:user, user)
833 |> get("/api/v1/notifications")
836 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
838 }\">@<span>#{user.nickname}</span></a></span>"
840 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
841 assert response == expected_response
844 test "getting a single notification", %{conn: conn} do
846 other_user = insert(:user)
849 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
851 {:ok, [notification]} = Notification.create_notifications(activity)
855 |> assign(:user, user)
856 |> get("/api/v1/notifications/#{notification.id}")
859 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
861 }\">@<span>#{user.nickname}</span></a></span>"
863 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
864 assert response == expected_response
867 test "dismissing a single notification", %{conn: conn} do
869 other_user = insert(:user)
872 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
874 {:ok, [notification]} = Notification.create_notifications(activity)
878 |> assign(:user, user)
879 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
881 assert %{} = json_response(conn, 200)
884 test "clearing all notifications", %{conn: conn} do
886 other_user = insert(:user)
889 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
891 {:ok, [_notification]} = Notification.create_notifications(activity)
895 |> assign(:user, user)
896 |> post("/api/v1/notifications/clear")
898 assert %{} = json_response(conn, 200)
902 |> assign(:user, user)
903 |> get("/api/v1/notifications")
905 assert all = json_response(conn, 200)
909 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
911 other_user = insert(:user)
913 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
914 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
915 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
916 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
918 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
919 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
920 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
921 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
925 |> assign(:user, user)
930 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
932 result = json_response(conn_res, 200)
933 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
938 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
940 result = json_response(conn_res, 200)
941 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
946 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
948 result = json_response(conn_res, 200)
949 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
952 test "filters notifications using exclude_types", %{conn: conn} do
954 other_user = insert(:user)
956 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
957 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
958 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
959 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
960 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
962 mention_notification_id =
963 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
965 favorite_notification_id =
966 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
968 reblog_notification_id =
969 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
971 follow_notification_id =
972 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
976 |> assign(:user, user)
979 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
981 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
984 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
986 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
989 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
991 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
994 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
996 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1000 describe "reblogging" do
1001 test "reblogs and returns the reblogged status", %{conn: conn} do
1002 activity = insert(:note_activity)
1003 user = insert(:user)
1007 |> assign(:user, user)
1008 |> post("/api/v1/statuses/#{activity.id}/reblog")
1010 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
1011 json_response(conn, 200)
1013 assert to_string(activity.id) == id
1017 describe "unreblogging" do
1018 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1019 activity = insert(:note_activity)
1020 user = insert(:user)
1022 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1026 |> assign(:user, user)
1027 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1029 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1031 assert to_string(activity.id) == id
1035 describe "favoriting" do
1036 test "favs a status and returns it", %{conn: conn} do
1037 activity = insert(:note_activity)
1038 user = insert(:user)
1042 |> assign(:user, user)
1043 |> post("/api/v1/statuses/#{activity.id}/favourite")
1045 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1046 json_response(conn, 200)
1048 assert to_string(activity.id) == id
1051 test "returns 500 for a wrong id", %{conn: conn} do
1052 user = insert(:user)
1056 |> assign(:user, user)
1057 |> post("/api/v1/statuses/1/favourite")
1058 |> json_response(500)
1060 assert resp == "Something went wrong"
1064 describe "unfavoriting" do
1065 test "unfavorites a status and returns it", %{conn: conn} do
1066 activity = insert(:note_activity)
1067 user = insert(:user)
1069 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1073 |> assign(:user, user)
1074 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1076 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1077 json_response(conn, 200)
1079 assert to_string(activity.id) == id
1083 describe "user timelines" do
1084 test "gets a users statuses", %{conn: conn} do
1085 user_one = insert(:user)
1086 user_two = insert(:user)
1087 user_three = insert(:user)
1089 {:ok, user_three} = User.follow(user_three, user_one)
1091 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1093 {:ok, direct_activity} =
1094 CommonAPI.post(user_one, %{
1095 "status" => "Hi, @#{user_two.nickname}.",
1096 "visibility" => "direct"
1099 {:ok, private_activity} =
1100 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1104 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1106 assert [%{"id" => id}] = json_response(resp, 200)
1107 assert id == to_string(activity.id)
1111 |> assign(:user, user_two)
1112 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1114 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1115 assert id_one == to_string(direct_activity.id)
1116 assert id_two == to_string(activity.id)
1120 |> assign(:user, user_three)
1121 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1123 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1124 assert id_one == to_string(private_activity.id)
1125 assert id_two == to_string(activity.id)
1128 test "unimplemented pinned statuses feature", %{conn: conn} do
1129 note = insert(:note_activity)
1130 user = User.get_by_ap_id(note.data["actor"])
1134 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1136 assert json_response(conn, 200) == []
1139 test "gets an users media", %{conn: conn} do
1140 note = insert(:note_activity)
1141 user = User.get_by_ap_id(note.data["actor"])
1143 file = %Plug.Upload{
1144 content_type: "image/jpg",
1145 path: Path.absname("test/fixtures/image.jpg"),
1146 filename: "an_image.jpg"
1150 TwitterAPI.upload(file, user, "json")
1154 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1158 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1160 assert [%{"id" => id}] = json_response(conn, 200)
1161 assert id == to_string(image_post.id)
1165 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1167 assert [%{"id" => id}] = json_response(conn, 200)
1168 assert id == to_string(image_post.id)
1171 test "gets a user's statuses without reblogs", %{conn: conn} do
1172 user = insert(:user)
1173 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1174 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1178 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1180 assert [%{"id" => id}] = json_response(conn, 200)
1181 assert id == to_string(post.id)
1185 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1187 assert [%{"id" => id}] = json_response(conn, 200)
1188 assert id == to_string(post.id)
1192 describe "user relationships" do
1193 test "returns the relationships for the current user", %{conn: conn} do
1194 user = insert(:user)
1195 other_user = insert(:user)
1196 {:ok, user} = User.follow(user, other_user)
1200 |> assign(:user, user)
1201 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1203 assert [relationship] = json_response(conn, 200)
1205 assert to_string(other_user.id) == relationship["id"]
1209 describe "locked accounts" do
1210 test "/api/v1/follow_requests works" do
1211 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1212 other_user = insert(:user)
1214 {:ok, _activity} = ActivityPub.follow(other_user, user)
1216 user = User.get_by_id(user.id)
1217 other_user = User.get_by_id(other_user.id)
1219 assert User.following?(other_user, user) == false
1223 |> assign(:user, user)
1224 |> get("/api/v1/follow_requests")
1226 assert [relationship] = json_response(conn, 200)
1227 assert to_string(other_user.id) == relationship["id"]
1230 test "/api/v1/follow_requests/:id/authorize works" do
1231 user = insert(:user, %{info: %User.Info{locked: true}})
1232 other_user = insert(:user)
1234 {:ok, _activity} = ActivityPub.follow(other_user, user)
1236 user = User.get_by_id(user.id)
1237 other_user = User.get_by_id(other_user.id)
1239 assert User.following?(other_user, user) == false
1243 |> assign(:user, user)
1244 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1246 assert relationship = json_response(conn, 200)
1247 assert to_string(other_user.id) == relationship["id"]
1249 user = User.get_by_id(user.id)
1250 other_user = User.get_by_id(other_user.id)
1252 assert User.following?(other_user, user) == true
1255 test "verify_credentials", %{conn: conn} do
1256 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "private"}})
1260 |> assign(:user, user)
1261 |> get("/api/v1/accounts/verify_credentials")
1263 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1264 assert id == to_string(user.id)
1267 test "/api/v1/follow_requests/:id/reject works" do
1268 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1269 other_user = insert(:user)
1271 {:ok, _activity} = ActivityPub.follow(other_user, user)
1273 user = User.get_by_id(user.id)
1277 |> assign(:user, user)
1278 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1280 assert relationship = json_response(conn, 200)
1281 assert to_string(other_user.id) == relationship["id"]
1283 user = User.get_by_id(user.id)
1284 other_user = User.get_by_id(other_user.id)
1286 assert User.following?(other_user, user) == false
1290 test "account fetching", %{conn: conn} do
1291 user = insert(:user)
1295 |> get("/api/v1/accounts/#{user.id}")
1297 assert %{"id" => id} = json_response(conn, 200)
1298 assert id == to_string(user.id)
1302 |> get("/api/v1/accounts/-1")
1304 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1307 test "account fetching also works nickname", %{conn: conn} do
1308 user = insert(:user)
1312 |> get("/api/v1/accounts/#{user.nickname}")
1314 assert %{"id" => id} = json_response(conn, 200)
1315 assert id == user.id
1318 test "media upload", %{conn: conn} do
1319 file = %Plug.Upload{
1320 content_type: "image/jpg",
1321 path: Path.absname("test/fixtures/image.jpg"),
1322 filename: "an_image.jpg"
1325 desc = "Description of the image"
1327 user = insert(:user)
1331 |> assign(:user, user)
1332 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1334 assert media = json_response(conn, 200)
1336 assert media["type"] == "image"
1337 assert media["description"] == desc
1340 object = Repo.get(Object, media["id"])
1341 assert object.data["actor"] == User.ap_id(user)
1344 test "hashtag timeline", %{conn: conn} do
1345 following = insert(:user)
1348 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1350 {:ok, [_activity]} =
1351 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1355 |> get("/api/v1/timelines/tag/2hu")
1357 assert [%{"id" => id}] = json_response(nconn, 200)
1359 assert id == to_string(activity.id)
1361 # works for different capitalization too
1364 |> get("/api/v1/timelines/tag/2HU")
1366 assert [%{"id" => id}] = json_response(nconn, 200)
1368 assert id == to_string(activity.id)
1372 test "multi-hashtag timeline", %{conn: conn} do
1373 user = insert(:user)
1375 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1376 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1377 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1381 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1383 [status_none, status_test1, status_test] = json_response(any_test, 200)
1385 assert to_string(activity_test.id) == status_test["id"]
1386 assert to_string(activity_test1.id) == status_test1["id"]
1387 assert to_string(activity_none.id) == status_none["id"]
1391 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1393 assert [status_test1] == json_response(restricted_test, 200)
1395 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1397 assert [status_none] == json_response(all_test, 200)
1400 test "getting followers", %{conn: conn} do
1401 user = insert(:user)
1402 other_user = insert(:user)
1403 {:ok, user} = User.follow(user, other_user)
1407 |> get("/api/v1/accounts/#{other_user.id}/followers")
1409 assert [%{"id" => id}] = json_response(conn, 200)
1410 assert id == to_string(user.id)
1413 test "getting followers, hide_followers", %{conn: conn} do
1414 user = insert(:user)
1415 other_user = insert(:user, %{info: %{hide_followers: true}})
1416 {:ok, _user} = User.follow(user, other_user)
1420 |> get("/api/v1/accounts/#{other_user.id}/followers")
1422 assert [] == json_response(conn, 200)
1425 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1426 user = insert(:user)
1427 other_user = insert(:user, %{info: %{hide_followers: true}})
1428 {:ok, _user} = User.follow(user, other_user)
1432 |> assign(:user, other_user)
1433 |> get("/api/v1/accounts/#{other_user.id}/followers")
1435 refute [] == json_response(conn, 200)
1438 test "getting followers, pagination", %{conn: conn} do
1439 user = insert(:user)
1440 follower1 = insert(:user)
1441 follower2 = insert(:user)
1442 follower3 = insert(:user)
1443 {:ok, _} = User.follow(follower1, user)
1444 {:ok, _} = User.follow(follower2, user)
1445 {:ok, _} = User.follow(follower3, user)
1449 |> assign(:user, user)
1453 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1455 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1456 assert id3 == follower3.id
1457 assert id2 == follower2.id
1461 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1463 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1464 assert id2 == follower2.id
1465 assert id1 == follower1.id
1469 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1471 assert [%{"id" => id2}] = json_response(res_conn, 200)
1472 assert id2 == follower2.id
1474 assert [link_header] = get_resp_header(res_conn, "link")
1475 assert link_header =~ ~r/since_id=#{follower2.id}/
1476 assert link_header =~ ~r/max_id=#{follower2.id}/
1479 test "getting following", %{conn: conn} do
1480 user = insert(:user)
1481 other_user = insert(:user)
1482 {:ok, user} = User.follow(user, other_user)
1486 |> get("/api/v1/accounts/#{user.id}/following")
1488 assert [%{"id" => id}] = json_response(conn, 200)
1489 assert id == to_string(other_user.id)
1492 test "getting following, hide_follows", %{conn: conn} do
1493 user = insert(:user, %{info: %{hide_follows: true}})
1494 other_user = insert(:user)
1495 {:ok, user} = User.follow(user, other_user)
1499 |> get("/api/v1/accounts/#{user.id}/following")
1501 assert [] == json_response(conn, 200)
1504 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1505 user = insert(:user, %{info: %{hide_follows: true}})
1506 other_user = insert(:user)
1507 {:ok, user} = User.follow(user, other_user)
1511 |> assign(:user, user)
1512 |> get("/api/v1/accounts/#{user.id}/following")
1514 refute [] == json_response(conn, 200)
1517 test "getting following, pagination", %{conn: conn} do
1518 user = insert(:user)
1519 following1 = insert(:user)
1520 following2 = insert(:user)
1521 following3 = insert(:user)
1522 {:ok, _} = User.follow(user, following1)
1523 {:ok, _} = User.follow(user, following2)
1524 {:ok, _} = User.follow(user, following3)
1528 |> assign(:user, user)
1532 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1534 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1535 assert id3 == following3.id
1536 assert id2 == following2.id
1540 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1542 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1543 assert id2 == following2.id
1544 assert id1 == following1.id
1548 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1550 assert [%{"id" => id2}] = json_response(res_conn, 200)
1551 assert id2 == following2.id
1553 assert [link_header] = get_resp_header(res_conn, "link")
1554 assert link_header =~ ~r/since_id=#{following2.id}/
1555 assert link_header =~ ~r/max_id=#{following2.id}/
1558 test "following / unfollowing a user", %{conn: conn} do
1559 user = insert(:user)
1560 other_user = insert(:user)
1564 |> assign(:user, user)
1565 |> post("/api/v1/accounts/#{other_user.id}/follow")
1567 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1569 user = User.get_by_id(user.id)
1573 |> assign(:user, user)
1574 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1576 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1578 user = User.get_by_id(user.id)
1582 |> assign(:user, user)
1583 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1585 assert %{"id" => id} = json_response(conn, 200)
1586 assert id == to_string(other_user.id)
1589 test "muting / unmuting a user", %{conn: conn} do
1590 user = insert(:user)
1591 other_user = insert(:user)
1595 |> assign(:user, user)
1596 |> post("/api/v1/accounts/#{other_user.id}/mute")
1598 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1600 user = User.get_by_id(user.id)
1604 |> assign(:user, user)
1605 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1607 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1610 test "getting a list of mutes", %{conn: conn} do
1611 user = insert(:user)
1612 other_user = insert(:user)
1614 {:ok, user} = User.mute(user, other_user)
1618 |> assign(:user, user)
1619 |> get("/api/v1/mutes")
1621 other_user_id = to_string(other_user.id)
1622 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1625 test "blocking / unblocking a user", %{conn: conn} do
1626 user = insert(:user)
1627 other_user = insert(:user)
1631 |> assign(:user, user)
1632 |> post("/api/v1/accounts/#{other_user.id}/block")
1634 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1636 user = User.get_by_id(user.id)
1640 |> assign(:user, user)
1641 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1643 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1646 test "getting a list of blocks", %{conn: conn} do
1647 user = insert(:user)
1648 other_user = insert(:user)
1650 {:ok, user} = User.block(user, other_user)
1654 |> assign(:user, user)
1655 |> get("/api/v1/blocks")
1657 other_user_id = to_string(other_user.id)
1658 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1661 test "blocking / unblocking a domain", %{conn: conn} do
1662 user = insert(:user)
1663 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1667 |> assign(:user, user)
1668 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1670 assert %{} = json_response(conn, 200)
1671 user = User.get_cached_by_ap_id(user.ap_id)
1672 assert User.blocks?(user, other_user)
1676 |> assign(:user, user)
1677 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1679 assert %{} = json_response(conn, 200)
1680 user = User.get_cached_by_ap_id(user.ap_id)
1681 refute User.blocks?(user, other_user)
1684 test "getting a list of domain blocks", %{conn: conn} do
1685 user = insert(:user)
1687 {:ok, user} = User.block_domain(user, "bad.site")
1688 {:ok, user} = User.block_domain(user, "even.worse.site")
1692 |> assign(:user, user)
1693 |> get("/api/v1/domain_blocks")
1695 domain_blocks = json_response(conn, 200)
1697 assert "bad.site" in domain_blocks
1698 assert "even.worse.site" in domain_blocks
1701 test "unimplemented follow_requests, blocks, domain blocks" do
1702 user = insert(:user)
1704 ["blocks", "domain_blocks", "follow_requests"]
1705 |> Enum.each(fn endpoint ->
1708 |> assign(:user, user)
1709 |> get("/api/v1/#{endpoint}")
1711 assert [] = json_response(conn, 200)
1715 test "account search", %{conn: conn} do
1716 user = insert(:user)
1717 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1718 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1722 |> assign(:user, user)
1723 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1724 |> json_response(200)
1726 result_ids = for result <- results, do: result["acct"]
1728 assert user_two.nickname in result_ids
1729 assert user_three.nickname in result_ids
1733 |> assign(:user, user)
1734 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
1735 |> json_response(200)
1737 result_ids = for result <- results, do: result["acct"]
1739 assert user_three.nickname in result_ids
1742 test "search", %{conn: conn} do
1743 user = insert(:user)
1744 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1745 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1747 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
1750 CommonAPI.post(user, %{
1751 "status" => "This is about 2hu, but private",
1752 "visibility" => "private"
1755 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
1759 |> get("/api/v1/search", %{"q" => "2hu"})
1761 assert results = json_response(conn, 200)
1763 [account | _] = results["accounts"]
1764 assert account["id"] == to_string(user_three.id)
1766 assert results["hashtags"] == []
1768 [status] = results["statuses"]
1769 assert status["id"] == to_string(activity.id)
1772 test "search fetches remote statuses", %{conn: conn} do
1776 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
1778 assert results = json_response(conn, 200)
1780 [status] = results["statuses"]
1781 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1785 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
1787 CommonAPI.post(insert(:user), %{
1788 "status" => "This is about 2hu, but private",
1789 "visibility" => "private"
1795 |> get("/api/v1/search", %{"q" => activity.data["object"]["id"]})
1797 assert results = json_response(conn, 200)
1799 [] = results["statuses"]
1803 test "search fetches remote accounts", %{conn: conn} do
1806 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
1808 assert results = json_response(conn, 200)
1809 [account] = results["accounts"]
1810 assert account["acct"] == "shp@social.heldscal.la"
1813 test "returns the favorites of a user", %{conn: conn} do
1814 user = insert(:user)
1815 other_user = insert(:user)
1817 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1818 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1820 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1824 |> assign(:user, user)
1825 |> get("/api/v1/favourites")
1827 assert [status] = json_response(first_conn, 200)
1828 assert status["id"] == to_string(activity.id)
1830 assert [{"link", _link_header}] =
1831 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1833 # Honours query params
1834 {:ok, second_activity} =
1835 CommonAPI.post(other_user, %{
1837 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1840 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1842 last_like = status["id"]
1846 |> assign(:user, user)
1847 |> get("/api/v1/favourites?since_id=#{last_like}")
1849 assert [second_status] = json_response(second_conn, 200)
1850 assert second_status["id"] == to_string(second_activity.id)
1854 |> assign(:user, user)
1855 |> get("/api/v1/favourites?limit=0")
1857 assert [] = json_response(third_conn, 200)
1860 describe "updating credentials" do
1861 test "updates the user's bio", %{conn: conn} do
1862 user = insert(:user)
1863 user2 = insert(:user)
1867 |> assign(:user, user)
1868 |> patch("/api/v1/accounts/update_credentials", %{
1869 "note" => "I drink #cofe with @#{user2.nickname}"
1872 assert user = json_response(conn, 200)
1874 assert user["note"] ==
1875 ~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=") <>
1877 ~s(" class="u-url mention" href=") <>
1878 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
1881 test "updates the user's locking status", %{conn: conn} do
1882 user = insert(:user)
1886 |> assign(:user, user)
1887 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
1889 assert user = json_response(conn, 200)
1890 assert user["locked"] == true
1893 test "updates the user's name", %{conn: conn} do
1894 user = insert(:user)
1898 |> assign(:user, user)
1899 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
1901 assert user = json_response(conn, 200)
1902 assert user["display_name"] == "markorepairs"
1905 test "updates the user's avatar", %{conn: conn} do
1906 user = insert(:user)
1908 new_avatar = %Plug.Upload{
1909 content_type: "image/jpg",
1910 path: Path.absname("test/fixtures/image.jpg"),
1911 filename: "an_image.jpg"
1916 |> assign(:user, user)
1917 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
1919 assert user_response = json_response(conn, 200)
1920 assert user_response["avatar"] != User.avatar_url(user)
1923 test "updates the user's banner", %{conn: conn} do
1924 user = insert(:user)
1926 new_header = %Plug.Upload{
1927 content_type: "image/jpg",
1928 path: Path.absname("test/fixtures/image.jpg"),
1929 filename: "an_image.jpg"
1934 |> assign(:user, user)
1935 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
1937 assert user_response = json_response(conn, 200)
1938 assert user_response["header"] != User.banner_url(user)
1941 test "requires 'write' permission", %{conn: conn} do
1942 token1 = insert(:oauth_token, scopes: ["read"])
1943 token2 = insert(:oauth_token, scopes: ["write", "follow"])
1945 for token <- [token1, token2] do
1948 |> put_req_header("authorization", "Bearer #{token.token}")
1949 |> patch("/api/v1/accounts/update_credentials", %{})
1951 if token == token1 do
1952 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
1954 assert json_response(conn, 200)
1960 test "get instance information", %{conn: conn} do
1961 conn = get(conn, "/api/v1/instance")
1962 assert result = json_response(conn, 200)
1964 # Note: not checking for "max_toot_chars" since it's optional
1972 "streaming_api" => _
1977 "registrations" => _
1981 test "get instance stats", %{conn: conn} do
1982 user = insert(:user, %{local: true})
1984 user2 = insert(:user, %{local: true})
1985 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1987 insert(:user, %{local: false, nickname: "u@peer1.com"})
1988 insert(:user, %{local: false, nickname: "u@peer2.com"})
1990 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
1992 # Stats should count users with missing or nil `info.deactivated` value
1993 user = User.get_by_id(user.id)
1994 info_change = Changeset.change(user.info, %{deactivated: nil})
1998 |> Changeset.change()
1999 |> Changeset.put_embed(:info, info_change)
2000 |> User.update_and_set_cache()
2002 Pleroma.Stats.update_stats()
2004 conn = get(conn, "/api/v1/instance")
2006 assert result = json_response(conn, 200)
2008 stats = result["stats"]
2011 assert stats["user_count"] == 1
2012 assert stats["status_count"] == 1
2013 assert stats["domain_count"] == 2
2016 test "get peers", %{conn: conn} do
2017 insert(:user, %{local: false, nickname: "u@peer1.com"})
2018 insert(:user, %{local: false, nickname: "u@peer2.com"})
2020 Pleroma.Stats.update_stats()
2022 conn = get(conn, "/api/v1/instance/peers")
2024 assert result = json_response(conn, 200)
2026 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2029 test "put settings", %{conn: conn} do
2030 user = insert(:user)
2034 |> assign(:user, user)
2035 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2037 assert _result = json_response(conn, 200)
2039 user = User.get_cached_by_ap_id(user.ap_id)
2040 assert user.info.settings == %{"programming" => "socks"}
2043 describe "pinned statuses" do
2045 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2047 user = insert(:user)
2048 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2050 [user: user, activity: activity]
2053 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2054 {:ok, _} = CommonAPI.pin(activity.id, user)
2058 |> assign(:user, user)
2059 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2060 |> json_response(200)
2062 id_str = to_string(activity.id)
2064 assert [%{"id" => ^id_str, "pinned" => true}] = result
2067 test "pin status", %{conn: conn, user: user, activity: activity} do
2068 id_str = to_string(activity.id)
2070 assert %{"id" => ^id_str, "pinned" => true} =
2072 |> assign(:user, user)
2073 |> post("/api/v1/statuses/#{activity.id}/pin")
2074 |> json_response(200)
2076 assert [%{"id" => ^id_str, "pinned" => true}] =
2078 |> assign(:user, user)
2079 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2080 |> json_response(200)
2083 test "unpin status", %{conn: conn, user: user, activity: activity} do
2084 {:ok, _} = CommonAPI.pin(activity.id, user)
2086 id_str = to_string(activity.id)
2087 user = refresh_record(user)
2089 assert %{"id" => ^id_str, "pinned" => false} =
2091 |> assign(:user, user)
2092 |> post("/api/v1/statuses/#{activity.id}/unpin")
2093 |> json_response(200)
2097 |> assign(:user, user)
2098 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2099 |> json_response(200)
2102 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2103 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2105 id_str_one = to_string(activity_one.id)
2107 assert %{"id" => ^id_str_one, "pinned" => true} =
2109 |> assign(:user, user)
2110 |> post("/api/v1/statuses/#{id_str_one}/pin")
2111 |> json_response(200)
2113 user = refresh_record(user)
2115 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2117 |> assign(:user, user)
2118 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2119 |> json_response(400)
2122 test "Status rich-media Card", %{conn: conn, user: user} do
2123 Pleroma.Config.put([:rich_media, :enabled], true)
2124 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2128 |> get("/api/v1/statuses/#{activity.id}/card")
2129 |> json_response(200)
2131 assert response == %{
2132 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2133 "provider_name" => "www.imdb.com",
2134 "provider_url" => "http://www.imdb.com",
2135 "title" => "The Rock",
2137 "url" => "http://www.imdb.com/title/tt0117500/",
2138 "description" => nil,
2141 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2142 "title" => "The Rock",
2143 "type" => "video.movie",
2144 "url" => "http://www.imdb.com/title/tt0117500/"
2149 # works with private posts
2151 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2155 |> assign(:user, user)
2156 |> get("/api/v1/statuses/#{activity.id}/card")
2157 |> json_response(200)
2159 assert response_two == response
2161 Pleroma.Config.put([:rich_media, :enabled], false)
2166 user = insert(:user)
2167 for_user = insert(:user)
2170 CommonAPI.post(user, %{
2171 "status" => "heweoo?"
2175 CommonAPI.post(user, %{
2176 "status" => "heweoo!"
2181 |> assign(:user, for_user)
2182 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2184 assert json_response(response1, 200)["bookmarked"] == true
2188 |> assign(:user, for_user)
2189 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2191 assert json_response(response2, 200)["bookmarked"] == true
2195 |> assign(:user, for_user)
2196 |> get("/api/v1/bookmarks")
2198 assert [json_response(response2, 200), json_response(response1, 200)] ==
2199 json_response(bookmarks, 200)
2203 |> assign(:user, for_user)
2204 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2206 assert json_response(response1, 200)["bookmarked"] == false
2210 |> assign(:user, for_user)
2211 |> get("/api/v1/bookmarks")
2213 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2216 describe "conversation muting" do
2218 user = insert(:user)
2219 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2221 [user: user, activity: activity]
2224 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2225 id_str = to_string(activity.id)
2227 assert %{"id" => ^id_str, "muted" => true} =
2229 |> assign(:user, user)
2230 |> post("/api/v1/statuses/#{activity.id}/mute")
2231 |> json_response(200)
2234 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2235 {:ok, _} = CommonAPI.add_mute(user, activity)
2237 id_str = to_string(activity.id)
2238 user = refresh_record(user)
2240 assert %{"id" => ^id_str, "muted" => false} =
2242 |> assign(:user, user)
2243 |> post("/api/v1/statuses/#{activity.id}/unmute")
2244 |> json_response(200)
2248 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2249 user = insert(:user)
2253 |> assign(:user, user)
2254 |> get("/api/v1/pleroma/flavour")
2256 assert "glitch" == json_response(get_old_flavour, 200)
2260 |> assign(:user, user)
2261 |> post("/api/v1/pleroma/flavour/vanilla")
2263 assert "vanilla" == json_response(set_flavour, 200)
2267 |> assign(:user, user)
2268 |> post("/api/v1/pleroma/flavour/vanilla")
2270 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2273 describe "reports" do
2275 reporter = insert(:user)
2276 target_user = insert(:user)
2278 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2280 [reporter: reporter, target_user: target_user, activity: activity]
2283 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2284 assert %{"action_taken" => false, "id" => _} =
2286 |> assign(:user, reporter)
2287 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2288 |> json_response(200)
2291 test "submit a report with statuses and comment", %{
2294 target_user: target_user,
2297 assert %{"action_taken" => false, "id" => _} =
2299 |> assign(:user, reporter)
2300 |> post("/api/v1/reports", %{
2301 "account_id" => target_user.id,
2302 "status_ids" => [activity.id],
2303 "comment" => "bad status!"
2305 |> json_response(200)
2308 test "account_id is required", %{
2313 assert %{"error" => "Valid `account_id` required"} =
2315 |> assign(:user, reporter)
2316 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2317 |> json_response(400)
2320 test "comment must be up to the size specified in the config", %{
2323 target_user: target_user
2325 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2326 comment = String.pad_trailing("a", max_size + 1, "a")
2328 error = %{"error" => "Comment must be up to #{max_size} characters"}
2332 |> assign(:user, reporter)
2333 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2334 |> json_response(400)
2338 describe "link headers" do
2339 test "preserves parameters in link headers", %{conn: conn} do
2340 user = insert(:user)
2341 other_user = insert(:user)
2344 CommonAPI.post(other_user, %{
2345 "status" => "hi @#{user.nickname}",
2346 "visibility" => "public"
2350 CommonAPI.post(other_user, %{
2351 "status" => "hi @#{user.nickname}",
2352 "visibility" => "public"
2355 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2356 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2360 |> assign(:user, user)
2361 |> get("/api/v1/notifications", %{media_only: true})
2363 assert [link_header] = get_resp_header(conn, "link")
2364 assert link_header =~ ~r/media_only=true/
2365 assert link_header =~ ~r/since_id=#{notification2.id}/
2366 assert link_header =~ ~r/max_id=#{notification1.id}/
2370 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2371 # Need to set an old-style integer ID to reproduce the problem
2372 # (these are no longer assigned to new accounts but were preserved
2373 # for existing accounts during the migration to flakeIDs)
2374 user_one = insert(:user, %{id: 1212})
2375 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2379 |> get("/api/v1/accounts/#{user_one.id}")
2383 |> get("/api/v1/accounts/#{user_two.nickname}")
2387 |> get("/api/v1/accounts/#{user_two.id}")
2389 acc_one = json_response(resp_one, 200)
2390 acc_two = json_response(resp_two, 200)
2391 acc_three = json_response(resp_three, 200)
2392 refute acc_one == acc_two
2393 assert acc_two == acc_three
2396 describe "custom emoji" do
2397 test "with tags", %{conn: conn} do
2400 |> get("/api/v1/custom_emojis")
2401 |> json_response(200)
2403 assert Map.has_key?(emoji, "shortcode")
2404 assert Map.has_key?(emoji, "static_url")
2405 assert Map.has_key?(emoji, "tags")
2406 assert is_list(emoji["tags"])
2407 assert Map.has_key?(emoji, "url")
2408 assert Map.has_key?(emoji, "visible_in_picker")
2412 describe "index/2 redirections" do
2413 setup %{conn: conn} do
2417 signing_salt: "cooldude"
2422 |> Plug.Session.call(Plug.Session.init(session_opts))
2425 test_path = "/web/statuses/test"
2426 %{conn: conn, path: test_path}
2429 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2430 conn = get(conn, path)
2432 assert conn.status == 302
2433 assert redirected_to(conn) == "/web/login"
2436 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2437 token = insert(:oauth_token)
2441 |> assign(:user, token.user)
2442 |> put_session(:oauth_token, token.token)
2445 assert conn.status == 200
2448 test "saves referer path to session", %{conn: conn, path: path} do
2449 conn = get(conn, path)
2450 return_to = Plug.Conn.get_session(conn, :return_to)
2452 assert return_to == path
2455 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2456 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2457 auth = insert(:oauth_authorization, app: app)
2461 |> put_session(:return_to, path)
2462 |> get("/web/login", %{code: auth.token})
2464 assert conn.status == 302
2465 assert redirected_to(conn) == path
2468 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2469 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2470 auth = insert(:oauth_authorization, app: app)
2472 conn = get(conn, "/web/login", %{code: auth.token})
2474 assert conn.status == 302
2475 assert redirected_to(conn) == "/web/getting-started"
2479 describe "scheduled activities" do
2480 test "creates a scheduled activity", %{conn: conn} do
2481 user = insert(:user)
2482 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2486 |> assign(:user, user)
2487 |> post("/api/v1/statuses", %{
2488 "status" => "scheduled",
2489 "scheduled_at" => scheduled_at
2492 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2493 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2494 assert [] == Repo.all(Activity)
2497 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2498 user = insert(:user)
2499 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2501 file = %Plug.Upload{
2502 content_type: "image/jpg",
2503 path: Path.absname("test/fixtures/image.jpg"),
2504 filename: "an_image.jpg"
2507 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
2511 |> assign(:user, user)
2512 |> post("/api/v1/statuses", %{
2513 "media_ids" => [to_string(upload.id)],
2514 "status" => "scheduled",
2515 "scheduled_at" => scheduled_at
2518 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
2519 assert %{"type" => "image"} = media_attachment
2522 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
2524 user = insert(:user)
2527 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
2531 |> assign(:user, user)
2532 |> post("/api/v1/statuses", %{
2533 "status" => "not scheduled",
2534 "scheduled_at" => scheduled_at
2537 assert %{"content" => "not scheduled"} = json_response(conn, 200)
2538 assert [] == Repo.all(ScheduledActivity)
2541 test "returns error when daily user limit is exceeded", %{conn: conn} do
2542 user = insert(:user)
2545 NaiveDateTime.utc_now()
2546 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2547 |> NaiveDateTime.to_iso8601()
2549 attrs = %{params: %{}, scheduled_at: today}
2550 {:ok, _} = ScheduledActivity.create(user, attrs)
2551 {:ok, _} = ScheduledActivity.create(user, attrs)
2555 |> assign(:user, user)
2556 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
2558 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
2561 test "returns error when total user limit is exceeded", %{conn: conn} do
2562 user = insert(:user)
2565 NaiveDateTime.utc_now()
2566 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2567 |> NaiveDateTime.to_iso8601()
2570 NaiveDateTime.utc_now()
2571 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
2572 |> NaiveDateTime.to_iso8601()
2574 attrs = %{params: %{}, scheduled_at: today}
2575 {:ok, _} = ScheduledActivity.create(user, attrs)
2576 {:ok, _} = ScheduledActivity.create(user, attrs)
2577 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
2581 |> assign(:user, user)
2582 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
2584 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
2587 test "shows scheduled activities", %{conn: conn} do
2588 user = insert(:user)
2589 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
2590 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
2591 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
2592 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
2596 |> assign(:user, user)
2601 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
2603 result = json_response(conn_res, 200)
2604 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2609 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
2611 result = json_response(conn_res, 200)
2612 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
2617 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
2619 result = json_response(conn_res, 200)
2620 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2623 test "shows a scheduled activity", %{conn: conn} do
2624 user = insert(:user)
2625 scheduled_activity = insert(:scheduled_activity, user: user)
2629 |> assign(:user, user)
2630 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2632 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
2633 assert scheduled_activity_id == scheduled_activity.id |> to_string()
2637 |> assign(:user, user)
2638 |> get("/api/v1/scheduled_statuses/404")
2640 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2643 test "updates a scheduled activity", %{conn: conn} do
2644 user = insert(:user)
2645 scheduled_activity = insert(:scheduled_activity, user: user)
2648 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2652 |> assign(:user, user)
2653 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
2654 scheduled_at: new_scheduled_at
2657 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
2658 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
2662 |> assign(:user, user)
2663 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
2665 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2668 test "deletes a scheduled activity", %{conn: conn} do
2669 user = insert(:user)
2670 scheduled_activity = insert(:scheduled_activity, user: user)
2674 |> assign(:user, user)
2675 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2677 assert %{} = json_response(res_conn, 200)
2678 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
2682 |> assign(:user, user)
2683 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2685 assert %{"error" => "Record not found"} = json_response(res_conn, 404)