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 "hashtag timeline", %{conn: conn} do
1459 following = insert(:user)
1462 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1464 {:ok, [_activity]} =
1465 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1469 |> get("/api/v1/timelines/tag/2hu")
1471 assert [%{"id" => id}] = json_response(nconn, 200)
1473 assert id == to_string(activity.id)
1475 # works for different capitalization too
1478 |> get("/api/v1/timelines/tag/2HU")
1480 assert [%{"id" => id}] = json_response(nconn, 200)
1482 assert id == to_string(activity.id)
1486 test "multi-hashtag timeline", %{conn: conn} do
1487 user = insert(:user)
1489 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1490 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1491 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1495 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1497 [status_none, status_test1, status_test] = json_response(any_test, 200)
1499 assert to_string(activity_test.id) == status_test["id"]
1500 assert to_string(activity_test1.id) == status_test1["id"]
1501 assert to_string(activity_none.id) == status_none["id"]
1505 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1507 assert [status_test1] == json_response(restricted_test, 200)
1509 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1511 assert [status_none] == json_response(all_test, 200)
1514 test "getting followers", %{conn: conn} do
1515 user = insert(:user)
1516 other_user = insert(:user)
1517 {:ok, user} = User.follow(user, other_user)
1521 |> get("/api/v1/accounts/#{other_user.id}/followers")
1523 assert [%{"id" => id}] = json_response(conn, 200)
1524 assert id == to_string(user.id)
1527 test "getting followers, hide_followers", %{conn: conn} do
1528 user = insert(:user)
1529 other_user = insert(:user, %{info: %{hide_followers: true}})
1530 {:ok, _user} = User.follow(user, other_user)
1534 |> get("/api/v1/accounts/#{other_user.id}/followers")
1536 assert [] == json_response(conn, 200)
1539 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1540 user = insert(:user)
1541 other_user = insert(:user, %{info: %{hide_followers: true}})
1542 {:ok, _user} = User.follow(user, other_user)
1546 |> assign(:user, other_user)
1547 |> get("/api/v1/accounts/#{other_user.id}/followers")
1549 refute [] == json_response(conn, 200)
1552 test "getting followers, pagination", %{conn: conn} do
1553 user = insert(:user)
1554 follower1 = insert(:user)
1555 follower2 = insert(:user)
1556 follower3 = insert(:user)
1557 {:ok, _} = User.follow(follower1, user)
1558 {:ok, _} = User.follow(follower2, user)
1559 {:ok, _} = User.follow(follower3, user)
1563 |> assign(:user, user)
1567 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1569 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1570 assert id3 == follower3.id
1571 assert id2 == follower2.id
1575 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1577 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1578 assert id2 == follower2.id
1579 assert id1 == follower1.id
1583 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1585 assert [%{"id" => id2}] = json_response(res_conn, 200)
1586 assert id2 == follower2.id
1588 assert [link_header] = get_resp_header(res_conn, "link")
1589 assert link_header =~ ~r/min_id=#{follower2.id}/
1590 assert link_header =~ ~r/max_id=#{follower2.id}/
1593 test "getting following", %{conn: conn} do
1594 user = insert(:user)
1595 other_user = insert(:user)
1596 {:ok, user} = User.follow(user, other_user)
1600 |> get("/api/v1/accounts/#{user.id}/following")
1602 assert [%{"id" => id}] = json_response(conn, 200)
1603 assert id == to_string(other_user.id)
1606 test "getting following, hide_follows", %{conn: conn} do
1607 user = insert(:user, %{info: %{hide_follows: true}})
1608 other_user = insert(:user)
1609 {:ok, user} = User.follow(user, other_user)
1613 |> get("/api/v1/accounts/#{user.id}/following")
1615 assert [] == json_response(conn, 200)
1618 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1619 user = insert(:user, %{info: %{hide_follows: true}})
1620 other_user = insert(:user)
1621 {:ok, user} = User.follow(user, other_user)
1625 |> assign(:user, user)
1626 |> get("/api/v1/accounts/#{user.id}/following")
1628 refute [] == json_response(conn, 200)
1631 test "getting following, pagination", %{conn: conn} do
1632 user = insert(:user)
1633 following1 = insert(:user)
1634 following2 = insert(:user)
1635 following3 = insert(:user)
1636 {:ok, _} = User.follow(user, following1)
1637 {:ok, _} = User.follow(user, following2)
1638 {:ok, _} = User.follow(user, following3)
1642 |> assign(:user, user)
1646 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1648 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1649 assert id3 == following3.id
1650 assert id2 == following2.id
1654 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1656 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1657 assert id2 == following2.id
1658 assert id1 == following1.id
1662 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1664 assert [%{"id" => id2}] = json_response(res_conn, 200)
1665 assert id2 == following2.id
1667 assert [link_header] = get_resp_header(res_conn, "link")
1668 assert link_header =~ ~r/min_id=#{following2.id}/
1669 assert link_header =~ ~r/max_id=#{following2.id}/
1672 test "following / unfollowing a user", %{conn: conn} do
1673 user = insert(:user)
1674 other_user = insert(:user)
1678 |> assign(:user, user)
1679 |> post("/api/v1/accounts/#{other_user.id}/follow")
1681 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1683 user = User.get_cached_by_id(user.id)
1687 |> assign(:user, user)
1688 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1690 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1692 user = User.get_cached_by_id(user.id)
1696 |> assign(:user, user)
1697 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1699 assert %{"id" => id} = json_response(conn, 200)
1700 assert id == to_string(other_user.id)
1703 test "following without reblogs" do
1704 follower = insert(:user)
1705 followed = insert(:user)
1706 other_user = insert(:user)
1710 |> assign(:user, follower)
1711 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1713 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1715 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1716 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1720 |> assign(:user, User.get_cached_by_id(follower.id))
1721 |> get("/api/v1/timelines/home")
1723 assert [] == json_response(conn, 200)
1727 |> assign(:user, follower)
1728 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1730 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1734 |> assign(:user, User.get_cached_by_id(follower.id))
1735 |> get("/api/v1/timelines/home")
1737 expected_activity_id = reblog.id
1738 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1741 test "following / unfollowing errors" do
1742 user = insert(:user)
1746 |> assign(:user, user)
1749 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1750 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1753 user = User.get_cached_by_id(user.id)
1754 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1755 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1757 # self follow via uri
1758 user = User.get_cached_by_id(user.id)
1759 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1760 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1762 # follow non existing user
1763 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1764 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1766 # follow non existing user via uri
1767 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1768 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1770 # unfollow non existing user
1771 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1772 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1775 test "muting / unmuting a user", %{conn: conn} do
1776 user = insert(:user)
1777 other_user = insert(:user)
1781 |> assign(:user, user)
1782 |> post("/api/v1/accounts/#{other_user.id}/mute")
1784 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1786 user = User.get_cached_by_id(user.id)
1790 |> assign(:user, user)
1791 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1793 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1796 test "subscribing / unsubscribing to a user", %{conn: conn} do
1797 user = insert(:user)
1798 subscription_target = insert(:user)
1802 |> assign(:user, user)
1803 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1805 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1809 |> assign(:user, user)
1810 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1812 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1815 test "getting a list of mutes", %{conn: conn} do
1816 user = insert(:user)
1817 other_user = insert(:user)
1819 {:ok, user} = User.mute(user, other_user)
1823 |> assign(:user, user)
1824 |> get("/api/v1/mutes")
1826 other_user_id = to_string(other_user.id)
1827 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1830 test "blocking / unblocking a user", %{conn: conn} do
1831 user = insert(:user)
1832 other_user = insert(:user)
1836 |> assign(:user, user)
1837 |> post("/api/v1/accounts/#{other_user.id}/block")
1839 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1841 user = User.get_cached_by_id(user.id)
1845 |> assign(:user, user)
1846 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1848 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1851 test "getting a list of blocks", %{conn: conn} do
1852 user = insert(:user)
1853 other_user = insert(:user)
1855 {:ok, user} = User.block(user, other_user)
1859 |> assign(:user, user)
1860 |> get("/api/v1/blocks")
1862 other_user_id = to_string(other_user.id)
1863 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1866 test "blocking / unblocking a domain", %{conn: conn} do
1867 user = insert(:user)
1868 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1872 |> assign(:user, user)
1873 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1875 assert %{} = json_response(conn, 200)
1876 user = User.get_cached_by_ap_id(user.ap_id)
1877 assert User.blocks?(user, other_user)
1881 |> assign(:user, user)
1882 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1884 assert %{} = json_response(conn, 200)
1885 user = User.get_cached_by_ap_id(user.ap_id)
1886 refute User.blocks?(user, other_user)
1889 test "getting a list of domain blocks", %{conn: conn} do
1890 user = insert(:user)
1892 {:ok, user} = User.block_domain(user, "bad.site")
1893 {:ok, user} = User.block_domain(user, "even.worse.site")
1897 |> assign(:user, user)
1898 |> get("/api/v1/domain_blocks")
1900 domain_blocks = json_response(conn, 200)
1902 assert "bad.site" in domain_blocks
1903 assert "even.worse.site" in domain_blocks
1906 test "unimplemented follow_requests, blocks, domain blocks" do
1907 user = insert(:user)
1909 ["blocks", "domain_blocks", "follow_requests"]
1910 |> Enum.each(fn endpoint ->
1913 |> assign(:user, user)
1914 |> get("/api/v1/#{endpoint}")
1916 assert [] = json_response(conn, 200)
1920 test "account search", %{conn: conn} do
1921 user = insert(:user)
1922 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1923 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1927 |> assign(:user, user)
1928 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1929 |> json_response(200)
1931 result_ids = for result <- results, do: result["acct"]
1933 assert user_two.nickname in result_ids
1934 assert user_three.nickname in result_ids
1938 |> assign(:user, user)
1939 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
1940 |> json_response(200)
1942 result_ids = for result <- results, do: result["acct"]
1944 assert user_three.nickname in result_ids
1947 test "search", %{conn: conn} do
1948 user = insert(:user)
1949 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1950 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1952 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
1955 CommonAPI.post(user, %{
1956 "status" => "This is about 2hu, but private",
1957 "visibility" => "private"
1960 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
1964 |> get("/api/v1/search", %{"q" => "2hu"})
1966 assert results = json_response(conn, 200)
1968 [account | _] = results["accounts"]
1969 assert account["id"] == to_string(user_three.id)
1971 assert results["hashtags"] == []
1973 [status] = results["statuses"]
1974 assert status["id"] == to_string(activity.id)
1977 test "search fetches remote statuses", %{conn: conn} do
1981 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
1983 assert results = json_response(conn, 200)
1985 [status] = results["statuses"]
1986 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1990 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
1992 CommonAPI.post(insert(:user), %{
1993 "status" => "This is about 2hu, but private",
1994 "visibility" => "private"
2000 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2002 assert results = json_response(conn, 200)
2004 [] = results["statuses"]
2008 test "search fetches remote accounts", %{conn: conn} do
2011 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2013 assert results = json_response(conn, 200)
2014 [account] = results["accounts"]
2015 assert account["acct"] == "shp@social.heldscal.la"
2018 test "returns the favorites of a user", %{conn: conn} do
2019 user = insert(:user)
2020 other_user = insert(:user)
2022 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2023 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2025 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2029 |> assign(:user, user)
2030 |> get("/api/v1/favourites")
2032 assert [status] = json_response(first_conn, 200)
2033 assert status["id"] == to_string(activity.id)
2035 assert [{"link", _link_header}] =
2036 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2038 # Honours query params
2039 {:ok, second_activity} =
2040 CommonAPI.post(other_user, %{
2042 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2045 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2047 last_like = status["id"]
2051 |> assign(:user, user)
2052 |> get("/api/v1/favourites?since_id=#{last_like}")
2054 assert [second_status] = json_response(second_conn, 200)
2055 assert second_status["id"] == to_string(second_activity.id)
2059 |> assign(:user, user)
2060 |> get("/api/v1/favourites?limit=0")
2062 assert [] = json_response(third_conn, 200)
2065 describe "getting favorites timeline of specified user" do
2067 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2068 [current_user: current_user, user: user]
2071 test "returns list of statuses favorited by specified user", %{
2073 current_user: current_user,
2076 [activity | _] = insert_pair(:note_activity)
2077 CommonAPI.favorite(activity.id, user)
2081 |> assign(:user, current_user)
2082 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2083 |> json_response(:ok)
2087 assert length(response) == 1
2088 assert like["id"] == activity.id
2091 test "returns favorites for specified user_id when user is not logged in", %{
2095 activity = insert(:note_activity)
2096 CommonAPI.favorite(activity.id, user)
2100 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2101 |> json_response(:ok)
2103 assert length(response) == 1
2106 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2108 current_user: current_user,
2112 CommonAPI.post(current_user, %{
2113 "status" => "Hi @#{user.nickname}!",
2114 "visibility" => "direct"
2117 CommonAPI.favorite(direct.id, user)
2121 |> assign(:user, current_user)
2122 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2123 |> json_response(:ok)
2125 assert length(response) == 1
2127 anonymous_response =
2129 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2130 |> json_response(:ok)
2132 assert Enum.empty?(anonymous_response)
2135 test "does not return others' favorited DM when user is not one of recipients", %{
2137 current_user: current_user,
2140 user_two = insert(:user)
2143 CommonAPI.post(user_two, %{
2144 "status" => "Hi @#{user.nickname}!",
2145 "visibility" => "direct"
2148 CommonAPI.favorite(direct.id, user)
2152 |> assign(:user, current_user)
2153 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2154 |> json_response(:ok)
2156 assert Enum.empty?(response)
2159 test "paginates favorites using since_id and max_id", %{
2161 current_user: current_user,
2164 activities = insert_list(10, :note_activity)
2166 Enum.each(activities, fn activity ->
2167 CommonAPI.favorite(activity.id, user)
2170 third_activity = Enum.at(activities, 2)
2171 seventh_activity = Enum.at(activities, 6)
2175 |> assign(:user, current_user)
2176 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2177 since_id: third_activity.id,
2178 max_id: seventh_activity.id
2180 |> json_response(:ok)
2182 assert length(response) == 3
2183 refute third_activity in response
2184 refute seventh_activity in response
2187 test "limits favorites using limit parameter", %{
2189 current_user: current_user,
2193 |> insert_list(:note_activity)
2194 |> Enum.each(fn activity ->
2195 CommonAPI.favorite(activity.id, user)
2200 |> assign(:user, current_user)
2201 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2202 |> json_response(:ok)
2204 assert length(response) == 3
2207 test "returns empty response when user does not have any favorited statuses", %{
2209 current_user: current_user,
2214 |> assign(:user, current_user)
2215 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2216 |> json_response(:ok)
2218 assert Enum.empty?(response)
2221 test "returns 404 error when specified user is not exist", %{conn: conn} do
2222 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2224 assert json_response(conn, 404) == %{"error" => "Record not found"}
2227 test "returns 403 error when user has hidden own favorites", %{
2229 current_user: current_user
2231 user = insert(:user, %{info: %{hide_favorites: true}})
2232 activity = insert(:note_activity)
2233 CommonAPI.favorite(activity.id, user)
2237 |> assign(:user, current_user)
2238 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2240 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2243 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2244 user = insert(:user)
2245 activity = insert(:note_activity)
2246 CommonAPI.favorite(activity.id, user)
2250 |> assign(:user, current_user)
2251 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2253 assert user.info.hide_favorites
2254 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2258 describe "updating credentials" do
2259 test "updates the user's bio", %{conn: conn} do
2260 user = insert(:user)
2261 user2 = insert(:user)
2265 |> assign(:user, user)
2266 |> patch("/api/v1/accounts/update_credentials", %{
2267 "note" => "I drink #cofe with @#{user2.nickname}"
2270 assert user = json_response(conn, 200)
2272 assert user["note"] ==
2273 ~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=") <>
2275 ~s(" class="u-url mention" href=") <>
2276 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2279 test "updates the user's locking status", %{conn: conn} do
2280 user = insert(:user)
2284 |> assign(:user, user)
2285 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2287 assert user = json_response(conn, 200)
2288 assert user["locked"] == true
2291 test "updates the user's default scope", %{conn: conn} do
2292 user = insert(:user)
2296 |> assign(:user, user)
2297 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2299 assert user = json_response(conn, 200)
2300 assert user["source"]["privacy"] == "cofe"
2303 test "updates the user's hide_followers status", %{conn: conn} do
2304 user = insert(:user)
2308 |> assign(:user, user)
2309 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2311 assert user = json_response(conn, 200)
2312 assert user["pleroma"]["hide_followers"] == true
2315 test "updates the user's hide_follows status", %{conn: conn} do
2316 user = insert(:user)
2320 |> assign(:user, user)
2321 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2323 assert user = json_response(conn, 200)
2324 assert user["pleroma"]["hide_follows"] == true
2327 test "updates the user's hide_favorites status", %{conn: conn} do
2328 user = insert(:user)
2332 |> assign(:user, user)
2333 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2335 assert user = json_response(conn, 200)
2336 assert user["pleroma"]["hide_favorites"] == true
2339 test "updates the user's show_role status", %{conn: conn} do
2340 user = insert(:user)
2344 |> assign(:user, user)
2345 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2347 assert user = json_response(conn, 200)
2348 assert user["source"]["pleroma"]["show_role"] == false
2351 test "updates the user's no_rich_text status", %{conn: conn} do
2352 user = insert(:user)
2356 |> assign(:user, user)
2357 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2359 assert user = json_response(conn, 200)
2360 assert user["source"]["pleroma"]["no_rich_text"] == true
2363 test "updates the user's name", %{conn: conn} do
2364 user = insert(:user)
2368 |> assign(:user, user)
2369 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2371 assert user = json_response(conn, 200)
2372 assert user["display_name"] == "markorepairs"
2375 test "updates the user's avatar", %{conn: conn} do
2376 user = insert(:user)
2378 new_avatar = %Plug.Upload{
2379 content_type: "image/jpg",
2380 path: Path.absname("test/fixtures/image.jpg"),
2381 filename: "an_image.jpg"
2386 |> assign(:user, user)
2387 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2389 assert user_response = json_response(conn, 200)
2390 assert user_response["avatar"] != User.avatar_url(user)
2393 test "updates the user's banner", %{conn: conn} do
2394 user = insert(:user)
2396 new_header = %Plug.Upload{
2397 content_type: "image/jpg",
2398 path: Path.absname("test/fixtures/image.jpg"),
2399 filename: "an_image.jpg"
2404 |> assign(:user, user)
2405 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2407 assert user_response = json_response(conn, 200)
2408 assert user_response["header"] != User.banner_url(user)
2411 test "requires 'write' permission", %{conn: conn} do
2412 token1 = insert(:oauth_token, scopes: ["read"])
2413 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2415 for token <- [token1, token2] do
2418 |> put_req_header("authorization", "Bearer #{token.token}")
2419 |> patch("/api/v1/accounts/update_credentials", %{})
2421 if token == token1 do
2422 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2424 assert json_response(conn, 200)
2429 test "updates profile emojos", %{conn: conn} do
2430 user = insert(:user)
2432 note = "*sips :blank:*"
2433 name = "I am :firefox:"
2437 |> assign(:user, user)
2438 |> patch("/api/v1/accounts/update_credentials", %{
2440 "display_name" => name
2443 assert json_response(conn, 200)
2447 |> get("/api/v1/accounts/#{user.id}")
2449 assert user = json_response(conn, 200)
2451 assert user["note"] == note
2452 assert user["display_name"] == name
2453 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2457 test "get instance information", %{conn: conn} do
2458 conn = get(conn, "/api/v1/instance")
2459 assert result = json_response(conn, 200)
2461 email = Pleroma.Config.get([:instance, :email])
2462 # Note: not checking for "max_toot_chars" since it's optional
2468 "email" => from_config_email,
2470 "streaming_api" => _
2475 "registrations" => _
2478 assert email == from_config_email
2481 test "get instance stats", %{conn: conn} do
2482 user = insert(:user, %{local: true})
2484 user2 = insert(:user, %{local: true})
2485 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2487 insert(:user, %{local: false, nickname: "u@peer1.com"})
2488 insert(:user, %{local: false, nickname: "u@peer2.com"})
2490 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2492 # Stats should count users with missing or nil `info.deactivated` value
2493 user = User.get_cached_by_id(user.id)
2494 info_change = Changeset.change(user.info, %{deactivated: nil})
2498 |> Changeset.change()
2499 |> Changeset.put_embed(:info, info_change)
2500 |> User.update_and_set_cache()
2502 Pleroma.Stats.update_stats()
2504 conn = get(conn, "/api/v1/instance")
2506 assert result = json_response(conn, 200)
2508 stats = result["stats"]
2511 assert stats["user_count"] == 1
2512 assert stats["status_count"] == 1
2513 assert stats["domain_count"] == 2
2516 test "get peers", %{conn: conn} do
2517 insert(:user, %{local: false, nickname: "u@peer1.com"})
2518 insert(:user, %{local: false, nickname: "u@peer2.com"})
2520 Pleroma.Stats.update_stats()
2522 conn = get(conn, "/api/v1/instance/peers")
2524 assert result = json_response(conn, 200)
2526 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2529 test "put settings", %{conn: conn} do
2530 user = insert(:user)
2534 |> assign(:user, user)
2535 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2537 assert _result = json_response(conn, 200)
2539 user = User.get_cached_by_ap_id(user.ap_id)
2540 assert user.info.settings == %{"programming" => "socks"}
2543 describe "pinned statuses" do
2545 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2547 user = insert(:user)
2548 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2550 [user: user, activity: activity]
2553 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2554 {:ok, _} = CommonAPI.pin(activity.id, user)
2558 |> assign(:user, user)
2559 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2560 |> json_response(200)
2562 id_str = to_string(activity.id)
2564 assert [%{"id" => ^id_str, "pinned" => true}] = result
2567 test "pin status", %{conn: conn, user: user, activity: activity} do
2568 id_str = to_string(activity.id)
2570 assert %{"id" => ^id_str, "pinned" => true} =
2572 |> assign(:user, user)
2573 |> post("/api/v1/statuses/#{activity.id}/pin")
2574 |> json_response(200)
2576 assert [%{"id" => ^id_str, "pinned" => true}] =
2578 |> assign(:user, user)
2579 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2580 |> json_response(200)
2583 test "unpin status", %{conn: conn, user: user, activity: activity} do
2584 {:ok, _} = CommonAPI.pin(activity.id, user)
2586 id_str = to_string(activity.id)
2587 user = refresh_record(user)
2589 assert %{"id" => ^id_str, "pinned" => false} =
2591 |> assign(:user, user)
2592 |> post("/api/v1/statuses/#{activity.id}/unpin")
2593 |> json_response(200)
2597 |> assign(:user, user)
2598 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2599 |> json_response(200)
2602 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2603 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2605 id_str_one = to_string(activity_one.id)
2607 assert %{"id" => ^id_str_one, "pinned" => true} =
2609 |> assign(:user, user)
2610 |> post("/api/v1/statuses/#{id_str_one}/pin")
2611 |> json_response(200)
2613 user = refresh_record(user)
2615 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2617 |> assign(:user, user)
2618 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2619 |> json_response(400)
2622 test "Status rich-media Card", %{conn: conn, user: user} do
2623 Pleroma.Config.put([:rich_media, :enabled], true)
2624 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2628 |> get("/api/v1/statuses/#{activity.id}/card")
2629 |> json_response(200)
2631 assert response == %{
2632 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2633 "provider_name" => "www.imdb.com",
2634 "provider_url" => "http://www.imdb.com",
2635 "title" => "The Rock",
2637 "url" => "http://www.imdb.com/title/tt0117500/",
2638 "description" => nil,
2641 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2642 "title" => "The Rock",
2643 "type" => "video.movie",
2644 "url" => "http://www.imdb.com/title/tt0117500/"
2649 # works with private posts
2651 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2655 |> assign(:user, user)
2656 |> get("/api/v1/statuses/#{activity.id}/card")
2657 |> json_response(200)
2659 assert response_two == response
2661 Pleroma.Config.put([:rich_media, :enabled], false)
2666 user = insert(:user)
2667 for_user = insert(:user)
2670 CommonAPI.post(user, %{
2671 "status" => "heweoo?"
2675 CommonAPI.post(user, %{
2676 "status" => "heweoo!"
2681 |> assign(:user, for_user)
2682 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2684 assert json_response(response1, 200)["bookmarked"] == true
2688 |> assign(:user, for_user)
2689 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2691 assert json_response(response2, 200)["bookmarked"] == true
2695 |> assign(:user, for_user)
2696 |> get("/api/v1/bookmarks")
2698 assert [json_response(response2, 200), json_response(response1, 200)] ==
2699 json_response(bookmarks, 200)
2703 |> assign(:user, for_user)
2704 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2706 assert json_response(response1, 200)["bookmarked"] == false
2710 |> assign(:user, for_user)
2711 |> get("/api/v1/bookmarks")
2713 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2716 describe "conversation muting" do
2718 user = insert(:user)
2719 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2721 [user: user, activity: activity]
2724 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2725 id_str = to_string(activity.id)
2727 assert %{"id" => ^id_str, "muted" => true} =
2729 |> assign(:user, user)
2730 |> post("/api/v1/statuses/#{activity.id}/mute")
2731 |> json_response(200)
2734 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2735 {:ok, _} = CommonAPI.add_mute(user, activity)
2737 id_str = to_string(activity.id)
2738 user = refresh_record(user)
2740 assert %{"id" => ^id_str, "muted" => false} =
2742 |> assign(:user, user)
2743 |> post("/api/v1/statuses/#{activity.id}/unmute")
2744 |> json_response(200)
2748 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2749 user = insert(:user)
2753 |> assign(:user, user)
2754 |> get("/api/v1/pleroma/flavour")
2756 assert "glitch" == json_response(get_old_flavour, 200)
2760 |> assign(:user, user)
2761 |> post("/api/v1/pleroma/flavour/vanilla")
2763 assert "vanilla" == json_response(set_flavour, 200)
2767 |> assign(:user, user)
2768 |> post("/api/v1/pleroma/flavour/vanilla")
2770 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2773 describe "reports" do
2775 reporter = insert(:user)
2776 target_user = insert(:user)
2778 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2780 [reporter: reporter, target_user: target_user, activity: activity]
2783 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2784 assert %{"action_taken" => false, "id" => _} =
2786 |> assign(:user, reporter)
2787 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2788 |> json_response(200)
2791 test "submit a report with statuses and comment", %{
2794 target_user: target_user,
2797 assert %{"action_taken" => false, "id" => _} =
2799 |> assign(:user, reporter)
2800 |> post("/api/v1/reports", %{
2801 "account_id" => target_user.id,
2802 "status_ids" => [activity.id],
2803 "comment" => "bad status!"
2805 |> json_response(200)
2808 test "account_id is required", %{
2813 assert %{"error" => "Valid `account_id` required"} =
2815 |> assign(:user, reporter)
2816 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2817 |> json_response(400)
2820 test "comment must be up to the size specified in the config", %{
2823 target_user: target_user
2825 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2826 comment = String.pad_trailing("a", max_size + 1, "a")
2828 error = %{"error" => "Comment must be up to #{max_size} characters"}
2832 |> assign(:user, reporter)
2833 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2834 |> json_response(400)
2838 describe "link headers" do
2839 test "preserves parameters in link headers", %{conn: conn} do
2840 user = insert(:user)
2841 other_user = insert(:user)
2844 CommonAPI.post(other_user, %{
2845 "status" => "hi @#{user.nickname}",
2846 "visibility" => "public"
2850 CommonAPI.post(other_user, %{
2851 "status" => "hi @#{user.nickname}",
2852 "visibility" => "public"
2855 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2856 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2860 |> assign(:user, user)
2861 |> get("/api/v1/notifications", %{media_only: true})
2863 assert [link_header] = get_resp_header(conn, "link")
2864 assert link_header =~ ~r/media_only=true/
2865 assert link_header =~ ~r/min_id=#{notification2.id}/
2866 assert link_header =~ ~r/max_id=#{notification1.id}/
2870 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2871 # Need to set an old-style integer ID to reproduce the problem
2872 # (these are no longer assigned to new accounts but were preserved
2873 # for existing accounts during the migration to flakeIDs)
2874 user_one = insert(:user, %{id: 1212})
2875 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2879 |> get("/api/v1/accounts/#{user_one.id}")
2883 |> get("/api/v1/accounts/#{user_two.nickname}")
2887 |> get("/api/v1/accounts/#{user_two.id}")
2889 acc_one = json_response(resp_one, 200)
2890 acc_two = json_response(resp_two, 200)
2891 acc_three = json_response(resp_three, 200)
2892 refute acc_one == acc_two
2893 assert acc_two == acc_three
2896 describe "custom emoji" do
2897 test "with tags", %{conn: conn} do
2900 |> get("/api/v1/custom_emojis")
2901 |> json_response(200)
2903 assert Map.has_key?(emoji, "shortcode")
2904 assert Map.has_key?(emoji, "static_url")
2905 assert Map.has_key?(emoji, "tags")
2906 assert is_list(emoji["tags"])
2907 assert Map.has_key?(emoji, "url")
2908 assert Map.has_key?(emoji, "visible_in_picker")
2912 describe "index/2 redirections" do
2913 setup %{conn: conn} do
2917 signing_salt: "cooldude"
2922 |> Plug.Session.call(Plug.Session.init(session_opts))
2925 test_path = "/web/statuses/test"
2926 %{conn: conn, path: test_path}
2929 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2930 conn = get(conn, path)
2932 assert conn.status == 302
2933 assert redirected_to(conn) == "/web/login"
2936 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2937 token = insert(:oauth_token)
2941 |> assign(:user, token.user)
2942 |> put_session(:oauth_token, token.token)
2945 assert conn.status == 200
2948 test "saves referer path to session", %{conn: conn, path: path} do
2949 conn = get(conn, path)
2950 return_to = Plug.Conn.get_session(conn, :return_to)
2952 assert return_to == path
2955 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2956 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2957 auth = insert(:oauth_authorization, app: app)
2961 |> put_session(:return_to, path)
2962 |> get("/web/login", %{code: auth.token})
2964 assert conn.status == 302
2965 assert redirected_to(conn) == path
2968 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2969 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2970 auth = insert(:oauth_authorization, app: app)
2972 conn = get(conn, "/web/login", %{code: auth.token})
2974 assert conn.status == 302
2975 assert redirected_to(conn) == "/web/getting-started"
2979 describe "scheduled activities" do
2980 test "creates a scheduled activity", %{conn: conn} do
2981 user = insert(:user)
2982 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2986 |> assign(:user, user)
2987 |> post("/api/v1/statuses", %{
2988 "status" => "scheduled",
2989 "scheduled_at" => scheduled_at
2992 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2993 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2994 assert [] == Repo.all(Activity)
2997 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2998 user = insert(:user)
2999 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3001 file = %Plug.Upload{
3002 content_type: "image/jpg",
3003 path: Path.absname("test/fixtures/image.jpg"),
3004 filename: "an_image.jpg"
3007 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3011 |> assign(:user, user)
3012 |> post("/api/v1/statuses", %{
3013 "media_ids" => [to_string(upload.id)],
3014 "status" => "scheduled",
3015 "scheduled_at" => scheduled_at
3018 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3019 assert %{"type" => "image"} = media_attachment
3022 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3024 user = insert(:user)
3027 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3031 |> assign(:user, user)
3032 |> post("/api/v1/statuses", %{
3033 "status" => "not scheduled",
3034 "scheduled_at" => scheduled_at
3037 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3038 assert [] == Repo.all(ScheduledActivity)
3041 test "returns error when daily user limit is exceeded", %{conn: conn} do
3042 user = insert(:user)
3045 NaiveDateTime.utc_now()
3046 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3047 |> NaiveDateTime.to_iso8601()
3049 attrs = %{params: %{}, scheduled_at: today}
3050 {:ok, _} = ScheduledActivity.create(user, attrs)
3051 {:ok, _} = ScheduledActivity.create(user, attrs)
3055 |> assign(:user, user)
3056 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3058 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3061 test "returns error when total user limit is exceeded", %{conn: conn} do
3062 user = insert(:user)
3065 NaiveDateTime.utc_now()
3066 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3067 |> NaiveDateTime.to_iso8601()
3070 NaiveDateTime.utc_now()
3071 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3072 |> NaiveDateTime.to_iso8601()
3074 attrs = %{params: %{}, scheduled_at: today}
3075 {:ok, _} = ScheduledActivity.create(user, attrs)
3076 {:ok, _} = ScheduledActivity.create(user, attrs)
3077 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3081 |> assign(:user, user)
3082 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3084 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3087 test "shows scheduled activities", %{conn: conn} do
3088 user = insert(:user)
3089 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3090 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3091 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3092 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3096 |> assign(:user, user)
3101 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3103 result = json_response(conn_res, 200)
3104 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3109 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3111 result = json_response(conn_res, 200)
3112 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3117 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3119 result = json_response(conn_res, 200)
3120 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3123 test "shows a scheduled activity", %{conn: conn} do
3124 user = insert(:user)
3125 scheduled_activity = insert(:scheduled_activity, user: user)
3129 |> assign(:user, user)
3130 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3132 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3133 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3137 |> assign(:user, user)
3138 |> get("/api/v1/scheduled_statuses/404")
3140 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3143 test "updates a scheduled activity", %{conn: conn} do
3144 user = insert(:user)
3145 scheduled_activity = insert(:scheduled_activity, user: user)
3148 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3152 |> assign(:user, user)
3153 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3154 scheduled_at: new_scheduled_at
3157 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3158 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3162 |> assign(:user, user)
3163 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3165 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3168 test "deletes a scheduled activity", %{conn: conn} do
3169 user = insert(:user)
3170 scheduled_activity = insert(:scheduled_activity, user: user)
3174 |> assign(:user, user)
3175 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3177 assert %{} = json_response(res_conn, 200)
3178 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3182 |> assign(:user, user)
3183 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3185 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3189 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3190 user1 = insert(:user)
3191 user2 = insert(:user)
3192 user3 = insert(:user)
3194 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3196 # Reply to status from another user
3199 |> assign(:user, user2)
3200 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3202 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3204 activity = Activity.get_by_id_with_object(id)
3206 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3207 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3209 # Reblog from the third user
3212 |> assign(:user, user3)
3213 |> post("/api/v1/statuses/#{activity.id}/reblog")
3215 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3216 json_response(conn2, 200)
3218 assert to_string(activity.id) == id
3220 # Getting third user status
3223 |> assign(:user, user3)
3224 |> get("api/v1/timelines/home")
3226 [reblogged_activity] = json_response(conn3, 200)
3228 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3230 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3231 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3234 describe "create account by app" do
3236 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3237 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3238 interval = Pleroma.Config.get([:app_account_creation, :interval])
3240 Pleroma.Config.put([:app_account_creation, :enabled], true)
3241 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3242 Pleroma.Config.put([:app_account_creation, :interval], 1)
3245 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3246 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3247 Pleroma.Config.put([:app_account_creation, :interval], interval)
3253 test "Account registration via Application", %{conn: conn} do
3256 |> post("/api/v1/apps", %{
3257 client_name: "client_name",
3258 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3259 scopes: "read, write, follow"
3263 "client_id" => client_id,
3264 "client_secret" => client_secret,
3266 "name" => "client_name",
3267 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3270 } = json_response(conn, 200)
3274 |> post("/oauth/token", %{
3275 grant_type: "client_credentials",
3276 client_id: client_id,
3277 client_secret: client_secret
3280 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3281 json_response(conn, 200)
3284 token_from_db = Repo.get_by(Token, token: token)
3285 assert token_from_db
3287 assert scope == "read write follow"
3291 |> put_req_header("authorization", "Bearer " <> token)
3292 |> post("/api/v1/accounts", %{
3294 email: "lain@example.org",
3295 password: "PlzDontHackLain",
3300 "access_token" => token,
3301 "created_at" => _created_at,
3303 "token_type" => "Bearer"
3304 } = json_response(conn, 200)
3306 token_from_db = Repo.get_by(Token, token: token)
3307 assert token_from_db
3308 token_from_db = Repo.preload(token_from_db, :user)
3309 assert token_from_db.user
3311 assert token_from_db.user.info.confirmation_pending
3314 test "rate limit", %{conn: conn} do
3315 app_token = insert(:oauth_token, user: nil)
3318 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3319 |> Map.put(:remote_ip, {15, 15, 15, 15})
3324 |> post("/api/v1/accounts", %{
3325 username: "#{i}lain",
3326 email: "#{i}lain@example.org",
3327 password: "PlzDontHackLain",
3332 "access_token" => token,
3333 "created_at" => _created_at,
3335 "token_type" => "Bearer"
3336 } = json_response(conn, 200)
3338 token_from_db = Repo.get_by(Token, token: token)
3339 assert token_from_db
3340 token_from_db = Repo.preload(token_from_db, :user)
3341 assert token_from_db.user
3343 assert token_from_db.user.info.confirmation_pending
3348 |> post("/api/v1/accounts", %{
3350 email: "6lain@example.org",
3351 password: "PlzDontHackLain",
3355 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}