1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.Notification
13 alias Pleroma.ScheduledActivity
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.MastodonAPI.FilterView
18 alias Pleroma.Web.OAuth.App
19 alias Pleroma.Web.OAuth.Token
20 alias Pleroma.Web.OStatus
21 alias Pleroma.Web.Push
22 alias Pleroma.Web.TwitterAPI.TwitterAPI
23 import Pleroma.Factory
24 import ExUnit.CaptureLog
28 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
32 test "the home timeline", %{conn: conn} do
34 following = insert(:user)
36 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
40 |> assign(:user, user)
41 |> get("/api/v1/timelines/home")
43 assert Enum.empty?(json_response(conn, 200))
45 {:ok, user} = User.follow(user, following)
49 |> assign(:user, user)
50 |> get("/api/v1/timelines/home")
52 assert [%{"content" => "test"}] = json_response(conn, 200)
55 test "the public timeline", %{conn: conn} do
56 following = insert(:user)
59 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
62 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
66 |> get("/api/v1/timelines/public", %{"local" => "False"})
68 assert length(json_response(conn, 200)) == 2
72 |> get("/api/v1/timelines/public", %{"local" => "True"})
74 assert [%{"content" => "test"}] = json_response(conn, 200)
78 |> get("/api/v1/timelines/public", %{"local" => "1"})
80 assert [%{"content" => "test"}] = json_response(conn, 200)
84 test "the public timeline when public is set to false", %{conn: conn} do
85 public = Pleroma.Config.get([:instance, :public])
86 Pleroma.Config.put([:instance, :public], false)
89 Pleroma.Config.put([:instance, :public], public)
93 |> get("/api/v1/timelines/public", %{"local" => "False"})
94 |> json_response(403) == %{"error" => "This resource requires authentication."}
97 test "posting a status", %{conn: conn} do
100 idempotency_key = "Pikachu rocks!"
104 |> assign(:user, user)
105 |> put_req_header("idempotency-key", idempotency_key)
106 |> post("/api/v1/statuses", %{
108 "spoiler_text" => "2hu",
109 "sensitive" => "false"
112 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
114 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
116 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
117 json_response(conn_one, 200)
119 assert Activity.get_by_id(id)
123 |> assign(:user, user)
124 |> put_req_header("idempotency-key", idempotency_key)
125 |> post("/api/v1/statuses", %{
127 "spoiler_text" => "2hu",
128 "sensitive" => "false"
131 assert %{"id" => second_id} = json_response(conn_two, 200)
133 assert id == second_id
137 |> assign(:user, user)
138 |> post("/api/v1/statuses", %{
140 "spoiler_text" => "2hu",
141 "sensitive" => "false"
144 assert %{"id" => third_id} = json_response(conn_three, 200)
146 refute id == third_id
149 test "posting a sensitive status", %{conn: conn} do
154 |> assign(:user, user)
155 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
157 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
158 assert Activity.get_by_id(id)
161 test "posting a fake status", %{conn: conn} do
166 |> assign(:user, user)
167 |> post("/api/v1/statuses", %{
169 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
172 real_status = json_response(real_conn, 200)
175 assert Object.get_by_ap_id(real_status["uri"])
179 |> Map.put("id", nil)
180 |> Map.put("url", nil)
181 |> Map.put("uri", nil)
182 |> Map.put("created_at", nil)
183 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
187 |> assign(:user, user)
188 |> post("/api/v1/statuses", %{
190 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
194 fake_status = json_response(fake_conn, 200)
197 refute Object.get_by_ap_id(fake_status["uri"])
201 |> Map.put("id", nil)
202 |> Map.put("url", nil)
203 |> Map.put("uri", nil)
204 |> Map.put("created_at", nil)
205 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
207 assert real_status == fake_status
210 test "posting a status with OGP link preview", %{conn: conn} do
211 Pleroma.Config.put([:rich_media, :enabled], true)
216 |> assign(:user, user)
217 |> post("/api/v1/statuses", %{
218 "status" => "http://example.com/ogp"
221 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
222 assert Activity.get_by_id(id)
223 Pleroma.Config.put([:rich_media, :enabled], false)
226 test "posting a direct status", %{conn: conn} do
227 user1 = insert(:user)
228 user2 = insert(:user)
229 content = "direct cofe @#{user2.nickname}"
233 |> assign(:user, user1)
234 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
236 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
237 assert activity = Activity.get_by_id(id)
238 assert activity.recipients == [user2.ap_id, user1.ap_id]
239 assert activity.data["to"] == [user2.ap_id]
240 assert activity.data["cc"] == []
243 test "direct timeline", %{conn: conn} do
244 user_one = insert(:user)
245 user_two = insert(:user)
247 {:ok, user_two} = User.follow(user_two, user_one)
250 CommonAPI.post(user_one, %{
251 "status" => "Hi @#{user_two.nickname}!",
252 "visibility" => "direct"
255 {:ok, _follower_only} =
256 CommonAPI.post(user_one, %{
257 "status" => "Hi @#{user_two.nickname}!",
258 "visibility" => "private"
261 # Only direct should be visible here
264 |> assign(:user, user_two)
265 |> get("api/v1/timelines/direct")
267 [status] = json_response(res_conn, 200)
269 assert %{"visibility" => "direct"} = status
270 assert status["url"] != direct.data["id"]
272 # User should be able to see his own direct message
275 |> assign(:user, user_one)
276 |> get("api/v1/timelines/direct")
278 [status] = json_response(res_conn, 200)
280 assert %{"visibility" => "direct"} = status
282 # Both should be visible here
285 |> assign(:user, user_two)
286 |> get("api/v1/timelines/home")
288 [_s1, _s2] = json_response(res_conn, 200)
291 Enum.each(1..20, fn _ ->
293 CommonAPI.post(user_one, %{
294 "status" => "Hi @#{user_two.nickname}!",
295 "visibility" => "direct"
301 |> assign(:user, user_two)
302 |> get("api/v1/timelines/direct")
304 statuses = json_response(res_conn, 200)
305 assert length(statuses) == 20
309 |> assign(:user, user_two)
310 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
312 [status] = json_response(res_conn, 200)
314 assert status["url"] != direct.data["id"]
317 test "Conversations", %{conn: conn} do
318 user_one = insert(:user)
319 user_two = insert(:user)
321 {:ok, user_two} = User.follow(user_two, user_one)
324 CommonAPI.post(user_one, %{
325 "status" => "Hi @#{user_two.nickname}!",
326 "visibility" => "direct"
329 {:ok, _follower_only} =
330 CommonAPI.post(user_one, %{
331 "status" => "Hi @#{user_two.nickname}!",
332 "visibility" => "private"
337 |> assign(:user, user_one)
338 |> get("/api/v1/conversations")
340 assert response = json_response(res_conn, 200)
345 "accounts" => res_accounts,
346 "last_status" => res_last_status,
351 assert length(res_accounts) == 2
352 assert is_binary(res_id)
353 assert unread == true
354 assert res_last_status["id"] == direct.id
356 # Apparently undocumented API endpoint
359 |> assign(:user, user_one)
360 |> post("/api/v1/conversations/#{res_id}/read")
362 assert response = json_response(res_conn, 200)
363 assert length(response["accounts"]) == 2
364 assert response["last_status"]["id"] == direct.id
365 assert response["unread"] == false
367 # (vanilla) Mastodon frontend behaviour
370 |> assign(:user, user_one)
371 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
373 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
376 test "doesn't include DMs from blocked users", %{conn: conn} do
377 blocker = insert(:user)
378 blocked = insert(:user)
380 {:ok, blocker} = User.block(blocker, blocked)
382 {:ok, _blocked_direct} =
383 CommonAPI.post(blocked, %{
384 "status" => "Hi @#{blocker.nickname}!",
385 "visibility" => "direct"
389 CommonAPI.post(user, %{
390 "status" => "Hi @#{blocker.nickname}!",
391 "visibility" => "direct"
396 |> assign(:user, user)
397 |> get("api/v1/timelines/direct")
399 [status] = json_response(res_conn, 200)
400 assert status["id"] == direct.id
403 test "replying to a status", %{conn: conn} do
406 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
410 |> assign(:user, user)
411 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
413 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
415 activity = Activity.get_by_id(id)
417 assert activity.data["context"] == replied_to.data["context"]
418 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
421 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
426 |> assign(:user, user)
427 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
429 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
431 activity = Activity.get_by_id(id)
436 test "verify_credentials", %{conn: conn} do
441 |> assign(:user, user)
442 |> get("/api/v1/accounts/verify_credentials")
444 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
445 assert id == to_string(user.id)
448 test "verify_credentials default scope unlisted", %{conn: conn} do
449 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
453 |> assign(:user, user)
454 |> get("/api/v1/accounts/verify_credentials")
456 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
457 assert id == to_string(user.id)
460 test "apps/verify_credentials", %{conn: conn} do
461 token = insert(:oauth_token)
465 |> assign(:user, token.user)
466 |> assign(:token, token)
467 |> get("/api/v1/apps/verify_credentials")
469 app = Repo.preload(token, :app).app
472 "name" => app.client_name,
473 "website" => app.website,
474 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
477 assert expected == json_response(conn, 200)
480 test "creates an oauth app", %{conn: conn} do
482 app_attrs = build(:oauth_app)
486 |> assign(:user, user)
487 |> post("/api/v1/apps", %{
488 client_name: app_attrs.client_name,
489 redirect_uris: app_attrs.redirect_uris
492 [app] = Repo.all(App)
495 "name" => app.client_name,
496 "website" => app.website,
497 "client_id" => app.client_id,
498 "client_secret" => app.client_secret,
499 "id" => app.id |> to_string(),
500 "redirect_uri" => app.redirect_uris,
501 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
504 assert expected == json_response(conn, 200)
507 test "get a status", %{conn: conn} do
508 activity = insert(:note_activity)
512 |> get("/api/v1/statuses/#{activity.id}")
514 assert %{"id" => id} = json_response(conn, 200)
515 assert id == to_string(activity.id)
518 describe "deleting a status" do
519 test "when you created it", %{conn: conn} do
520 activity = insert(:note_activity)
521 author = User.get_cached_by_ap_id(activity.data["actor"])
525 |> assign(:user, author)
526 |> delete("/api/v1/statuses/#{activity.id}")
528 assert %{} = json_response(conn, 200)
530 refute Activity.get_by_id(activity.id)
533 test "when you didn't create it", %{conn: conn} do
534 activity = insert(:note_activity)
539 |> assign(:user, user)
540 |> delete("/api/v1/statuses/#{activity.id}")
542 assert %{"error" => _} = json_response(conn, 403)
544 assert Activity.get_by_id(activity.id) == activity
547 test "when you're an admin or moderator", %{conn: conn} do
548 activity1 = insert(:note_activity)
549 activity2 = insert(:note_activity)
550 admin = insert(:user, info: %{is_admin: true})
551 moderator = insert(:user, info: %{is_moderator: true})
555 |> assign(:user, admin)
556 |> delete("/api/v1/statuses/#{activity1.id}")
558 assert %{} = json_response(res_conn, 200)
562 |> assign(:user, moderator)
563 |> delete("/api/v1/statuses/#{activity2.id}")
565 assert %{} = json_response(res_conn, 200)
567 refute Activity.get_by_id(activity1.id)
568 refute Activity.get_by_id(activity2.id)
572 describe "filters" do
573 test "creating a filter", %{conn: conn} do
576 filter = %Pleroma.Filter{
583 |> assign(:user, user)
584 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
586 assert response = json_response(conn, 200)
587 assert response["phrase"] == filter.phrase
588 assert response["context"] == filter.context
589 assert response["irreversible"] == false
590 assert response["id"] != nil
591 assert response["id"] != ""
594 test "fetching a list of filters", %{conn: conn} do
597 query_one = %Pleroma.Filter{
604 query_two = %Pleroma.Filter{
611 {:ok, filter_one} = Pleroma.Filter.create(query_one)
612 {:ok, filter_two} = Pleroma.Filter.create(query_two)
616 |> assign(:user, user)
617 |> get("/api/v1/filters")
618 |> json_response(200)
624 filters: [filter_two, filter_one]
628 test "get a filter", %{conn: conn} do
631 query = %Pleroma.Filter{
638 {:ok, filter} = Pleroma.Filter.create(query)
642 |> assign(:user, user)
643 |> get("/api/v1/filters/#{filter.filter_id}")
645 assert _response = json_response(conn, 200)
648 test "update a filter", %{conn: conn} do
651 query = %Pleroma.Filter{
658 {:ok, _filter} = Pleroma.Filter.create(query)
660 new = %Pleroma.Filter{
667 |> assign(:user, user)
668 |> put("/api/v1/filters/#{query.filter_id}", %{
673 assert response = json_response(conn, 200)
674 assert response["phrase"] == new.phrase
675 assert response["context"] == new.context
678 test "delete a filter", %{conn: conn} do
681 query = %Pleroma.Filter{
688 {:ok, filter} = Pleroma.Filter.create(query)
692 |> assign(:user, user)
693 |> delete("/api/v1/filters/#{filter.filter_id}")
695 assert response = json_response(conn, 200)
696 assert response == %{}
701 test "creating a list", %{conn: conn} do
706 |> assign(:user, user)
707 |> post("/api/v1/lists", %{"title" => "cuties"})
709 assert %{"title" => title} = json_response(conn, 200)
710 assert title == "cuties"
713 test "adding users to a list", %{conn: conn} do
715 other_user = insert(:user)
716 {:ok, list} = Pleroma.List.create("name", user)
720 |> assign(:user, user)
721 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
723 assert %{} == json_response(conn, 200)
724 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
725 assert following == [other_user.follower_address]
728 test "removing users from a list", %{conn: conn} do
730 other_user = insert(:user)
731 third_user = insert(:user)
732 {:ok, list} = Pleroma.List.create("name", user)
733 {:ok, list} = Pleroma.List.follow(list, other_user)
734 {:ok, list} = Pleroma.List.follow(list, third_user)
738 |> assign(:user, user)
739 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
741 assert %{} == json_response(conn, 200)
742 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
743 assert following == [third_user.follower_address]
746 test "listing users in a list", %{conn: conn} do
748 other_user = insert(:user)
749 {:ok, list} = Pleroma.List.create("name", user)
750 {:ok, list} = Pleroma.List.follow(list, other_user)
754 |> assign(:user, user)
755 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
757 assert [%{"id" => id}] = json_response(conn, 200)
758 assert id == to_string(other_user.id)
761 test "retrieving a list", %{conn: conn} do
763 {:ok, list} = Pleroma.List.create("name", user)
767 |> assign(:user, user)
768 |> get("/api/v1/lists/#{list.id}")
770 assert %{"id" => id} = json_response(conn, 200)
771 assert id == to_string(list.id)
774 test "renaming a list", %{conn: conn} do
776 {:ok, list} = Pleroma.List.create("name", user)
780 |> assign(:user, user)
781 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
783 assert %{"title" => name} = json_response(conn, 200)
784 assert name == "newname"
787 test "deleting a list", %{conn: conn} do
789 {:ok, list} = Pleroma.List.create("name", user)
793 |> assign(:user, user)
794 |> delete("/api/v1/lists/#{list.id}")
796 assert %{} = json_response(conn, 200)
797 assert is_nil(Repo.get(Pleroma.List, list.id))
800 test "list timeline", %{conn: conn} do
802 other_user = insert(:user)
803 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
804 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
805 {:ok, list} = Pleroma.List.create("name", user)
806 {:ok, list} = Pleroma.List.follow(list, other_user)
810 |> assign(:user, user)
811 |> get("/api/v1/timelines/list/#{list.id}")
813 assert [%{"id" => id}] = json_response(conn, 200)
815 assert id == to_string(activity_two.id)
818 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
820 other_user = insert(:user)
821 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
823 {:ok, _activity_two} =
824 TwitterAPI.create_status(other_user, %{
825 "status" => "Marisa is cute.",
826 "visibility" => "private"
829 {:ok, list} = Pleroma.List.create("name", user)
830 {:ok, list} = Pleroma.List.follow(list, other_user)
834 |> assign(:user, user)
835 |> get("/api/v1/timelines/list/#{list.id}")
837 assert [%{"id" => id}] = json_response(conn, 200)
839 assert id == to_string(activity_one.id)
843 describe "notifications" do
844 test "list of notifications", %{conn: conn} do
846 other_user = insert(:user)
849 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
851 {:ok, [_notification]} = Notification.create_notifications(activity)
855 |> assign(:user, user)
856 |> get("/api/v1/notifications")
859 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
861 }\">@<span>#{user.nickname}</span></a></span>"
863 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
864 assert response == expected_response
867 test "getting a single notification", %{conn: conn} do
869 other_user = insert(:user)
872 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
874 {:ok, [notification]} = Notification.create_notifications(activity)
878 |> assign(:user, user)
879 |> get("/api/v1/notifications/#{notification.id}")
882 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
884 }\">@<span>#{user.nickname}</span></a></span>"
886 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
887 assert response == expected_response
890 test "dismissing a single notification", %{conn: conn} do
892 other_user = insert(:user)
895 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
897 {:ok, [notification]} = Notification.create_notifications(activity)
901 |> assign(:user, user)
902 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
904 assert %{} = json_response(conn, 200)
907 test "clearing all notifications", %{conn: conn} do
909 other_user = insert(:user)
912 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
914 {:ok, [_notification]} = Notification.create_notifications(activity)
918 |> assign(:user, user)
919 |> post("/api/v1/notifications/clear")
921 assert %{} = json_response(conn, 200)
925 |> assign(:user, user)
926 |> get("/api/v1/notifications")
928 assert all = json_response(conn, 200)
932 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
934 other_user = insert(:user)
936 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
937 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
938 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
939 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
941 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
942 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
943 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
944 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
948 |> assign(:user, user)
953 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
955 result = json_response(conn_res, 200)
956 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
961 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
963 result = json_response(conn_res, 200)
964 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
969 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
971 result = json_response(conn_res, 200)
972 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
975 test "filters notifications using exclude_types", %{conn: conn} do
977 other_user = insert(:user)
979 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
980 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
981 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
982 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
983 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
985 mention_notification_id =
986 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
988 favorite_notification_id =
989 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
991 reblog_notification_id =
992 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
994 follow_notification_id =
995 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
999 |> assign(:user, user)
1002 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1004 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1007 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1009 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1012 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1014 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1017 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1019 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1022 test "destroy multiple", %{conn: conn} do
1023 user = insert(:user)
1024 other_user = insert(:user)
1026 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1027 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1028 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1029 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1031 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1032 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1033 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1034 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1038 |> assign(:user, user)
1042 |> get("/api/v1/notifications")
1044 result = json_response(conn_res, 200)
1045 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1049 |> assign(:user, other_user)
1053 |> get("/api/v1/notifications")
1055 result = json_response(conn_res, 200)
1056 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1060 |> delete("/api/v1/notifications/destroy_multiple", %{
1061 "ids" => [notification1_id, notification2_id]
1064 assert json_response(conn_destroy, 200) == %{}
1068 |> get("/api/v1/notifications")
1070 result = json_response(conn_res, 200)
1071 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1075 describe "reblogging" do
1076 test "reblogs and returns the reblogged status", %{conn: conn} do
1077 activity = insert(:note_activity)
1078 user = insert(:user)
1082 |> assign(:user, user)
1083 |> post("/api/v1/statuses/#{activity.id}/reblog")
1086 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1088 } = json_response(conn, 200)
1090 assert to_string(activity.id) == id
1093 test "reblogged status for another user", %{conn: conn} do
1094 activity = insert(:note_activity)
1095 user1 = insert(:user)
1096 user2 = insert(:user)
1097 user3 = insert(:user)
1098 CommonAPI.favorite(activity.id, user2)
1099 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1100 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1101 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1105 |> assign(:user, user3)
1106 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1109 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1110 "reblogged" => false,
1111 "favourited" => false,
1112 "bookmarked" => false
1113 } = json_response(conn_res, 200)
1117 |> assign(:user, user2)
1118 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1121 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1122 "reblogged" => true,
1123 "favourited" => true,
1124 "bookmarked" => true
1125 } = json_response(conn_res, 200)
1127 assert to_string(activity.id) == id
1131 describe "unreblogging" do
1132 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1133 activity = insert(:note_activity)
1134 user = insert(:user)
1136 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1140 |> assign(:user, user)
1141 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1143 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1145 assert to_string(activity.id) == id
1149 describe "favoriting" do
1150 test "favs a status and returns it", %{conn: conn} do
1151 activity = insert(:note_activity)
1152 user = insert(:user)
1156 |> assign(:user, user)
1157 |> post("/api/v1/statuses/#{activity.id}/favourite")
1159 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1160 json_response(conn, 200)
1162 assert to_string(activity.id) == id
1165 test "returns 500 for a wrong id", %{conn: conn} do
1166 user = insert(:user)
1170 |> assign(:user, user)
1171 |> post("/api/v1/statuses/1/favourite")
1172 |> json_response(500)
1174 assert resp == "Something went wrong"
1178 describe "unfavoriting" do
1179 test "unfavorites a status and returns it", %{conn: conn} do
1180 activity = insert(:note_activity)
1181 user = insert(:user)
1183 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1187 |> assign(:user, user)
1188 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1190 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1191 json_response(conn, 200)
1193 assert to_string(activity.id) == id
1197 describe "user timelines" do
1198 test "gets a users statuses", %{conn: conn} do
1199 user_one = insert(:user)
1200 user_two = insert(:user)
1201 user_three = insert(:user)
1203 {:ok, user_three} = User.follow(user_three, user_one)
1205 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1207 {:ok, direct_activity} =
1208 CommonAPI.post(user_one, %{
1209 "status" => "Hi, @#{user_two.nickname}.",
1210 "visibility" => "direct"
1213 {:ok, private_activity} =
1214 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1218 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1220 assert [%{"id" => id}] = json_response(resp, 200)
1221 assert id == to_string(activity.id)
1225 |> assign(:user, user_two)
1226 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1228 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1229 assert id_one == to_string(direct_activity.id)
1230 assert id_two == to_string(activity.id)
1234 |> assign(:user, user_three)
1235 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1237 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1238 assert id_one == to_string(private_activity.id)
1239 assert id_two == to_string(activity.id)
1242 test "unimplemented pinned statuses feature", %{conn: conn} do
1243 note = insert(:note_activity)
1244 user = User.get_cached_by_ap_id(note.data["actor"])
1248 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1250 assert json_response(conn, 200) == []
1253 test "gets an users media", %{conn: conn} do
1254 note = insert(:note_activity)
1255 user = User.get_cached_by_ap_id(note.data["actor"])
1257 file = %Plug.Upload{
1258 content_type: "image/jpg",
1259 path: Path.absname("test/fixtures/image.jpg"),
1260 filename: "an_image.jpg"
1264 TwitterAPI.upload(file, user, "json")
1268 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1272 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1274 assert [%{"id" => id}] = json_response(conn, 200)
1275 assert id == to_string(image_post.id)
1279 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1281 assert [%{"id" => id}] = json_response(conn, 200)
1282 assert id == to_string(image_post.id)
1285 test "gets a user's statuses without reblogs", %{conn: conn} do
1286 user = insert(:user)
1287 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1288 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1292 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1294 assert [%{"id" => id}] = json_response(conn, 200)
1295 assert id == to_string(post.id)
1299 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1301 assert [%{"id" => id}] = json_response(conn, 200)
1302 assert id == to_string(post.id)
1306 describe "user relationships" do
1307 test "returns the relationships for the current user", %{conn: conn} do
1308 user = insert(:user)
1309 other_user = insert(:user)
1310 {:ok, user} = User.follow(user, other_user)
1314 |> assign(:user, user)
1315 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1317 assert [relationship] = json_response(conn, 200)
1319 assert to_string(other_user.id) == relationship["id"]
1323 describe "locked accounts" do
1324 test "/api/v1/follow_requests works" do
1325 user = insert(:user, %{info: %User.Info{locked: true}})
1326 other_user = insert(:user)
1328 {:ok, _activity} = ActivityPub.follow(other_user, user)
1330 user = User.get_cached_by_id(user.id)
1331 other_user = User.get_cached_by_id(other_user.id)
1333 assert User.following?(other_user, user) == false
1337 |> assign(:user, user)
1338 |> get("/api/v1/follow_requests")
1340 assert [relationship] = json_response(conn, 200)
1341 assert to_string(other_user.id) == relationship["id"]
1344 test "/api/v1/follow_requests/:id/authorize works" do
1345 user = insert(:user, %{info: %User.Info{locked: true}})
1346 other_user = insert(:user)
1348 {:ok, _activity} = ActivityPub.follow(other_user, user)
1350 user = User.get_cached_by_id(user.id)
1351 other_user = User.get_cached_by_id(other_user.id)
1353 assert User.following?(other_user, user) == false
1357 |> assign(:user, user)
1358 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1360 assert relationship = json_response(conn, 200)
1361 assert to_string(other_user.id) == relationship["id"]
1363 user = User.get_cached_by_id(user.id)
1364 other_user = User.get_cached_by_id(other_user.id)
1366 assert User.following?(other_user, user) == true
1369 test "verify_credentials", %{conn: conn} do
1370 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1374 |> assign(:user, user)
1375 |> get("/api/v1/accounts/verify_credentials")
1377 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1378 assert id == to_string(user.id)
1381 test "/api/v1/follow_requests/:id/reject works" do
1382 user = insert(:user, %{info: %User.Info{locked: true}})
1383 other_user = insert(:user)
1385 {:ok, _activity} = ActivityPub.follow(other_user, user)
1387 user = User.get_cached_by_id(user.id)
1391 |> assign(:user, user)
1392 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1394 assert relationship = json_response(conn, 200)
1395 assert to_string(other_user.id) == relationship["id"]
1397 user = User.get_cached_by_id(user.id)
1398 other_user = User.get_cached_by_id(other_user.id)
1400 assert User.following?(other_user, user) == false
1404 test "account fetching", %{conn: conn} do
1405 user = insert(:user)
1409 |> get("/api/v1/accounts/#{user.id}")
1411 assert %{"id" => id} = json_response(conn, 200)
1412 assert id == to_string(user.id)
1416 |> get("/api/v1/accounts/-1")
1418 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1421 test "account fetching also works nickname", %{conn: conn} do
1422 user = insert(:user)
1426 |> get("/api/v1/accounts/#{user.nickname}")
1428 assert %{"id" => id} = json_response(conn, 200)
1429 assert id == user.id
1432 test "media upload", %{conn: conn} do
1433 file = %Plug.Upload{
1434 content_type: "image/jpg",
1435 path: Path.absname("test/fixtures/image.jpg"),
1436 filename: "an_image.jpg"
1439 desc = "Description of the image"
1441 user = insert(:user)
1445 |> assign(:user, user)
1446 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1448 assert media = json_response(conn, 200)
1450 assert media["type"] == "image"
1451 assert media["description"] == desc
1454 object = Repo.get(Object, media["id"])
1455 assert object.data["actor"] == User.ap_id(user)
1458 test "mascot upload", %{conn: conn} do
1459 user = insert(:user)
1461 non_image_file = %Plug.Upload{
1462 content_type: "audio/mpeg",
1463 path: Path.absname("test/fixtures/sound.mp3"),
1464 filename: "sound.mp3"
1469 |> assign(:user, user)
1470 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1472 assert json_response(conn, 415)
1474 file = %Plug.Upload{
1475 content_type: "image/jpg",
1476 path: Path.absname("test/fixtures/image.jpg"),
1477 filename: "an_image.jpg"
1482 |> assign(:user, user)
1483 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1485 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1488 test "mascot retrieving", %{conn: conn} do
1489 user = insert(:user)
1490 # When user hasn't set a mascot, we should just get pleroma tan back
1493 |> assign(:user, user)
1494 |> get("/api/v1/pleroma/mascot")
1496 assert %{"url" => url} = json_response(conn, 200)
1497 assert url =~ "pleroma-fox-tan-smol"
1499 # When a user sets their mascot, we should get that back
1500 file = %Plug.Upload{
1501 content_type: "image/jpg",
1502 path: Path.absname("test/fixtures/image.jpg"),
1503 filename: "an_image.jpg"
1508 |> assign(:user, user)
1509 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1511 assert json_response(conn, 200)
1513 user = User.get_cached_by_id(user.id)
1517 |> assign(:user, user)
1518 |> get("/api/v1/pleroma/mascot")
1520 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1521 assert url =~ "an_image"
1524 test "hashtag timeline", %{conn: conn} do
1525 following = insert(:user)
1528 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1530 {:ok, [_activity]} =
1531 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1535 |> get("/api/v1/timelines/tag/2hu")
1537 assert [%{"id" => id}] = json_response(nconn, 200)
1539 assert id == to_string(activity.id)
1541 # works for different capitalization too
1544 |> get("/api/v1/timelines/tag/2HU")
1546 assert [%{"id" => id}] = json_response(nconn, 200)
1548 assert id == to_string(activity.id)
1552 test "multi-hashtag timeline", %{conn: conn} do
1553 user = insert(:user)
1555 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1556 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1557 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1561 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1563 [status_none, status_test1, status_test] = json_response(any_test, 200)
1565 assert to_string(activity_test.id) == status_test["id"]
1566 assert to_string(activity_test1.id) == status_test1["id"]
1567 assert to_string(activity_none.id) == status_none["id"]
1571 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1573 assert [status_test1] == json_response(restricted_test, 200)
1575 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1577 assert [status_none] == json_response(all_test, 200)
1580 test "getting followers", %{conn: conn} do
1581 user = insert(:user)
1582 other_user = insert(:user)
1583 {:ok, user} = User.follow(user, other_user)
1587 |> get("/api/v1/accounts/#{other_user.id}/followers")
1589 assert [%{"id" => id}] = json_response(conn, 200)
1590 assert id == to_string(user.id)
1593 test "getting followers, hide_followers", %{conn: conn} do
1594 user = insert(:user)
1595 other_user = insert(:user, %{info: %{hide_followers: true}})
1596 {:ok, _user} = User.follow(user, other_user)
1600 |> get("/api/v1/accounts/#{other_user.id}/followers")
1602 assert [] == json_response(conn, 200)
1605 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1606 user = insert(:user)
1607 other_user = insert(:user, %{info: %{hide_followers: true}})
1608 {:ok, _user} = User.follow(user, other_user)
1612 |> assign(:user, other_user)
1613 |> get("/api/v1/accounts/#{other_user.id}/followers")
1615 refute [] == json_response(conn, 200)
1618 test "getting followers, pagination", %{conn: conn} do
1619 user = insert(:user)
1620 follower1 = insert(:user)
1621 follower2 = insert(:user)
1622 follower3 = insert(:user)
1623 {:ok, _} = User.follow(follower1, user)
1624 {:ok, _} = User.follow(follower2, user)
1625 {:ok, _} = User.follow(follower3, user)
1629 |> assign(:user, user)
1633 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1635 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1636 assert id3 == follower3.id
1637 assert id2 == follower2.id
1641 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1643 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1644 assert id2 == follower2.id
1645 assert id1 == follower1.id
1649 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1651 assert [%{"id" => id2}] = json_response(res_conn, 200)
1652 assert id2 == follower2.id
1654 assert [link_header] = get_resp_header(res_conn, "link")
1655 assert link_header =~ ~r/min_id=#{follower2.id}/
1656 assert link_header =~ ~r/max_id=#{follower2.id}/
1659 test "getting following", %{conn: conn} do
1660 user = insert(:user)
1661 other_user = insert(:user)
1662 {:ok, user} = User.follow(user, other_user)
1666 |> get("/api/v1/accounts/#{user.id}/following")
1668 assert [%{"id" => id}] = json_response(conn, 200)
1669 assert id == to_string(other_user.id)
1672 test "getting following, hide_follows", %{conn: conn} do
1673 user = insert(:user, %{info: %{hide_follows: true}})
1674 other_user = insert(:user)
1675 {:ok, user} = User.follow(user, other_user)
1679 |> get("/api/v1/accounts/#{user.id}/following")
1681 assert [] == json_response(conn, 200)
1684 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1685 user = insert(:user, %{info: %{hide_follows: true}})
1686 other_user = insert(:user)
1687 {:ok, user} = User.follow(user, other_user)
1691 |> assign(:user, user)
1692 |> get("/api/v1/accounts/#{user.id}/following")
1694 refute [] == json_response(conn, 200)
1697 test "getting following, pagination", %{conn: conn} do
1698 user = insert(:user)
1699 following1 = insert(:user)
1700 following2 = insert(:user)
1701 following3 = insert(:user)
1702 {:ok, _} = User.follow(user, following1)
1703 {:ok, _} = User.follow(user, following2)
1704 {:ok, _} = User.follow(user, following3)
1708 |> assign(:user, user)
1712 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1714 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1715 assert id3 == following3.id
1716 assert id2 == following2.id
1720 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1722 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1723 assert id2 == following2.id
1724 assert id1 == following1.id
1728 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1730 assert [%{"id" => id2}] = json_response(res_conn, 200)
1731 assert id2 == following2.id
1733 assert [link_header] = get_resp_header(res_conn, "link")
1734 assert link_header =~ ~r/min_id=#{following2.id}/
1735 assert link_header =~ ~r/max_id=#{following2.id}/
1738 test "following / unfollowing a user", %{conn: conn} do
1739 user = insert(:user)
1740 other_user = insert(:user)
1744 |> assign(:user, user)
1745 |> post("/api/v1/accounts/#{other_user.id}/follow")
1747 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1749 user = User.get_cached_by_id(user.id)
1753 |> assign(:user, user)
1754 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1756 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1758 user = User.get_cached_by_id(user.id)
1762 |> assign(:user, user)
1763 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1765 assert %{"id" => id} = json_response(conn, 200)
1766 assert id == to_string(other_user.id)
1769 test "following without reblogs" do
1770 follower = insert(:user)
1771 followed = insert(:user)
1772 other_user = insert(:user)
1776 |> assign(:user, follower)
1777 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1779 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1781 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1782 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1786 |> assign(:user, User.get_cached_by_id(follower.id))
1787 |> get("/api/v1/timelines/home")
1789 assert [] == json_response(conn, 200)
1793 |> assign(:user, follower)
1794 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1796 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1800 |> assign(:user, User.get_cached_by_id(follower.id))
1801 |> get("/api/v1/timelines/home")
1803 expected_activity_id = reblog.id
1804 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1807 test "following / unfollowing errors" do
1808 user = insert(:user)
1812 |> assign(:user, user)
1815 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1816 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1819 user = User.get_cached_by_id(user.id)
1820 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1821 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1823 # self follow via uri
1824 user = User.get_cached_by_id(user.id)
1825 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1826 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1828 # follow non existing user
1829 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1830 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1832 # follow non existing user via uri
1833 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1834 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1836 # unfollow non existing user
1837 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1838 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1841 test "muting / unmuting a user", %{conn: conn} do
1842 user = insert(:user)
1843 other_user = insert(:user)
1847 |> assign(:user, user)
1848 |> post("/api/v1/accounts/#{other_user.id}/mute")
1850 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1852 user = User.get_cached_by_id(user.id)
1856 |> assign(:user, user)
1857 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1859 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1862 test "subscribing / unsubscribing to a user", %{conn: conn} do
1863 user = insert(:user)
1864 subscription_target = insert(:user)
1868 |> assign(:user, user)
1869 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1871 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1875 |> assign(:user, user)
1876 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1878 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1881 test "getting a list of mutes", %{conn: conn} do
1882 user = insert(:user)
1883 other_user = insert(:user)
1885 {:ok, user} = User.mute(user, other_user)
1889 |> assign(:user, user)
1890 |> get("/api/v1/mutes")
1892 other_user_id = to_string(other_user.id)
1893 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1896 test "blocking / unblocking a user", %{conn: conn} do
1897 user = insert(:user)
1898 other_user = insert(:user)
1902 |> assign(:user, user)
1903 |> post("/api/v1/accounts/#{other_user.id}/block")
1905 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1907 user = User.get_cached_by_id(user.id)
1911 |> assign(:user, user)
1912 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1914 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1917 test "getting a list of blocks", %{conn: conn} do
1918 user = insert(:user)
1919 other_user = insert(:user)
1921 {:ok, user} = User.block(user, other_user)
1925 |> assign(:user, user)
1926 |> get("/api/v1/blocks")
1928 other_user_id = to_string(other_user.id)
1929 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1932 test "blocking / unblocking a domain", %{conn: conn} do
1933 user = insert(:user)
1934 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1938 |> assign(:user, user)
1939 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1941 assert %{} = json_response(conn, 200)
1942 user = User.get_cached_by_ap_id(user.ap_id)
1943 assert User.blocks?(user, other_user)
1947 |> assign(:user, user)
1948 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1950 assert %{} = json_response(conn, 200)
1951 user = User.get_cached_by_ap_id(user.ap_id)
1952 refute User.blocks?(user, other_user)
1955 test "getting a list of domain blocks", %{conn: conn} do
1956 user = insert(:user)
1958 {:ok, user} = User.block_domain(user, "bad.site")
1959 {:ok, user} = User.block_domain(user, "even.worse.site")
1963 |> assign(:user, user)
1964 |> get("/api/v1/domain_blocks")
1966 domain_blocks = json_response(conn, 200)
1968 assert "bad.site" in domain_blocks
1969 assert "even.worse.site" in domain_blocks
1972 test "unimplemented follow_requests, blocks, domain blocks" do
1973 user = insert(:user)
1975 ["blocks", "domain_blocks", "follow_requests"]
1976 |> Enum.each(fn endpoint ->
1979 |> assign(:user, user)
1980 |> get("/api/v1/#{endpoint}")
1982 assert [] = json_response(conn, 200)
1986 test "account search", %{conn: conn} do
1987 user = insert(:user)
1988 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1989 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1993 |> assign(:user, user)
1994 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1995 |> json_response(200)
1997 result_ids = for result <- results, do: result["acct"]
1999 assert user_two.nickname in result_ids
2000 assert user_three.nickname in result_ids
2004 |> assign(:user, user)
2005 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
2006 |> json_response(200)
2008 result_ids = for result <- results, do: result["acct"]
2010 assert user_three.nickname in result_ids
2013 test "search", %{conn: conn} do
2014 user = insert(:user)
2015 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2016 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2018 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
2021 CommonAPI.post(user, %{
2022 "status" => "This is about 2hu, but private",
2023 "visibility" => "private"
2026 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
2030 |> get("/api/v1/search", %{"q" => "2hu"})
2032 assert results = json_response(conn, 200)
2034 [account | _] = results["accounts"]
2035 assert account["id"] == to_string(user_three.id)
2037 assert results["hashtags"] == []
2039 [status] = results["statuses"]
2040 assert status["id"] == to_string(activity.id)
2043 test "search fetches remote statuses", %{conn: conn} do
2047 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
2049 assert results = json_response(conn, 200)
2051 [status] = results["statuses"]
2052 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
2056 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
2058 CommonAPI.post(insert(:user), %{
2059 "status" => "This is about 2hu, but private",
2060 "visibility" => "private"
2066 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2068 assert results = json_response(conn, 200)
2070 [] = results["statuses"]
2074 test "search fetches remote accounts", %{conn: conn} do
2077 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2079 assert results = json_response(conn, 200)
2080 [account] = results["accounts"]
2081 assert account["acct"] == "shp@social.heldscal.la"
2084 test "returns the favorites of a user", %{conn: conn} do
2085 user = insert(:user)
2086 other_user = insert(:user)
2088 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2089 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2091 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2095 |> assign(:user, user)
2096 |> get("/api/v1/favourites")
2098 assert [status] = json_response(first_conn, 200)
2099 assert status["id"] == to_string(activity.id)
2101 assert [{"link", _link_header}] =
2102 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2104 # Honours query params
2105 {:ok, second_activity} =
2106 CommonAPI.post(other_user, %{
2108 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2111 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2113 last_like = status["id"]
2117 |> assign(:user, user)
2118 |> get("/api/v1/favourites?since_id=#{last_like}")
2120 assert [second_status] = json_response(second_conn, 200)
2121 assert second_status["id"] == to_string(second_activity.id)
2125 |> assign(:user, user)
2126 |> get("/api/v1/favourites?limit=0")
2128 assert [] = json_response(third_conn, 200)
2131 describe "getting favorites timeline of specified user" do
2133 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2134 [current_user: current_user, user: user]
2137 test "returns list of statuses favorited by specified user", %{
2139 current_user: current_user,
2142 [activity | _] = insert_pair(:note_activity)
2143 CommonAPI.favorite(activity.id, user)
2147 |> assign(:user, current_user)
2148 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2149 |> json_response(:ok)
2153 assert length(response) == 1
2154 assert like["id"] == activity.id
2157 test "returns favorites for specified user_id when user is not logged in", %{
2161 activity = insert(:note_activity)
2162 CommonAPI.favorite(activity.id, user)
2166 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2167 |> json_response(:ok)
2169 assert length(response) == 1
2172 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2174 current_user: current_user,
2178 CommonAPI.post(current_user, %{
2179 "status" => "Hi @#{user.nickname}!",
2180 "visibility" => "direct"
2183 CommonAPI.favorite(direct.id, user)
2187 |> assign(:user, current_user)
2188 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2189 |> json_response(:ok)
2191 assert length(response) == 1
2193 anonymous_response =
2195 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2196 |> json_response(:ok)
2198 assert Enum.empty?(anonymous_response)
2201 test "does not return others' favorited DM when user is not one of recipients", %{
2203 current_user: current_user,
2206 user_two = insert(:user)
2209 CommonAPI.post(user_two, %{
2210 "status" => "Hi @#{user.nickname}!",
2211 "visibility" => "direct"
2214 CommonAPI.favorite(direct.id, user)
2218 |> assign(:user, current_user)
2219 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2220 |> json_response(:ok)
2222 assert Enum.empty?(response)
2225 test "paginates favorites using since_id and max_id", %{
2227 current_user: current_user,
2230 activities = insert_list(10, :note_activity)
2232 Enum.each(activities, fn activity ->
2233 CommonAPI.favorite(activity.id, user)
2236 third_activity = Enum.at(activities, 2)
2237 seventh_activity = Enum.at(activities, 6)
2241 |> assign(:user, current_user)
2242 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2243 since_id: third_activity.id,
2244 max_id: seventh_activity.id
2246 |> json_response(:ok)
2248 assert length(response) == 3
2249 refute third_activity in response
2250 refute seventh_activity in response
2253 test "limits favorites using limit parameter", %{
2255 current_user: current_user,
2259 |> insert_list(:note_activity)
2260 |> Enum.each(fn activity ->
2261 CommonAPI.favorite(activity.id, user)
2266 |> assign(:user, current_user)
2267 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2268 |> json_response(:ok)
2270 assert length(response) == 3
2273 test "returns empty response when user does not have any favorited statuses", %{
2275 current_user: current_user,
2280 |> assign(:user, current_user)
2281 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2282 |> json_response(:ok)
2284 assert Enum.empty?(response)
2287 test "returns 404 error when specified user is not exist", %{conn: conn} do
2288 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2290 assert json_response(conn, 404) == %{"error" => "Record not found"}
2293 test "returns 403 error when user has hidden own favorites", %{
2295 current_user: current_user
2297 user = insert(:user, %{info: %{hide_favorites: true}})
2298 activity = insert(:note_activity)
2299 CommonAPI.favorite(activity.id, user)
2303 |> assign(:user, current_user)
2304 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2306 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2309 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2310 user = insert(:user)
2311 activity = insert(:note_activity)
2312 CommonAPI.favorite(activity.id, user)
2316 |> assign(:user, current_user)
2317 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2319 assert user.info.hide_favorites
2320 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2324 describe "updating credentials" do
2325 test "updates the user's bio", %{conn: conn} do
2326 user = insert(:user)
2327 user2 = insert(:user)
2331 |> assign(:user, user)
2332 |> patch("/api/v1/accounts/update_credentials", %{
2333 "note" => "I drink #cofe with @#{user2.nickname}"
2336 assert user = json_response(conn, 200)
2338 assert user["note"] ==
2339 ~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=") <>
2341 ~s(" class="u-url mention" href=") <>
2342 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2345 test "updates the user's locking status", %{conn: conn} do
2346 user = insert(:user)
2350 |> assign(:user, user)
2351 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2353 assert user = json_response(conn, 200)
2354 assert user["locked"] == true
2357 test "updates the user's default scope", %{conn: conn} do
2358 user = insert(:user)
2362 |> assign(:user, user)
2363 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2365 assert user = json_response(conn, 200)
2366 assert user["source"]["privacy"] == "cofe"
2369 test "updates the user's hide_followers status", %{conn: conn} do
2370 user = insert(:user)
2374 |> assign(:user, user)
2375 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2377 assert user = json_response(conn, 200)
2378 assert user["pleroma"]["hide_followers"] == true
2381 test "updates the user's hide_follows status", %{conn: conn} do
2382 user = insert(:user)
2386 |> assign(:user, user)
2387 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2389 assert user = json_response(conn, 200)
2390 assert user["pleroma"]["hide_follows"] == true
2393 test "updates the user's hide_favorites status", %{conn: conn} do
2394 user = insert(:user)
2398 |> assign(:user, user)
2399 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2401 assert user = json_response(conn, 200)
2402 assert user["pleroma"]["hide_favorites"] == true
2405 test "updates the user's show_role status", %{conn: conn} do
2406 user = insert(:user)
2410 |> assign(:user, user)
2411 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2413 assert user = json_response(conn, 200)
2414 assert user["source"]["pleroma"]["show_role"] == false
2417 test "updates the user's no_rich_text status", %{conn: conn} do
2418 user = insert(:user)
2422 |> assign(:user, user)
2423 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2425 assert user = json_response(conn, 200)
2426 assert user["source"]["pleroma"]["no_rich_text"] == true
2429 test "updates the user's name", %{conn: conn} do
2430 user = insert(:user)
2434 |> assign(:user, user)
2435 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2437 assert user = json_response(conn, 200)
2438 assert user["display_name"] == "markorepairs"
2441 test "updates the user's avatar", %{conn: conn} do
2442 user = insert(:user)
2444 new_avatar = %Plug.Upload{
2445 content_type: "image/jpg",
2446 path: Path.absname("test/fixtures/image.jpg"),
2447 filename: "an_image.jpg"
2452 |> assign(:user, user)
2453 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2455 assert user_response = json_response(conn, 200)
2456 assert user_response["avatar"] != User.avatar_url(user)
2459 test "updates the user's banner", %{conn: conn} do
2460 user = insert(:user)
2462 new_header = %Plug.Upload{
2463 content_type: "image/jpg",
2464 path: Path.absname("test/fixtures/image.jpg"),
2465 filename: "an_image.jpg"
2470 |> assign(:user, user)
2471 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2473 assert user_response = json_response(conn, 200)
2474 assert user_response["header"] != User.banner_url(user)
2477 test "requires 'write' permission", %{conn: conn} do
2478 token1 = insert(:oauth_token, scopes: ["read"])
2479 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2481 for token <- [token1, token2] do
2484 |> put_req_header("authorization", "Bearer #{token.token}")
2485 |> patch("/api/v1/accounts/update_credentials", %{})
2487 if token == token1 do
2488 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2490 assert json_response(conn, 200)
2495 test "updates profile emojos", %{conn: conn} do
2496 user = insert(:user)
2498 note = "*sips :blank:*"
2499 name = "I am :firefox:"
2503 |> assign(:user, user)
2504 |> patch("/api/v1/accounts/update_credentials", %{
2506 "display_name" => name
2509 assert json_response(conn, 200)
2513 |> get("/api/v1/accounts/#{user.id}")
2515 assert user = json_response(conn, 200)
2517 assert user["note"] == note
2518 assert user["display_name"] == name
2519 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2523 test "get instance information", %{conn: conn} do
2524 conn = get(conn, "/api/v1/instance")
2525 assert result = json_response(conn, 200)
2527 email = Pleroma.Config.get([:instance, :email])
2528 # Note: not checking for "max_toot_chars" since it's optional
2534 "email" => from_config_email,
2536 "streaming_api" => _
2541 "registrations" => _
2544 assert email == from_config_email
2547 test "get instance stats", %{conn: conn} do
2548 user = insert(:user, %{local: true})
2550 user2 = insert(:user, %{local: true})
2551 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2553 insert(:user, %{local: false, nickname: "u@peer1.com"})
2554 insert(:user, %{local: false, nickname: "u@peer2.com"})
2556 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2558 # Stats should count users with missing or nil `info.deactivated` value
2559 user = User.get_cached_by_id(user.id)
2560 info_change = Changeset.change(user.info, %{deactivated: nil})
2564 |> Changeset.change()
2565 |> Changeset.put_embed(:info, info_change)
2566 |> User.update_and_set_cache()
2568 Pleroma.Stats.update_stats()
2570 conn = get(conn, "/api/v1/instance")
2572 assert result = json_response(conn, 200)
2574 stats = result["stats"]
2577 assert stats["user_count"] == 1
2578 assert stats["status_count"] == 1
2579 assert stats["domain_count"] == 2
2582 test "get peers", %{conn: conn} do
2583 insert(:user, %{local: false, nickname: "u@peer1.com"})
2584 insert(:user, %{local: false, nickname: "u@peer2.com"})
2586 Pleroma.Stats.update_stats()
2588 conn = get(conn, "/api/v1/instance/peers")
2590 assert result = json_response(conn, 200)
2592 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2595 test "put settings", %{conn: conn} do
2596 user = insert(:user)
2600 |> assign(:user, user)
2601 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2603 assert _result = json_response(conn, 200)
2605 user = User.get_cached_by_ap_id(user.ap_id)
2606 assert user.info.settings == %{"programming" => "socks"}
2609 describe "pinned statuses" do
2611 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2613 user = insert(:user)
2614 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2616 [user: user, activity: activity]
2619 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2620 {:ok, _} = CommonAPI.pin(activity.id, user)
2624 |> assign(:user, user)
2625 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2626 |> json_response(200)
2628 id_str = to_string(activity.id)
2630 assert [%{"id" => ^id_str, "pinned" => true}] = result
2633 test "pin status", %{conn: conn, user: user, activity: activity} do
2634 id_str = to_string(activity.id)
2636 assert %{"id" => ^id_str, "pinned" => true} =
2638 |> assign(:user, user)
2639 |> post("/api/v1/statuses/#{activity.id}/pin")
2640 |> json_response(200)
2642 assert [%{"id" => ^id_str, "pinned" => true}] =
2644 |> assign(:user, user)
2645 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2646 |> json_response(200)
2649 test "unpin status", %{conn: conn, user: user, activity: activity} do
2650 {:ok, _} = CommonAPI.pin(activity.id, user)
2652 id_str = to_string(activity.id)
2653 user = refresh_record(user)
2655 assert %{"id" => ^id_str, "pinned" => false} =
2657 |> assign(:user, user)
2658 |> post("/api/v1/statuses/#{activity.id}/unpin")
2659 |> json_response(200)
2663 |> assign(:user, user)
2664 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2665 |> json_response(200)
2668 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2669 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2671 id_str_one = to_string(activity_one.id)
2673 assert %{"id" => ^id_str_one, "pinned" => true} =
2675 |> assign(:user, user)
2676 |> post("/api/v1/statuses/#{id_str_one}/pin")
2677 |> json_response(200)
2679 user = refresh_record(user)
2681 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2683 |> assign(:user, user)
2684 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2685 |> json_response(400)
2691 Pleroma.Config.put([:rich_media, :enabled], true)
2694 Pleroma.Config.put([:rich_media, :enabled], false)
2697 user = insert(:user)
2701 test "returns rich-media card", %{conn: conn, user: user} do
2702 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2705 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2706 "provider_name" => "www.imdb.com",
2707 "provider_url" => "http://www.imdb.com",
2708 "title" => "The Rock",
2710 "url" => "http://www.imdb.com/title/tt0117500/",
2712 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2715 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2716 "title" => "The Rock",
2717 "type" => "video.movie",
2718 "url" => "http://www.imdb.com/title/tt0117500/",
2720 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2727 |> get("/api/v1/statuses/#{activity.id}/card")
2728 |> json_response(200)
2730 assert response == card_data
2732 # works with private posts
2734 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2738 |> assign(:user, user)
2739 |> get("/api/v1/statuses/#{activity.id}/card")
2740 |> json_response(200)
2742 assert response_two == card_data
2745 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2746 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp-missing-data"})
2750 |> get("/api/v1/statuses/#{activity.id}/card")
2751 |> json_response(:ok)
2753 assert response == %{
2755 "title" => "Pleroma",
2756 "description" => "",
2758 "provider_name" => "pleroma.social",
2759 "provider_url" => "https://pleroma.social",
2760 "url" => "https://pleroma.social/",
2763 "title" => "Pleroma",
2764 "type" => "website",
2765 "url" => "https://pleroma.social/"
2773 user = insert(:user)
2774 for_user = insert(:user)
2777 CommonAPI.post(user, %{
2778 "status" => "heweoo?"
2782 CommonAPI.post(user, %{
2783 "status" => "heweoo!"
2788 |> assign(:user, for_user)
2789 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2791 assert json_response(response1, 200)["bookmarked"] == true
2795 |> assign(:user, for_user)
2796 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2798 assert json_response(response2, 200)["bookmarked"] == true
2802 |> assign(:user, for_user)
2803 |> get("/api/v1/bookmarks")
2805 assert [json_response(response2, 200), json_response(response1, 200)] ==
2806 json_response(bookmarks, 200)
2810 |> assign(:user, for_user)
2811 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2813 assert json_response(response1, 200)["bookmarked"] == false
2817 |> assign(:user, for_user)
2818 |> get("/api/v1/bookmarks")
2820 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2823 describe "conversation muting" do
2825 user = insert(:user)
2826 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2828 [user: user, activity: activity]
2831 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2832 id_str = to_string(activity.id)
2834 assert %{"id" => ^id_str, "muted" => true} =
2836 |> assign(:user, user)
2837 |> post("/api/v1/statuses/#{activity.id}/mute")
2838 |> json_response(200)
2841 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2842 {:ok, _} = CommonAPI.add_mute(user, activity)
2844 id_str = to_string(activity.id)
2845 user = refresh_record(user)
2847 assert %{"id" => ^id_str, "muted" => false} =
2849 |> assign(:user, user)
2850 |> post("/api/v1/statuses/#{activity.id}/unmute")
2851 |> json_response(200)
2855 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2856 user = insert(:user)
2860 |> assign(:user, user)
2861 |> get("/api/v1/pleroma/flavour")
2863 assert "glitch" == json_response(get_old_flavour, 200)
2867 |> assign(:user, user)
2868 |> post("/api/v1/pleroma/flavour/vanilla")
2870 assert "vanilla" == json_response(set_flavour, 200)
2874 |> assign(:user, user)
2875 |> post("/api/v1/pleroma/flavour/vanilla")
2877 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2880 describe "reports" do
2882 reporter = insert(:user)
2883 target_user = insert(:user)
2885 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2887 [reporter: reporter, target_user: target_user, activity: activity]
2890 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2891 assert %{"action_taken" => false, "id" => _} =
2893 |> assign(:user, reporter)
2894 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2895 |> json_response(200)
2898 test "submit a report with statuses and comment", %{
2901 target_user: target_user,
2904 assert %{"action_taken" => false, "id" => _} =
2906 |> assign(:user, reporter)
2907 |> post("/api/v1/reports", %{
2908 "account_id" => target_user.id,
2909 "status_ids" => [activity.id],
2910 "comment" => "bad status!"
2912 |> json_response(200)
2915 test "account_id is required", %{
2920 assert %{"error" => "Valid `account_id` required"} =
2922 |> assign(:user, reporter)
2923 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2924 |> json_response(400)
2927 test "comment must be up to the size specified in the config", %{
2930 target_user: target_user
2932 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2933 comment = String.pad_trailing("a", max_size + 1, "a")
2935 error = %{"error" => "Comment must be up to #{max_size} characters"}
2939 |> assign(:user, reporter)
2940 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2941 |> json_response(400)
2945 describe "link headers" do
2946 test "preserves parameters in link headers", %{conn: conn} do
2947 user = insert(:user)
2948 other_user = insert(:user)
2951 CommonAPI.post(other_user, %{
2952 "status" => "hi @#{user.nickname}",
2953 "visibility" => "public"
2957 CommonAPI.post(other_user, %{
2958 "status" => "hi @#{user.nickname}",
2959 "visibility" => "public"
2962 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2963 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2967 |> assign(:user, user)
2968 |> get("/api/v1/notifications", %{media_only: true})
2970 assert [link_header] = get_resp_header(conn, "link")
2971 assert link_header =~ ~r/media_only=true/
2972 assert link_header =~ ~r/min_id=#{notification2.id}/
2973 assert link_header =~ ~r/max_id=#{notification1.id}/
2977 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2978 # Need to set an old-style integer ID to reproduce the problem
2979 # (these are no longer assigned to new accounts but were preserved
2980 # for existing accounts during the migration to flakeIDs)
2981 user_one = insert(:user, %{id: 1212})
2982 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2986 |> get("/api/v1/accounts/#{user_one.id}")
2990 |> get("/api/v1/accounts/#{user_two.nickname}")
2994 |> get("/api/v1/accounts/#{user_two.id}")
2996 acc_one = json_response(resp_one, 200)
2997 acc_two = json_response(resp_two, 200)
2998 acc_three = json_response(resp_three, 200)
2999 refute acc_one == acc_two
3000 assert acc_two == acc_three
3003 describe "custom emoji" do
3004 test "with tags", %{conn: conn} do
3007 |> get("/api/v1/custom_emojis")
3008 |> json_response(200)
3010 assert Map.has_key?(emoji, "shortcode")
3011 assert Map.has_key?(emoji, "static_url")
3012 assert Map.has_key?(emoji, "tags")
3013 assert is_list(emoji["tags"])
3014 assert Map.has_key?(emoji, "url")
3015 assert Map.has_key?(emoji, "visible_in_picker")
3019 describe "index/2 redirections" do
3020 setup %{conn: conn} do
3024 signing_salt: "cooldude"
3029 |> Plug.Session.call(Plug.Session.init(session_opts))
3032 test_path = "/web/statuses/test"
3033 %{conn: conn, path: test_path}
3036 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3037 conn = get(conn, path)
3039 assert conn.status == 302
3040 assert redirected_to(conn) == "/web/login"
3043 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3044 token = insert(:oauth_token)
3048 |> assign(:user, token.user)
3049 |> put_session(:oauth_token, token.token)
3052 assert conn.status == 200
3055 test "saves referer path to session", %{conn: conn, path: path} do
3056 conn = get(conn, path)
3057 return_to = Plug.Conn.get_session(conn, :return_to)
3059 assert return_to == path
3062 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3063 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3064 auth = insert(:oauth_authorization, app: app)
3068 |> put_session(:return_to, path)
3069 |> get("/web/login", %{code: auth.token})
3071 assert conn.status == 302
3072 assert redirected_to(conn) == path
3075 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3076 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3077 auth = insert(:oauth_authorization, app: app)
3079 conn = get(conn, "/web/login", %{code: auth.token})
3081 assert conn.status == 302
3082 assert redirected_to(conn) == "/web/getting-started"
3086 describe "scheduled activities" do
3087 test "creates a scheduled activity", %{conn: conn} do
3088 user = insert(:user)
3089 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3093 |> assign(:user, user)
3094 |> post("/api/v1/statuses", %{
3095 "status" => "scheduled",
3096 "scheduled_at" => scheduled_at
3099 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3100 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3101 assert [] == Repo.all(Activity)
3104 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3105 user = insert(:user)
3106 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3108 file = %Plug.Upload{
3109 content_type: "image/jpg",
3110 path: Path.absname("test/fixtures/image.jpg"),
3111 filename: "an_image.jpg"
3114 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3118 |> assign(:user, user)
3119 |> post("/api/v1/statuses", %{
3120 "media_ids" => [to_string(upload.id)],
3121 "status" => "scheduled",
3122 "scheduled_at" => scheduled_at
3125 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3126 assert %{"type" => "image"} = media_attachment
3129 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3131 user = insert(:user)
3134 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3138 |> assign(:user, user)
3139 |> post("/api/v1/statuses", %{
3140 "status" => "not scheduled",
3141 "scheduled_at" => scheduled_at
3144 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3145 assert [] == Repo.all(ScheduledActivity)
3148 test "returns error when daily user limit is exceeded", %{conn: conn} do
3149 user = insert(:user)
3152 NaiveDateTime.utc_now()
3153 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3154 |> NaiveDateTime.to_iso8601()
3156 attrs = %{params: %{}, scheduled_at: today}
3157 {:ok, _} = ScheduledActivity.create(user, attrs)
3158 {:ok, _} = ScheduledActivity.create(user, attrs)
3162 |> assign(:user, user)
3163 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3165 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3168 test "returns error when total user limit is exceeded", %{conn: conn} do
3169 user = insert(:user)
3172 NaiveDateTime.utc_now()
3173 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3174 |> NaiveDateTime.to_iso8601()
3177 NaiveDateTime.utc_now()
3178 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3179 |> NaiveDateTime.to_iso8601()
3181 attrs = %{params: %{}, scheduled_at: today}
3182 {:ok, _} = ScheduledActivity.create(user, attrs)
3183 {:ok, _} = ScheduledActivity.create(user, attrs)
3184 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3188 |> assign(:user, user)
3189 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3191 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3194 test "shows scheduled activities", %{conn: conn} do
3195 user = insert(:user)
3196 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3197 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3198 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3199 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3203 |> assign(:user, user)
3208 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3210 result = json_response(conn_res, 200)
3211 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3216 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3218 result = json_response(conn_res, 200)
3219 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3224 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3226 result = json_response(conn_res, 200)
3227 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3230 test "shows a scheduled activity", %{conn: conn} do
3231 user = insert(:user)
3232 scheduled_activity = insert(:scheduled_activity, user: user)
3236 |> assign(:user, user)
3237 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3239 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3240 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3244 |> assign(:user, user)
3245 |> get("/api/v1/scheduled_statuses/404")
3247 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3250 test "updates a scheduled activity", %{conn: conn} do
3251 user = insert(:user)
3252 scheduled_activity = insert(:scheduled_activity, user: user)
3255 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3259 |> assign(:user, user)
3260 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3261 scheduled_at: new_scheduled_at
3264 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3265 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3269 |> assign(:user, user)
3270 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3272 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3275 test "deletes a scheduled activity", %{conn: conn} do
3276 user = insert(:user)
3277 scheduled_activity = insert(:scheduled_activity, user: user)
3281 |> assign(:user, user)
3282 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3284 assert %{} = json_response(res_conn, 200)
3285 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3289 |> assign(:user, user)
3290 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3292 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3296 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3297 user1 = insert(:user)
3298 user2 = insert(:user)
3299 user3 = insert(:user)
3301 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3303 # Reply to status from another user
3306 |> assign(:user, user2)
3307 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3309 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3311 activity = Activity.get_by_id_with_object(id)
3313 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3314 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3316 # Reblog from the third user
3319 |> assign(:user, user3)
3320 |> post("/api/v1/statuses/#{activity.id}/reblog")
3322 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3323 json_response(conn2, 200)
3325 assert to_string(activity.id) == id
3327 # Getting third user status
3330 |> assign(:user, user3)
3331 |> get("api/v1/timelines/home")
3333 [reblogged_activity] = json_response(conn3, 200)
3335 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3337 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3338 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3341 describe "create account by app" do
3343 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3344 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3345 interval = Pleroma.Config.get([:app_account_creation, :interval])
3347 Pleroma.Config.put([:app_account_creation, :enabled], true)
3348 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3349 Pleroma.Config.put([:app_account_creation, :interval], 1)
3352 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3353 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3354 Pleroma.Config.put([:app_account_creation, :interval], interval)
3360 test "Account registration via Application", %{conn: conn} do
3363 |> post("/api/v1/apps", %{
3364 client_name: "client_name",
3365 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3366 scopes: "read, write, follow"
3370 "client_id" => client_id,
3371 "client_secret" => client_secret,
3373 "name" => "client_name",
3374 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3377 } = json_response(conn, 200)
3381 |> post("/oauth/token", %{
3382 grant_type: "client_credentials",
3383 client_id: client_id,
3384 client_secret: client_secret
3387 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3388 json_response(conn, 200)
3391 token_from_db = Repo.get_by(Token, token: token)
3392 assert token_from_db
3394 assert scope == "read write follow"
3398 |> put_req_header("authorization", "Bearer " <> token)
3399 |> post("/api/v1/accounts", %{
3401 email: "lain@example.org",
3402 password: "PlzDontHackLain",
3407 "access_token" => token,
3408 "created_at" => _created_at,
3410 "token_type" => "Bearer"
3411 } = json_response(conn, 200)
3413 token_from_db = Repo.get_by(Token, token: token)
3414 assert token_from_db
3415 token_from_db = Repo.preload(token_from_db, :user)
3416 assert token_from_db.user
3418 assert token_from_db.user.info.confirmation_pending
3421 test "rate limit", %{conn: conn} do
3422 app_token = insert(:oauth_token, user: nil)
3425 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3426 |> Map.put(:remote_ip, {15, 15, 15, 15})
3431 |> post("/api/v1/accounts", %{
3432 username: "#{i}lain",
3433 email: "#{i}lain@example.org",
3434 password: "PlzDontHackLain",
3439 "access_token" => token,
3440 "created_at" => _created_at,
3442 "token_type" => "Bearer"
3443 } = json_response(conn, 200)
3445 token_from_db = Repo.get_by(Token, token: token)
3446 assert token_from_db
3447 token_from_db = Repo.preload(token_from_db, :user)
3448 assert token_from_db.user
3450 assert token_from_db.user.info.confirmation_pending
3455 |> post("/api/v1/accounts", %{
3457 email: "6lain@example.org",
3458 password: "PlzDontHackLain",
3462 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}