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)
320 user_three = insert(:user)
322 {:ok, user_two} = User.follow(user_two, user_one)
325 CommonAPI.post(user_one, %{
326 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
327 "visibility" => "direct"
330 {:ok, _follower_only} =
331 CommonAPI.post(user_one, %{
332 "status" => "Hi @#{user_two.nickname}!",
333 "visibility" => "private"
338 |> assign(:user, user_one)
339 |> get("/api/v1/conversations")
341 assert response = json_response(res_conn, 200)
346 "accounts" => res_accounts,
347 "last_status" => res_last_status,
352 account_ids = Enum.map(res_accounts, & &1["id"])
353 assert length(res_accounts) == 2
354 assert user_two.id in account_ids
355 assert user_three.id in account_ids
356 assert is_binary(res_id)
357 assert unread == true
358 assert res_last_status["id"] == direct.id
360 # Apparently undocumented API endpoint
363 |> assign(:user, user_one)
364 |> post("/api/v1/conversations/#{res_id}/read")
366 assert response = json_response(res_conn, 200)
367 assert length(response["accounts"]) == 2
368 assert response["last_status"]["id"] == direct.id
369 assert response["unread"] == false
371 # (vanilla) Mastodon frontend behaviour
374 |> assign(:user, user_one)
375 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
377 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
380 test "doesn't include DMs from blocked users", %{conn: conn} do
381 blocker = insert(:user)
382 blocked = insert(:user)
384 {:ok, blocker} = User.block(blocker, blocked)
386 {:ok, _blocked_direct} =
387 CommonAPI.post(blocked, %{
388 "status" => "Hi @#{blocker.nickname}!",
389 "visibility" => "direct"
393 CommonAPI.post(user, %{
394 "status" => "Hi @#{blocker.nickname}!",
395 "visibility" => "direct"
400 |> assign(:user, user)
401 |> get("api/v1/timelines/direct")
403 [status] = json_response(res_conn, 200)
404 assert status["id"] == direct.id
407 test "replying to a status", %{conn: conn} do
410 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
414 |> assign(:user, user)
415 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
417 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
419 activity = Activity.get_by_id(id)
421 assert activity.data["context"] == replied_to.data["context"]
422 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
425 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
430 |> assign(:user, user)
431 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
433 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
435 activity = Activity.get_by_id(id)
440 test "verify_credentials", %{conn: conn} do
445 |> assign(:user, user)
446 |> get("/api/v1/accounts/verify_credentials")
448 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
449 assert id == to_string(user.id)
452 test "verify_credentials default scope unlisted", %{conn: conn} do
453 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
457 |> assign(:user, user)
458 |> get("/api/v1/accounts/verify_credentials")
460 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
461 assert id == to_string(user.id)
464 test "apps/verify_credentials", %{conn: conn} do
465 token = insert(:oauth_token)
469 |> assign(:user, token.user)
470 |> assign(:token, token)
471 |> get("/api/v1/apps/verify_credentials")
473 app = Repo.preload(token, :app).app
476 "name" => app.client_name,
477 "website" => app.website,
478 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
481 assert expected == json_response(conn, 200)
484 test "creates an oauth app", %{conn: conn} do
486 app_attrs = build(:oauth_app)
490 |> assign(:user, user)
491 |> post("/api/v1/apps", %{
492 client_name: app_attrs.client_name,
493 redirect_uris: app_attrs.redirect_uris
496 [app] = Repo.all(App)
499 "name" => app.client_name,
500 "website" => app.website,
501 "client_id" => app.client_id,
502 "client_secret" => app.client_secret,
503 "id" => app.id |> to_string(),
504 "redirect_uri" => app.redirect_uris,
505 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
508 assert expected == json_response(conn, 200)
511 test "get a status", %{conn: conn} do
512 activity = insert(:note_activity)
516 |> get("/api/v1/statuses/#{activity.id}")
518 assert %{"id" => id} = json_response(conn, 200)
519 assert id == to_string(activity.id)
522 describe "deleting a status" do
523 test "when you created it", %{conn: conn} do
524 activity = insert(:note_activity)
525 author = User.get_cached_by_ap_id(activity.data["actor"])
529 |> assign(:user, author)
530 |> delete("/api/v1/statuses/#{activity.id}")
532 assert %{} = json_response(conn, 200)
534 refute Activity.get_by_id(activity.id)
537 test "when you didn't create it", %{conn: conn} do
538 activity = insert(:note_activity)
543 |> assign(:user, user)
544 |> delete("/api/v1/statuses/#{activity.id}")
546 assert %{"error" => _} = json_response(conn, 403)
548 assert Activity.get_by_id(activity.id) == activity
551 test "when you're an admin or moderator", %{conn: conn} do
552 activity1 = insert(:note_activity)
553 activity2 = insert(:note_activity)
554 admin = insert(:user, info: %{is_admin: true})
555 moderator = insert(:user, info: %{is_moderator: true})
559 |> assign(:user, admin)
560 |> delete("/api/v1/statuses/#{activity1.id}")
562 assert %{} = json_response(res_conn, 200)
566 |> assign(:user, moderator)
567 |> delete("/api/v1/statuses/#{activity2.id}")
569 assert %{} = json_response(res_conn, 200)
571 refute Activity.get_by_id(activity1.id)
572 refute Activity.get_by_id(activity2.id)
576 describe "filters" do
577 test "creating a filter", %{conn: conn} do
580 filter = %Pleroma.Filter{
587 |> assign(:user, user)
588 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
590 assert response = json_response(conn, 200)
591 assert response["phrase"] == filter.phrase
592 assert response["context"] == filter.context
593 assert response["irreversible"] == false
594 assert response["id"] != nil
595 assert response["id"] != ""
598 test "fetching a list of filters", %{conn: conn} do
601 query_one = %Pleroma.Filter{
608 query_two = %Pleroma.Filter{
615 {:ok, filter_one} = Pleroma.Filter.create(query_one)
616 {:ok, filter_two} = Pleroma.Filter.create(query_two)
620 |> assign(:user, user)
621 |> get("/api/v1/filters")
622 |> json_response(200)
628 filters: [filter_two, filter_one]
632 test "get a filter", %{conn: conn} do
635 query = %Pleroma.Filter{
642 {:ok, filter} = Pleroma.Filter.create(query)
646 |> assign(:user, user)
647 |> get("/api/v1/filters/#{filter.filter_id}")
649 assert _response = json_response(conn, 200)
652 test "update a filter", %{conn: conn} do
655 query = %Pleroma.Filter{
662 {:ok, _filter} = Pleroma.Filter.create(query)
664 new = %Pleroma.Filter{
671 |> assign(:user, user)
672 |> put("/api/v1/filters/#{query.filter_id}", %{
677 assert response = json_response(conn, 200)
678 assert response["phrase"] == new.phrase
679 assert response["context"] == new.context
682 test "delete a filter", %{conn: conn} do
685 query = %Pleroma.Filter{
692 {:ok, filter} = Pleroma.Filter.create(query)
696 |> assign(:user, user)
697 |> delete("/api/v1/filters/#{filter.filter_id}")
699 assert response = json_response(conn, 200)
700 assert response == %{}
705 test "creating a list", %{conn: conn} do
710 |> assign(:user, user)
711 |> post("/api/v1/lists", %{"title" => "cuties"})
713 assert %{"title" => title} = json_response(conn, 200)
714 assert title == "cuties"
717 test "adding users to a list", %{conn: conn} do
719 other_user = insert(:user)
720 {:ok, list} = Pleroma.List.create("name", user)
724 |> assign(:user, user)
725 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
727 assert %{} == json_response(conn, 200)
728 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
729 assert following == [other_user.follower_address]
732 test "removing users from a list", %{conn: conn} do
734 other_user = insert(:user)
735 third_user = insert(:user)
736 {:ok, list} = Pleroma.List.create("name", user)
737 {:ok, list} = Pleroma.List.follow(list, other_user)
738 {:ok, list} = Pleroma.List.follow(list, third_user)
742 |> assign(:user, user)
743 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
745 assert %{} == json_response(conn, 200)
746 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
747 assert following == [third_user.follower_address]
750 test "listing users in a list", %{conn: conn} do
752 other_user = insert(:user)
753 {:ok, list} = Pleroma.List.create("name", user)
754 {:ok, list} = Pleroma.List.follow(list, other_user)
758 |> assign(:user, user)
759 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
761 assert [%{"id" => id}] = json_response(conn, 200)
762 assert id == to_string(other_user.id)
765 test "retrieving a list", %{conn: conn} do
767 {:ok, list} = Pleroma.List.create("name", user)
771 |> assign(:user, user)
772 |> get("/api/v1/lists/#{list.id}")
774 assert %{"id" => id} = json_response(conn, 200)
775 assert id == to_string(list.id)
778 test "renaming a list", %{conn: conn} do
780 {:ok, list} = Pleroma.List.create("name", user)
784 |> assign(:user, user)
785 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
787 assert %{"title" => name} = json_response(conn, 200)
788 assert name == "newname"
791 test "deleting a list", %{conn: conn} do
793 {:ok, list} = Pleroma.List.create("name", user)
797 |> assign(:user, user)
798 |> delete("/api/v1/lists/#{list.id}")
800 assert %{} = json_response(conn, 200)
801 assert is_nil(Repo.get(Pleroma.List, list.id))
804 test "list timeline", %{conn: conn} do
806 other_user = insert(:user)
807 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
808 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
809 {:ok, list} = Pleroma.List.create("name", user)
810 {:ok, list} = Pleroma.List.follow(list, other_user)
814 |> assign(:user, user)
815 |> get("/api/v1/timelines/list/#{list.id}")
817 assert [%{"id" => id}] = json_response(conn, 200)
819 assert id == to_string(activity_two.id)
822 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
824 other_user = insert(:user)
825 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
827 {:ok, _activity_two} =
828 TwitterAPI.create_status(other_user, %{
829 "status" => "Marisa is cute.",
830 "visibility" => "private"
833 {:ok, list} = Pleroma.List.create("name", user)
834 {:ok, list} = Pleroma.List.follow(list, other_user)
838 |> assign(:user, user)
839 |> get("/api/v1/timelines/list/#{list.id}")
841 assert [%{"id" => id}] = json_response(conn, 200)
843 assert id == to_string(activity_one.id)
847 describe "notifications" do
848 test "list of notifications", %{conn: conn} do
850 other_user = insert(:user)
853 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
855 {:ok, [_notification]} = Notification.create_notifications(activity)
859 |> assign(:user, user)
860 |> get("/api/v1/notifications")
863 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
865 }\">@<span>#{user.nickname}</span></a></span>"
867 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
868 assert response == expected_response
871 test "getting a single notification", %{conn: conn} do
873 other_user = insert(:user)
876 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
878 {:ok, [notification]} = Notification.create_notifications(activity)
882 |> assign(:user, user)
883 |> get("/api/v1/notifications/#{notification.id}")
886 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
888 }\">@<span>#{user.nickname}</span></a></span>"
890 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
891 assert response == expected_response
894 test "dismissing a single notification", %{conn: conn} do
896 other_user = insert(:user)
899 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
901 {:ok, [notification]} = Notification.create_notifications(activity)
905 |> assign(:user, user)
906 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
908 assert %{} = json_response(conn, 200)
911 test "clearing all notifications", %{conn: conn} do
913 other_user = insert(:user)
916 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
918 {:ok, [_notification]} = Notification.create_notifications(activity)
922 |> assign(:user, user)
923 |> post("/api/v1/notifications/clear")
925 assert %{} = json_response(conn, 200)
929 |> assign(:user, user)
930 |> get("/api/v1/notifications")
932 assert all = json_response(conn, 200)
936 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
938 other_user = insert(:user)
940 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
941 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
942 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
943 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
945 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
946 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
947 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
948 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
952 |> assign(:user, user)
957 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
959 result = json_response(conn_res, 200)
960 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
965 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
967 result = json_response(conn_res, 200)
968 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
973 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
975 result = json_response(conn_res, 200)
976 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
979 test "filters notifications using exclude_types", %{conn: conn} do
981 other_user = insert(:user)
983 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
984 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
985 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
986 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
987 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
989 mention_notification_id =
990 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
992 favorite_notification_id =
993 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
995 reblog_notification_id =
996 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
998 follow_notification_id =
999 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1003 |> assign(:user, user)
1006 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1008 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1011 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1013 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1016 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1018 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1021 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1023 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1026 test "destroy multiple", %{conn: conn} do
1027 user = insert(:user)
1028 other_user = insert(:user)
1030 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1031 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1032 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1033 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1035 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1036 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1037 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1038 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1042 |> assign(:user, user)
1046 |> get("/api/v1/notifications")
1048 result = json_response(conn_res, 200)
1049 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1053 |> assign(:user, other_user)
1057 |> get("/api/v1/notifications")
1059 result = json_response(conn_res, 200)
1060 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1064 |> delete("/api/v1/notifications/destroy_multiple", %{
1065 "ids" => [notification1_id, notification2_id]
1068 assert json_response(conn_destroy, 200) == %{}
1072 |> get("/api/v1/notifications")
1074 result = json_response(conn_res, 200)
1075 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1079 describe "reblogging" do
1080 test "reblogs and returns the reblogged status", %{conn: conn} do
1081 activity = insert(:note_activity)
1082 user = insert(:user)
1086 |> assign(:user, user)
1087 |> post("/api/v1/statuses/#{activity.id}/reblog")
1090 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1092 } = json_response(conn, 200)
1094 assert to_string(activity.id) == id
1097 test "reblogged status for another user", %{conn: conn} do
1098 activity = insert(:note_activity)
1099 user1 = insert(:user)
1100 user2 = insert(:user)
1101 user3 = insert(:user)
1102 CommonAPI.favorite(activity.id, user2)
1103 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1104 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1105 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1109 |> assign(:user, user3)
1110 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1113 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1114 "reblogged" => false,
1115 "favourited" => false,
1116 "bookmarked" => false
1117 } = json_response(conn_res, 200)
1121 |> assign(:user, user2)
1122 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1125 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1126 "reblogged" => true,
1127 "favourited" => true,
1128 "bookmarked" => true
1129 } = json_response(conn_res, 200)
1131 assert to_string(activity.id) == id
1135 describe "unreblogging" do
1136 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1137 activity = insert(:note_activity)
1138 user = insert(:user)
1140 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1144 |> assign(:user, user)
1145 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1147 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1149 assert to_string(activity.id) == id
1153 describe "favoriting" do
1154 test "favs a status and returns it", %{conn: conn} do
1155 activity = insert(:note_activity)
1156 user = insert(:user)
1160 |> assign(:user, user)
1161 |> post("/api/v1/statuses/#{activity.id}/favourite")
1163 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1164 json_response(conn, 200)
1166 assert to_string(activity.id) == id
1169 test "returns 500 for a wrong id", %{conn: conn} do
1170 user = insert(:user)
1174 |> assign(:user, user)
1175 |> post("/api/v1/statuses/1/favourite")
1176 |> json_response(500)
1178 assert resp == "Something went wrong"
1182 describe "unfavoriting" do
1183 test "unfavorites a status and returns it", %{conn: conn} do
1184 activity = insert(:note_activity)
1185 user = insert(:user)
1187 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1191 |> assign(:user, user)
1192 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1194 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1195 json_response(conn, 200)
1197 assert to_string(activity.id) == id
1201 describe "user timelines" do
1202 test "gets a users statuses", %{conn: conn} do
1203 user_one = insert(:user)
1204 user_two = insert(:user)
1205 user_three = insert(:user)
1207 {:ok, user_three} = User.follow(user_three, user_one)
1209 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1211 {:ok, direct_activity} =
1212 CommonAPI.post(user_one, %{
1213 "status" => "Hi, @#{user_two.nickname}.",
1214 "visibility" => "direct"
1217 {:ok, private_activity} =
1218 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1222 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1224 assert [%{"id" => id}] = json_response(resp, 200)
1225 assert id == to_string(activity.id)
1229 |> assign(:user, user_two)
1230 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1232 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1233 assert id_one == to_string(direct_activity.id)
1234 assert id_two == to_string(activity.id)
1238 |> assign(:user, user_three)
1239 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1241 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1242 assert id_one == to_string(private_activity.id)
1243 assert id_two == to_string(activity.id)
1246 test "unimplemented pinned statuses feature", %{conn: conn} do
1247 note = insert(:note_activity)
1248 user = User.get_cached_by_ap_id(note.data["actor"])
1252 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1254 assert json_response(conn, 200) == []
1257 test "gets an users media", %{conn: conn} do
1258 note = insert(:note_activity)
1259 user = User.get_cached_by_ap_id(note.data["actor"])
1261 file = %Plug.Upload{
1262 content_type: "image/jpg",
1263 path: Path.absname("test/fixtures/image.jpg"),
1264 filename: "an_image.jpg"
1268 TwitterAPI.upload(file, user, "json")
1272 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1276 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1278 assert [%{"id" => id}] = json_response(conn, 200)
1279 assert id == to_string(image_post.id)
1283 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1285 assert [%{"id" => id}] = json_response(conn, 200)
1286 assert id == to_string(image_post.id)
1289 test "gets a user's statuses without reblogs", %{conn: conn} do
1290 user = insert(:user)
1291 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1292 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1296 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1298 assert [%{"id" => id}] = json_response(conn, 200)
1299 assert id == to_string(post.id)
1303 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1305 assert [%{"id" => id}] = json_response(conn, 200)
1306 assert id == to_string(post.id)
1310 describe "user relationships" do
1311 test "returns the relationships for the current user", %{conn: conn} do
1312 user = insert(:user)
1313 other_user = insert(:user)
1314 {:ok, user} = User.follow(user, other_user)
1318 |> assign(:user, user)
1319 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1321 assert [relationship] = json_response(conn, 200)
1323 assert to_string(other_user.id) == relationship["id"]
1327 describe "locked accounts" do
1328 test "/api/v1/follow_requests works" do
1329 user = insert(:user, %{info: %User.Info{locked: true}})
1330 other_user = insert(:user)
1332 {:ok, _activity} = ActivityPub.follow(other_user, user)
1334 user = User.get_cached_by_id(user.id)
1335 other_user = User.get_cached_by_id(other_user.id)
1337 assert User.following?(other_user, user) == false
1341 |> assign(:user, user)
1342 |> get("/api/v1/follow_requests")
1344 assert [relationship] = json_response(conn, 200)
1345 assert to_string(other_user.id) == relationship["id"]
1348 test "/api/v1/follow_requests/:id/authorize works" do
1349 user = insert(:user, %{info: %User.Info{locked: true}})
1350 other_user = insert(:user)
1352 {:ok, _activity} = ActivityPub.follow(other_user, user)
1354 user = User.get_cached_by_id(user.id)
1355 other_user = User.get_cached_by_id(other_user.id)
1357 assert User.following?(other_user, user) == false
1361 |> assign(:user, user)
1362 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1364 assert relationship = json_response(conn, 200)
1365 assert to_string(other_user.id) == relationship["id"]
1367 user = User.get_cached_by_id(user.id)
1368 other_user = User.get_cached_by_id(other_user.id)
1370 assert User.following?(other_user, user) == true
1373 test "verify_credentials", %{conn: conn} do
1374 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1378 |> assign(:user, user)
1379 |> get("/api/v1/accounts/verify_credentials")
1381 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1382 assert id == to_string(user.id)
1385 test "/api/v1/follow_requests/:id/reject works" do
1386 user = insert(:user, %{info: %User.Info{locked: true}})
1387 other_user = insert(:user)
1389 {:ok, _activity} = ActivityPub.follow(other_user, user)
1391 user = User.get_cached_by_id(user.id)
1395 |> assign(:user, user)
1396 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1398 assert relationship = json_response(conn, 200)
1399 assert to_string(other_user.id) == relationship["id"]
1401 user = User.get_cached_by_id(user.id)
1402 other_user = User.get_cached_by_id(other_user.id)
1404 assert User.following?(other_user, user) == false
1408 test "account fetching", %{conn: conn} do
1409 user = insert(:user)
1413 |> get("/api/v1/accounts/#{user.id}")
1415 assert %{"id" => id} = json_response(conn, 200)
1416 assert id == to_string(user.id)
1420 |> get("/api/v1/accounts/-1")
1422 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1425 test "account fetching also works nickname", %{conn: conn} do
1426 user = insert(:user)
1430 |> get("/api/v1/accounts/#{user.nickname}")
1432 assert %{"id" => id} = json_response(conn, 200)
1433 assert id == user.id
1436 test "media upload", %{conn: conn} do
1437 file = %Plug.Upload{
1438 content_type: "image/jpg",
1439 path: Path.absname("test/fixtures/image.jpg"),
1440 filename: "an_image.jpg"
1443 desc = "Description of the image"
1445 user = insert(:user)
1449 |> assign(:user, user)
1450 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1452 assert media = json_response(conn, 200)
1454 assert media["type"] == "image"
1455 assert media["description"] == desc
1458 object = Repo.get(Object, media["id"])
1459 assert object.data["actor"] == User.ap_id(user)
1462 test "mascot upload", %{conn: conn} do
1463 user = insert(:user)
1465 non_image_file = %Plug.Upload{
1466 content_type: "audio/mpeg",
1467 path: Path.absname("test/fixtures/sound.mp3"),
1468 filename: "sound.mp3"
1473 |> assign(:user, user)
1474 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1476 assert json_response(conn, 415)
1478 file = %Plug.Upload{
1479 content_type: "image/jpg",
1480 path: Path.absname("test/fixtures/image.jpg"),
1481 filename: "an_image.jpg"
1486 |> assign(:user, user)
1487 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1489 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1492 test "mascot retrieving", %{conn: conn} do
1493 user = insert(:user)
1494 # When user hasn't set a mascot, we should just get pleroma tan back
1497 |> assign(:user, user)
1498 |> get("/api/v1/pleroma/mascot")
1500 assert %{"url" => url} = json_response(conn, 200)
1501 assert url =~ "pleroma-fox-tan-smol"
1503 # When a user sets their mascot, we should get that back
1504 file = %Plug.Upload{
1505 content_type: "image/jpg",
1506 path: Path.absname("test/fixtures/image.jpg"),
1507 filename: "an_image.jpg"
1512 |> assign(:user, user)
1513 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1515 assert json_response(conn, 200)
1517 user = User.get_cached_by_id(user.id)
1521 |> assign(:user, user)
1522 |> get("/api/v1/pleroma/mascot")
1524 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1525 assert url =~ "an_image"
1528 test "hashtag timeline", %{conn: conn} do
1529 following = insert(:user)
1532 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1534 {:ok, [_activity]} =
1535 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1539 |> get("/api/v1/timelines/tag/2hu")
1541 assert [%{"id" => id}] = json_response(nconn, 200)
1543 assert id == to_string(activity.id)
1545 # works for different capitalization too
1548 |> get("/api/v1/timelines/tag/2HU")
1550 assert [%{"id" => id}] = json_response(nconn, 200)
1552 assert id == to_string(activity.id)
1556 test "multi-hashtag timeline", %{conn: conn} do
1557 user = insert(:user)
1559 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1560 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1561 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1565 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1567 [status_none, status_test1, status_test] = json_response(any_test, 200)
1569 assert to_string(activity_test.id) == status_test["id"]
1570 assert to_string(activity_test1.id) == status_test1["id"]
1571 assert to_string(activity_none.id) == status_none["id"]
1575 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1577 assert [status_test1] == json_response(restricted_test, 200)
1579 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1581 assert [status_none] == json_response(all_test, 200)
1584 test "getting followers", %{conn: conn} do
1585 user = insert(:user)
1586 other_user = insert(:user)
1587 {:ok, user} = User.follow(user, other_user)
1591 |> get("/api/v1/accounts/#{other_user.id}/followers")
1593 assert [%{"id" => id}] = json_response(conn, 200)
1594 assert id == to_string(user.id)
1597 test "getting followers, hide_followers", %{conn: conn} do
1598 user = insert(:user)
1599 other_user = insert(:user, %{info: %{hide_followers: true}})
1600 {:ok, _user} = User.follow(user, other_user)
1604 |> get("/api/v1/accounts/#{other_user.id}/followers")
1606 assert [] == json_response(conn, 200)
1609 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1610 user = insert(:user)
1611 other_user = insert(:user, %{info: %{hide_followers: true}})
1612 {:ok, _user} = User.follow(user, other_user)
1616 |> assign(:user, other_user)
1617 |> get("/api/v1/accounts/#{other_user.id}/followers")
1619 refute [] == json_response(conn, 200)
1622 test "getting followers, pagination", %{conn: conn} do
1623 user = insert(:user)
1624 follower1 = insert(:user)
1625 follower2 = insert(:user)
1626 follower3 = insert(:user)
1627 {:ok, _} = User.follow(follower1, user)
1628 {:ok, _} = User.follow(follower2, user)
1629 {:ok, _} = User.follow(follower3, user)
1633 |> assign(:user, user)
1637 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1639 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1640 assert id3 == follower3.id
1641 assert id2 == follower2.id
1645 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1647 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1648 assert id2 == follower2.id
1649 assert id1 == follower1.id
1653 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1655 assert [%{"id" => id2}] = json_response(res_conn, 200)
1656 assert id2 == follower2.id
1658 assert [link_header] = get_resp_header(res_conn, "link")
1659 assert link_header =~ ~r/min_id=#{follower2.id}/
1660 assert link_header =~ ~r/max_id=#{follower2.id}/
1663 test "getting following", %{conn: conn} do
1664 user = insert(:user)
1665 other_user = insert(:user)
1666 {:ok, user} = User.follow(user, other_user)
1670 |> get("/api/v1/accounts/#{user.id}/following")
1672 assert [%{"id" => id}] = json_response(conn, 200)
1673 assert id == to_string(other_user.id)
1676 test "getting following, hide_follows", %{conn: conn} do
1677 user = insert(:user, %{info: %{hide_follows: true}})
1678 other_user = insert(:user)
1679 {:ok, user} = User.follow(user, other_user)
1683 |> get("/api/v1/accounts/#{user.id}/following")
1685 assert [] == json_response(conn, 200)
1688 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1689 user = insert(:user, %{info: %{hide_follows: true}})
1690 other_user = insert(:user)
1691 {:ok, user} = User.follow(user, other_user)
1695 |> assign(:user, user)
1696 |> get("/api/v1/accounts/#{user.id}/following")
1698 refute [] == json_response(conn, 200)
1701 test "getting following, pagination", %{conn: conn} do
1702 user = insert(:user)
1703 following1 = insert(:user)
1704 following2 = insert(:user)
1705 following3 = insert(:user)
1706 {:ok, _} = User.follow(user, following1)
1707 {:ok, _} = User.follow(user, following2)
1708 {:ok, _} = User.follow(user, following3)
1712 |> assign(:user, user)
1716 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1718 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1719 assert id3 == following3.id
1720 assert id2 == following2.id
1724 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1726 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1727 assert id2 == following2.id
1728 assert id1 == following1.id
1732 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1734 assert [%{"id" => id2}] = json_response(res_conn, 200)
1735 assert id2 == following2.id
1737 assert [link_header] = get_resp_header(res_conn, "link")
1738 assert link_header =~ ~r/min_id=#{following2.id}/
1739 assert link_header =~ ~r/max_id=#{following2.id}/
1742 test "following / unfollowing a user", %{conn: conn} do
1743 user = insert(:user)
1744 other_user = insert(:user)
1748 |> assign(:user, user)
1749 |> post("/api/v1/accounts/#{other_user.id}/follow")
1751 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1753 user = User.get_cached_by_id(user.id)
1757 |> assign(:user, user)
1758 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1760 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1762 user = User.get_cached_by_id(user.id)
1766 |> assign(:user, user)
1767 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1769 assert %{"id" => id} = json_response(conn, 200)
1770 assert id == to_string(other_user.id)
1773 test "following without reblogs" do
1774 follower = insert(:user)
1775 followed = insert(:user)
1776 other_user = insert(:user)
1780 |> assign(:user, follower)
1781 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1783 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1785 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1786 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1790 |> assign(:user, User.get_cached_by_id(follower.id))
1791 |> get("/api/v1/timelines/home")
1793 assert [] == json_response(conn, 200)
1797 |> assign(:user, follower)
1798 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1800 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1804 |> assign(:user, User.get_cached_by_id(follower.id))
1805 |> get("/api/v1/timelines/home")
1807 expected_activity_id = reblog.id
1808 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1811 test "following / unfollowing errors" do
1812 user = insert(:user)
1816 |> assign(:user, user)
1819 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1820 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1823 user = User.get_cached_by_id(user.id)
1824 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1825 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1827 # self follow via uri
1828 user = User.get_cached_by_id(user.id)
1829 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1830 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1832 # follow non existing user
1833 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1834 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1836 # follow non existing user via uri
1837 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1838 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1840 # unfollow non existing user
1841 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1842 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1845 test "muting / unmuting a user", %{conn: conn} do
1846 user = insert(:user)
1847 other_user = insert(:user)
1851 |> assign(:user, user)
1852 |> post("/api/v1/accounts/#{other_user.id}/mute")
1854 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1856 user = User.get_cached_by_id(user.id)
1860 |> assign(:user, user)
1861 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1863 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1866 test "subscribing / unsubscribing to a user", %{conn: conn} do
1867 user = insert(:user)
1868 subscription_target = insert(:user)
1872 |> assign(:user, user)
1873 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1875 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1879 |> assign(:user, user)
1880 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1882 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1885 test "getting a list of mutes", %{conn: conn} do
1886 user = insert(:user)
1887 other_user = insert(:user)
1889 {:ok, user} = User.mute(user, other_user)
1893 |> assign(:user, user)
1894 |> get("/api/v1/mutes")
1896 other_user_id = to_string(other_user.id)
1897 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1900 test "blocking / unblocking a user", %{conn: conn} do
1901 user = insert(:user)
1902 other_user = insert(:user)
1906 |> assign(:user, user)
1907 |> post("/api/v1/accounts/#{other_user.id}/block")
1909 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1911 user = User.get_cached_by_id(user.id)
1915 |> assign(:user, user)
1916 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1918 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1921 test "getting a list of blocks", %{conn: conn} do
1922 user = insert(:user)
1923 other_user = insert(:user)
1925 {:ok, user} = User.block(user, other_user)
1929 |> assign(:user, user)
1930 |> get("/api/v1/blocks")
1932 other_user_id = to_string(other_user.id)
1933 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1936 test "blocking / unblocking a domain", %{conn: conn} do
1937 user = insert(:user)
1938 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1942 |> assign(:user, user)
1943 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1945 assert %{} = json_response(conn, 200)
1946 user = User.get_cached_by_ap_id(user.ap_id)
1947 assert User.blocks?(user, other_user)
1951 |> assign(:user, user)
1952 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1954 assert %{} = json_response(conn, 200)
1955 user = User.get_cached_by_ap_id(user.ap_id)
1956 refute User.blocks?(user, other_user)
1959 test "getting a list of domain blocks", %{conn: conn} do
1960 user = insert(:user)
1962 {:ok, user} = User.block_domain(user, "bad.site")
1963 {:ok, user} = User.block_domain(user, "even.worse.site")
1967 |> assign(:user, user)
1968 |> get("/api/v1/domain_blocks")
1970 domain_blocks = json_response(conn, 200)
1972 assert "bad.site" in domain_blocks
1973 assert "even.worse.site" in domain_blocks
1976 test "unimplemented follow_requests, blocks, domain blocks" do
1977 user = insert(:user)
1979 ["blocks", "domain_blocks", "follow_requests"]
1980 |> Enum.each(fn endpoint ->
1983 |> assign(:user, user)
1984 |> get("/api/v1/#{endpoint}")
1986 assert [] = json_response(conn, 200)
1990 test "account search", %{conn: conn} do
1991 user = insert(:user)
1992 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1993 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1997 |> assign(:user, user)
1998 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1999 |> json_response(200)
2001 result_ids = for result <- results, do: result["acct"]
2003 assert user_two.nickname in result_ids
2004 assert user_three.nickname in result_ids
2008 |> assign(:user, user)
2009 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
2010 |> json_response(200)
2012 result_ids = for result <- results, do: result["acct"]
2014 assert user_three.nickname in result_ids
2017 test "search", %{conn: conn} do
2018 user = insert(:user)
2019 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2020 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2022 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
2025 CommonAPI.post(user, %{
2026 "status" => "This is about 2hu, but private",
2027 "visibility" => "private"
2030 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
2034 |> get("/api/v1/search", %{"q" => "2hu"})
2036 assert results = json_response(conn, 200)
2038 [account | _] = results["accounts"]
2039 assert account["id"] == to_string(user_three.id)
2041 assert results["hashtags"] == []
2043 [status] = results["statuses"]
2044 assert status["id"] == to_string(activity.id)
2047 test "search fetches remote statuses", %{conn: conn} do
2051 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
2053 assert results = json_response(conn, 200)
2055 [status] = results["statuses"]
2056 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
2060 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
2062 CommonAPI.post(insert(:user), %{
2063 "status" => "This is about 2hu, but private",
2064 "visibility" => "private"
2070 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2072 assert results = json_response(conn, 200)
2074 [] = results["statuses"]
2078 test "search fetches remote accounts", %{conn: conn} do
2081 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2083 assert results = json_response(conn, 200)
2084 [account] = results["accounts"]
2085 assert account["acct"] == "shp@social.heldscal.la"
2088 test "returns the favorites of a user", %{conn: conn} do
2089 user = insert(:user)
2090 other_user = insert(:user)
2092 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2093 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2095 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2099 |> assign(:user, user)
2100 |> get("/api/v1/favourites")
2102 assert [status] = json_response(first_conn, 200)
2103 assert status["id"] == to_string(activity.id)
2105 assert [{"link", _link_header}] =
2106 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2108 # Honours query params
2109 {:ok, second_activity} =
2110 CommonAPI.post(other_user, %{
2112 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2115 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2117 last_like = status["id"]
2121 |> assign(:user, user)
2122 |> get("/api/v1/favourites?since_id=#{last_like}")
2124 assert [second_status] = json_response(second_conn, 200)
2125 assert second_status["id"] == to_string(second_activity.id)
2129 |> assign(:user, user)
2130 |> get("/api/v1/favourites?limit=0")
2132 assert [] = json_response(third_conn, 200)
2135 describe "getting favorites timeline of specified user" do
2137 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2138 [current_user: current_user, user: user]
2141 test "returns list of statuses favorited by specified user", %{
2143 current_user: current_user,
2146 [activity | _] = insert_pair(:note_activity)
2147 CommonAPI.favorite(activity.id, user)
2151 |> assign(:user, current_user)
2152 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2153 |> json_response(:ok)
2157 assert length(response) == 1
2158 assert like["id"] == activity.id
2161 test "returns favorites for specified user_id when user is not logged in", %{
2165 activity = insert(:note_activity)
2166 CommonAPI.favorite(activity.id, user)
2170 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2171 |> json_response(:ok)
2173 assert length(response) == 1
2176 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2178 current_user: current_user,
2182 CommonAPI.post(current_user, %{
2183 "status" => "Hi @#{user.nickname}!",
2184 "visibility" => "direct"
2187 CommonAPI.favorite(direct.id, user)
2191 |> assign(:user, current_user)
2192 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2193 |> json_response(:ok)
2195 assert length(response) == 1
2197 anonymous_response =
2199 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2200 |> json_response(:ok)
2202 assert Enum.empty?(anonymous_response)
2205 test "does not return others' favorited DM when user is not one of recipients", %{
2207 current_user: current_user,
2210 user_two = insert(:user)
2213 CommonAPI.post(user_two, %{
2214 "status" => "Hi @#{user.nickname}!",
2215 "visibility" => "direct"
2218 CommonAPI.favorite(direct.id, user)
2222 |> assign(:user, current_user)
2223 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2224 |> json_response(:ok)
2226 assert Enum.empty?(response)
2229 test "paginates favorites using since_id and max_id", %{
2231 current_user: current_user,
2234 activities = insert_list(10, :note_activity)
2236 Enum.each(activities, fn activity ->
2237 CommonAPI.favorite(activity.id, user)
2240 third_activity = Enum.at(activities, 2)
2241 seventh_activity = Enum.at(activities, 6)
2245 |> assign(:user, current_user)
2246 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2247 since_id: third_activity.id,
2248 max_id: seventh_activity.id
2250 |> json_response(:ok)
2252 assert length(response) == 3
2253 refute third_activity in response
2254 refute seventh_activity in response
2257 test "limits favorites using limit parameter", %{
2259 current_user: current_user,
2263 |> insert_list(:note_activity)
2264 |> Enum.each(fn activity ->
2265 CommonAPI.favorite(activity.id, user)
2270 |> assign(:user, current_user)
2271 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2272 |> json_response(:ok)
2274 assert length(response) == 3
2277 test "returns empty response when user does not have any favorited statuses", %{
2279 current_user: current_user,
2284 |> assign(:user, current_user)
2285 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2286 |> json_response(:ok)
2288 assert Enum.empty?(response)
2291 test "returns 404 error when specified user is not exist", %{conn: conn} do
2292 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2294 assert json_response(conn, 404) == %{"error" => "Record not found"}
2297 test "returns 403 error when user has hidden own favorites", %{
2299 current_user: current_user
2301 user = insert(:user, %{info: %{hide_favorites: true}})
2302 activity = insert(:note_activity)
2303 CommonAPI.favorite(activity.id, user)
2307 |> assign(:user, current_user)
2308 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2310 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2313 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2314 user = insert(:user)
2315 activity = insert(:note_activity)
2316 CommonAPI.favorite(activity.id, user)
2320 |> assign(:user, current_user)
2321 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2323 assert user.info.hide_favorites
2324 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2328 describe "updating credentials" do
2329 test "updates the user's bio", %{conn: conn} do
2330 user = insert(:user)
2331 user2 = insert(:user)
2335 |> assign(:user, user)
2336 |> patch("/api/v1/accounts/update_credentials", %{
2337 "note" => "I drink #cofe with @#{user2.nickname}"
2340 assert user = json_response(conn, 200)
2342 assert user["note"] ==
2343 ~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=") <>
2345 ~s(" class="u-url mention" href=") <>
2346 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2349 test "updates the user's locking status", %{conn: conn} do
2350 user = insert(:user)
2354 |> assign(:user, user)
2355 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2357 assert user = json_response(conn, 200)
2358 assert user["locked"] == true
2361 test "updates the user's default scope", %{conn: conn} do
2362 user = insert(:user)
2366 |> assign(:user, user)
2367 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2369 assert user = json_response(conn, 200)
2370 assert user["source"]["privacy"] == "cofe"
2373 test "updates the user's hide_followers status", %{conn: conn} do
2374 user = insert(:user)
2378 |> assign(:user, user)
2379 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2381 assert user = json_response(conn, 200)
2382 assert user["pleroma"]["hide_followers"] == true
2385 test "updates the user's hide_follows status", %{conn: conn} do
2386 user = insert(:user)
2390 |> assign(:user, user)
2391 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2393 assert user = json_response(conn, 200)
2394 assert user["pleroma"]["hide_follows"] == true
2397 test "updates the user's hide_favorites status", %{conn: conn} do
2398 user = insert(:user)
2402 |> assign(:user, user)
2403 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2405 assert user = json_response(conn, 200)
2406 assert user["pleroma"]["hide_favorites"] == true
2409 test "updates the user's show_role status", %{conn: conn} do
2410 user = insert(:user)
2414 |> assign(:user, user)
2415 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2417 assert user = json_response(conn, 200)
2418 assert user["source"]["pleroma"]["show_role"] == false
2421 test "updates the user's no_rich_text status", %{conn: conn} do
2422 user = insert(:user)
2426 |> assign(:user, user)
2427 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2429 assert user = json_response(conn, 200)
2430 assert user["source"]["pleroma"]["no_rich_text"] == true
2433 test "updates the user's name", %{conn: conn} do
2434 user = insert(:user)
2438 |> assign(:user, user)
2439 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2441 assert user = json_response(conn, 200)
2442 assert user["display_name"] == "markorepairs"
2445 test "updates the user's avatar", %{conn: conn} do
2446 user = insert(:user)
2448 new_avatar = %Plug.Upload{
2449 content_type: "image/jpg",
2450 path: Path.absname("test/fixtures/image.jpg"),
2451 filename: "an_image.jpg"
2456 |> assign(:user, user)
2457 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2459 assert user_response = json_response(conn, 200)
2460 assert user_response["avatar"] != User.avatar_url(user)
2463 test "updates the user's banner", %{conn: conn} do
2464 user = insert(:user)
2466 new_header = %Plug.Upload{
2467 content_type: "image/jpg",
2468 path: Path.absname("test/fixtures/image.jpg"),
2469 filename: "an_image.jpg"
2474 |> assign(:user, user)
2475 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2477 assert user_response = json_response(conn, 200)
2478 assert user_response["header"] != User.banner_url(user)
2481 test "requires 'write' permission", %{conn: conn} do
2482 token1 = insert(:oauth_token, scopes: ["read"])
2483 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2485 for token <- [token1, token2] do
2488 |> put_req_header("authorization", "Bearer #{token.token}")
2489 |> patch("/api/v1/accounts/update_credentials", %{})
2491 if token == token1 do
2492 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2494 assert json_response(conn, 200)
2499 test "updates profile emojos", %{conn: conn} do
2500 user = insert(:user)
2502 note = "*sips :blank:*"
2503 name = "I am :firefox:"
2507 |> assign(:user, user)
2508 |> patch("/api/v1/accounts/update_credentials", %{
2510 "display_name" => name
2513 assert json_response(conn, 200)
2517 |> get("/api/v1/accounts/#{user.id}")
2519 assert user = json_response(conn, 200)
2521 assert user["note"] == note
2522 assert user["display_name"] == name
2523 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2527 test "get instance information", %{conn: conn} do
2528 conn = get(conn, "/api/v1/instance")
2529 assert result = json_response(conn, 200)
2531 email = Pleroma.Config.get([:instance, :email])
2532 # Note: not checking for "max_toot_chars" since it's optional
2538 "email" => from_config_email,
2540 "streaming_api" => _
2545 "registrations" => _
2548 assert email == from_config_email
2551 test "get instance stats", %{conn: conn} do
2552 user = insert(:user, %{local: true})
2554 user2 = insert(:user, %{local: true})
2555 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2557 insert(:user, %{local: false, nickname: "u@peer1.com"})
2558 insert(:user, %{local: false, nickname: "u@peer2.com"})
2560 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2562 # Stats should count users with missing or nil `info.deactivated` value
2563 user = User.get_cached_by_id(user.id)
2564 info_change = Changeset.change(user.info, %{deactivated: nil})
2568 |> Changeset.change()
2569 |> Changeset.put_embed(:info, info_change)
2570 |> User.update_and_set_cache()
2572 Pleroma.Stats.update_stats()
2574 conn = get(conn, "/api/v1/instance")
2576 assert result = json_response(conn, 200)
2578 stats = result["stats"]
2581 assert stats["user_count"] == 1
2582 assert stats["status_count"] == 1
2583 assert stats["domain_count"] == 2
2586 test "get peers", %{conn: conn} do
2587 insert(:user, %{local: false, nickname: "u@peer1.com"})
2588 insert(:user, %{local: false, nickname: "u@peer2.com"})
2590 Pleroma.Stats.update_stats()
2592 conn = get(conn, "/api/v1/instance/peers")
2594 assert result = json_response(conn, 200)
2596 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2599 test "put settings", %{conn: conn} do
2600 user = insert(:user)
2604 |> assign(:user, user)
2605 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2607 assert _result = json_response(conn, 200)
2609 user = User.get_cached_by_ap_id(user.ap_id)
2610 assert user.info.settings == %{"programming" => "socks"}
2613 describe "pinned statuses" do
2615 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2617 user = insert(:user)
2618 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2620 [user: user, activity: activity]
2623 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2624 {:ok, _} = CommonAPI.pin(activity.id, user)
2628 |> assign(:user, user)
2629 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2630 |> json_response(200)
2632 id_str = to_string(activity.id)
2634 assert [%{"id" => ^id_str, "pinned" => true}] = result
2637 test "pin status", %{conn: conn, user: user, activity: activity} do
2638 id_str = to_string(activity.id)
2640 assert %{"id" => ^id_str, "pinned" => true} =
2642 |> assign(:user, user)
2643 |> post("/api/v1/statuses/#{activity.id}/pin")
2644 |> json_response(200)
2646 assert [%{"id" => ^id_str, "pinned" => true}] =
2648 |> assign(:user, user)
2649 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2650 |> json_response(200)
2653 test "unpin status", %{conn: conn, user: user, activity: activity} do
2654 {:ok, _} = CommonAPI.pin(activity.id, user)
2656 id_str = to_string(activity.id)
2657 user = refresh_record(user)
2659 assert %{"id" => ^id_str, "pinned" => false} =
2661 |> assign(:user, user)
2662 |> post("/api/v1/statuses/#{activity.id}/unpin")
2663 |> json_response(200)
2667 |> assign(:user, user)
2668 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2669 |> json_response(200)
2672 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2673 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2675 id_str_one = to_string(activity_one.id)
2677 assert %{"id" => ^id_str_one, "pinned" => true} =
2679 |> assign(:user, user)
2680 |> post("/api/v1/statuses/#{id_str_one}/pin")
2681 |> json_response(200)
2683 user = refresh_record(user)
2685 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2687 |> assign(:user, user)
2688 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2689 |> json_response(400)
2692 test "Status rich-media Card", %{conn: conn, user: user} do
2693 Pleroma.Config.put([:rich_media, :enabled], true)
2694 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2698 |> get("/api/v1/statuses/#{activity.id}/card")
2699 |> json_response(200)
2701 assert response == %{
2702 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2703 "provider_name" => "www.imdb.com",
2704 "provider_url" => "http://www.imdb.com",
2705 "title" => "The Rock",
2707 "url" => "http://www.imdb.com/title/tt0117500/",
2708 "description" => nil,
2711 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2712 "title" => "The Rock",
2713 "type" => "video.movie",
2714 "url" => "http://www.imdb.com/title/tt0117500/"
2719 # works with private posts
2721 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2725 |> assign(:user, user)
2726 |> get("/api/v1/statuses/#{activity.id}/card")
2727 |> json_response(200)
2729 assert response_two == response
2731 Pleroma.Config.put([:rich_media, :enabled], false)
2736 user = insert(:user)
2737 for_user = insert(:user)
2740 CommonAPI.post(user, %{
2741 "status" => "heweoo?"
2745 CommonAPI.post(user, %{
2746 "status" => "heweoo!"
2751 |> assign(:user, for_user)
2752 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2754 assert json_response(response1, 200)["bookmarked"] == true
2758 |> assign(:user, for_user)
2759 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2761 assert json_response(response2, 200)["bookmarked"] == true
2765 |> assign(:user, for_user)
2766 |> get("/api/v1/bookmarks")
2768 assert [json_response(response2, 200), json_response(response1, 200)] ==
2769 json_response(bookmarks, 200)
2773 |> assign(:user, for_user)
2774 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2776 assert json_response(response1, 200)["bookmarked"] == false
2780 |> assign(:user, for_user)
2781 |> get("/api/v1/bookmarks")
2783 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2786 describe "conversation muting" do
2788 user = insert(:user)
2789 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2791 [user: user, activity: activity]
2794 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2795 id_str = to_string(activity.id)
2797 assert %{"id" => ^id_str, "muted" => true} =
2799 |> assign(:user, user)
2800 |> post("/api/v1/statuses/#{activity.id}/mute")
2801 |> json_response(200)
2804 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2805 {:ok, _} = CommonAPI.add_mute(user, activity)
2807 id_str = to_string(activity.id)
2808 user = refresh_record(user)
2810 assert %{"id" => ^id_str, "muted" => false} =
2812 |> assign(:user, user)
2813 |> post("/api/v1/statuses/#{activity.id}/unmute")
2814 |> json_response(200)
2818 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2819 user = insert(:user)
2823 |> assign(:user, user)
2824 |> get("/api/v1/pleroma/flavour")
2826 assert "glitch" == json_response(get_old_flavour, 200)
2830 |> assign(:user, user)
2831 |> post("/api/v1/pleroma/flavour/vanilla")
2833 assert "vanilla" == json_response(set_flavour, 200)
2837 |> assign(:user, user)
2838 |> post("/api/v1/pleroma/flavour/vanilla")
2840 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2843 describe "reports" do
2845 reporter = insert(:user)
2846 target_user = insert(:user)
2848 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2850 [reporter: reporter, target_user: target_user, activity: activity]
2853 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2854 assert %{"action_taken" => false, "id" => _} =
2856 |> assign(:user, reporter)
2857 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2858 |> json_response(200)
2861 test "submit a report with statuses and comment", %{
2864 target_user: target_user,
2867 assert %{"action_taken" => false, "id" => _} =
2869 |> assign(:user, reporter)
2870 |> post("/api/v1/reports", %{
2871 "account_id" => target_user.id,
2872 "status_ids" => [activity.id],
2873 "comment" => "bad status!"
2875 |> json_response(200)
2878 test "account_id is required", %{
2883 assert %{"error" => "Valid `account_id` required"} =
2885 |> assign(:user, reporter)
2886 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2887 |> json_response(400)
2890 test "comment must be up to the size specified in the config", %{
2893 target_user: target_user
2895 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2896 comment = String.pad_trailing("a", max_size + 1, "a")
2898 error = %{"error" => "Comment must be up to #{max_size} characters"}
2902 |> assign(:user, reporter)
2903 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2904 |> json_response(400)
2908 describe "link headers" do
2909 test "preserves parameters in link headers", %{conn: conn} do
2910 user = insert(:user)
2911 other_user = insert(:user)
2914 CommonAPI.post(other_user, %{
2915 "status" => "hi @#{user.nickname}",
2916 "visibility" => "public"
2920 CommonAPI.post(other_user, %{
2921 "status" => "hi @#{user.nickname}",
2922 "visibility" => "public"
2925 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2926 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2930 |> assign(:user, user)
2931 |> get("/api/v1/notifications", %{media_only: true})
2933 assert [link_header] = get_resp_header(conn, "link")
2934 assert link_header =~ ~r/media_only=true/
2935 assert link_header =~ ~r/min_id=#{notification2.id}/
2936 assert link_header =~ ~r/max_id=#{notification1.id}/
2940 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2941 # Need to set an old-style integer ID to reproduce the problem
2942 # (these are no longer assigned to new accounts but were preserved
2943 # for existing accounts during the migration to flakeIDs)
2944 user_one = insert(:user, %{id: 1212})
2945 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2949 |> get("/api/v1/accounts/#{user_one.id}")
2953 |> get("/api/v1/accounts/#{user_two.nickname}")
2957 |> get("/api/v1/accounts/#{user_two.id}")
2959 acc_one = json_response(resp_one, 200)
2960 acc_two = json_response(resp_two, 200)
2961 acc_three = json_response(resp_three, 200)
2962 refute acc_one == acc_two
2963 assert acc_two == acc_three
2966 describe "custom emoji" do
2967 test "with tags", %{conn: conn} do
2970 |> get("/api/v1/custom_emojis")
2971 |> json_response(200)
2973 assert Map.has_key?(emoji, "shortcode")
2974 assert Map.has_key?(emoji, "static_url")
2975 assert Map.has_key?(emoji, "tags")
2976 assert is_list(emoji["tags"])
2977 assert Map.has_key?(emoji, "url")
2978 assert Map.has_key?(emoji, "visible_in_picker")
2982 describe "index/2 redirections" do
2983 setup %{conn: conn} do
2987 signing_salt: "cooldude"
2992 |> Plug.Session.call(Plug.Session.init(session_opts))
2995 test_path = "/web/statuses/test"
2996 %{conn: conn, path: test_path}
2999 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3000 conn = get(conn, path)
3002 assert conn.status == 302
3003 assert redirected_to(conn) == "/web/login"
3006 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3007 token = insert(:oauth_token)
3011 |> assign(:user, token.user)
3012 |> put_session(:oauth_token, token.token)
3015 assert conn.status == 200
3018 test "saves referer path to session", %{conn: conn, path: path} do
3019 conn = get(conn, path)
3020 return_to = Plug.Conn.get_session(conn, :return_to)
3022 assert return_to == path
3025 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3026 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3027 auth = insert(:oauth_authorization, app: app)
3031 |> put_session(:return_to, path)
3032 |> get("/web/login", %{code: auth.token})
3034 assert conn.status == 302
3035 assert redirected_to(conn) == path
3038 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3039 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3040 auth = insert(:oauth_authorization, app: app)
3042 conn = get(conn, "/web/login", %{code: auth.token})
3044 assert conn.status == 302
3045 assert redirected_to(conn) == "/web/getting-started"
3049 describe "scheduled activities" do
3050 test "creates a scheduled activity", %{conn: conn} do
3051 user = insert(:user)
3052 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3056 |> assign(:user, user)
3057 |> post("/api/v1/statuses", %{
3058 "status" => "scheduled",
3059 "scheduled_at" => scheduled_at
3062 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3063 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3064 assert [] == Repo.all(Activity)
3067 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3068 user = insert(:user)
3069 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3071 file = %Plug.Upload{
3072 content_type: "image/jpg",
3073 path: Path.absname("test/fixtures/image.jpg"),
3074 filename: "an_image.jpg"
3077 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3081 |> assign(:user, user)
3082 |> post("/api/v1/statuses", %{
3083 "media_ids" => [to_string(upload.id)],
3084 "status" => "scheduled",
3085 "scheduled_at" => scheduled_at
3088 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3089 assert %{"type" => "image"} = media_attachment
3092 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3094 user = insert(:user)
3097 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3101 |> assign(:user, user)
3102 |> post("/api/v1/statuses", %{
3103 "status" => "not scheduled",
3104 "scheduled_at" => scheduled_at
3107 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3108 assert [] == Repo.all(ScheduledActivity)
3111 test "returns error when daily user limit is exceeded", %{conn: conn} do
3112 user = insert(:user)
3115 NaiveDateTime.utc_now()
3116 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3117 |> NaiveDateTime.to_iso8601()
3119 attrs = %{params: %{}, scheduled_at: today}
3120 {:ok, _} = ScheduledActivity.create(user, attrs)
3121 {:ok, _} = ScheduledActivity.create(user, attrs)
3125 |> assign(:user, user)
3126 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3128 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3131 test "returns error when total user limit is exceeded", %{conn: conn} do
3132 user = insert(:user)
3135 NaiveDateTime.utc_now()
3136 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3137 |> NaiveDateTime.to_iso8601()
3140 NaiveDateTime.utc_now()
3141 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3142 |> NaiveDateTime.to_iso8601()
3144 attrs = %{params: %{}, scheduled_at: today}
3145 {:ok, _} = ScheduledActivity.create(user, attrs)
3146 {:ok, _} = ScheduledActivity.create(user, attrs)
3147 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3151 |> assign(:user, user)
3152 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3154 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3157 test "shows scheduled activities", %{conn: conn} do
3158 user = insert(:user)
3159 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3160 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3161 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3162 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3166 |> assign(:user, user)
3171 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3173 result = json_response(conn_res, 200)
3174 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3179 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3181 result = json_response(conn_res, 200)
3182 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3187 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3189 result = json_response(conn_res, 200)
3190 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3193 test "shows a scheduled activity", %{conn: conn} do
3194 user = insert(:user)
3195 scheduled_activity = insert(:scheduled_activity, user: user)
3199 |> assign(:user, user)
3200 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3202 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3203 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3207 |> assign(:user, user)
3208 |> get("/api/v1/scheduled_statuses/404")
3210 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3213 test "updates a scheduled activity", %{conn: conn} do
3214 user = insert(:user)
3215 scheduled_activity = insert(:scheduled_activity, user: user)
3218 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3222 |> assign(:user, user)
3223 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3224 scheduled_at: new_scheduled_at
3227 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3228 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3232 |> assign(:user, user)
3233 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3235 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3238 test "deletes a scheduled activity", %{conn: conn} do
3239 user = insert(:user)
3240 scheduled_activity = insert(:scheduled_activity, user: user)
3244 |> assign(:user, user)
3245 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3247 assert %{} = json_response(res_conn, 200)
3248 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3252 |> assign(:user, user)
3253 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3255 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3259 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3260 user1 = insert(:user)
3261 user2 = insert(:user)
3262 user3 = insert(:user)
3264 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3266 # Reply to status from another user
3269 |> assign(:user, user2)
3270 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3272 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3274 activity = Activity.get_by_id_with_object(id)
3276 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3277 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3279 # Reblog from the third user
3282 |> assign(:user, user3)
3283 |> post("/api/v1/statuses/#{activity.id}/reblog")
3285 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3286 json_response(conn2, 200)
3288 assert to_string(activity.id) == id
3290 # Getting third user status
3293 |> assign(:user, user3)
3294 |> get("api/v1/timelines/home")
3296 [reblogged_activity] = json_response(conn3, 200)
3298 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3300 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3301 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3304 describe "create account by app" do
3306 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3307 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3308 interval = Pleroma.Config.get([:app_account_creation, :interval])
3310 Pleroma.Config.put([:app_account_creation, :enabled], true)
3311 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3312 Pleroma.Config.put([:app_account_creation, :interval], 1)
3315 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3316 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3317 Pleroma.Config.put([:app_account_creation, :interval], interval)
3323 test "Account registration via Application", %{conn: conn} do
3326 |> post("/api/v1/apps", %{
3327 client_name: "client_name",
3328 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3329 scopes: "read, write, follow"
3333 "client_id" => client_id,
3334 "client_secret" => client_secret,
3336 "name" => "client_name",
3337 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3340 } = json_response(conn, 200)
3344 |> post("/oauth/token", %{
3345 grant_type: "client_credentials",
3346 client_id: client_id,
3347 client_secret: client_secret
3350 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3351 json_response(conn, 200)
3354 token_from_db = Repo.get_by(Token, token: token)
3355 assert token_from_db
3357 assert scope == "read write follow"
3361 |> put_req_header("authorization", "Bearer " <> token)
3362 |> post("/api/v1/accounts", %{
3364 email: "lain@example.org",
3365 password: "PlzDontHackLain",
3370 "access_token" => token,
3371 "created_at" => _created_at,
3373 "token_type" => "Bearer"
3374 } = json_response(conn, 200)
3376 token_from_db = Repo.get_by(Token, token: token)
3377 assert token_from_db
3378 token_from_db = Repo.preload(token_from_db, :user)
3379 assert token_from_db.user
3381 assert token_from_db.user.info.confirmation_pending
3384 test "rate limit", %{conn: conn} do
3385 app_token = insert(:oauth_token, user: nil)
3388 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3389 |> Map.put(:remote_ip, {15, 15, 15, 15})
3394 |> post("/api/v1/accounts", %{
3395 username: "#{i}lain",
3396 email: "#{i}lain@example.org",
3397 password: "PlzDontHackLain",
3402 "access_token" => token,
3403 "created_at" => _created_at,
3405 "token_type" => "Bearer"
3406 } = json_response(conn, 200)
3408 token_from_db = Repo.get_by(Token, token: token)
3409 assert token_from_db
3410 token_from_db = Repo.preload(token_from_db, :user)
3411 assert token_from_db.user
3413 assert token_from_db.user.info.confirmation_pending
3418 |> post("/api/v1/accounts", %{
3420 email: "6lain@example.org",
3421 password: "PlzDontHackLain",
3425 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}