1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.Notification
13 alias Pleroma.ScheduledActivity
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.MastodonAPI.FilterView
18 alias Pleroma.Web.OAuth.App
19 alias Pleroma.Web.OStatus
20 alias Pleroma.Web.Push
21 alias Pleroma.Web.TwitterAPI.TwitterAPI
22 import Pleroma.Factory
23 import ExUnit.CaptureLog
27 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
31 test "the home timeline", %{conn: conn} do
33 following = insert(:user)
35 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
39 |> assign(:user, user)
40 |> get("/api/v1/timelines/home")
42 assert Enum.empty?(json_response(conn, 200))
44 {:ok, user} = User.follow(user, following)
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
51 assert [%{"content" => "test"}] = json_response(conn, 200)
54 test "the public timeline", %{conn: conn} do
55 following = insert(:user)
58 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
61 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
65 |> get("/api/v1/timelines/public", %{"local" => "False"})
67 assert length(json_response(conn, 200)) == 2
71 |> get("/api/v1/timelines/public", %{"local" => "True"})
73 assert [%{"content" => "test"}] = json_response(conn, 200)
77 |> get("/api/v1/timelines/public", %{"local" => "1"})
79 assert [%{"content" => "test"}] = json_response(conn, 200)
83 test "posting a status", %{conn: conn} do
86 idempotency_key = "Pikachu rocks!"
90 |> assign(:user, user)
91 |> put_req_header("idempotency-key", idempotency_key)
92 |> post("/api/v1/statuses", %{
94 "spoiler_text" => "2hu",
95 "sensitive" => "false"
98 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
100 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
102 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
103 json_response(conn_one, 200)
105 assert Activity.get_by_id(id)
109 |> assign(:user, user)
110 |> put_req_header("idempotency-key", idempotency_key)
111 |> post("/api/v1/statuses", %{
113 "spoiler_text" => "2hu",
114 "sensitive" => "false"
117 assert %{"id" => second_id} = json_response(conn_two, 200)
119 assert id == second_id
123 |> assign(:user, user)
124 |> post("/api/v1/statuses", %{
126 "spoiler_text" => "2hu",
127 "sensitive" => "false"
130 assert %{"id" => third_id} = json_response(conn_three, 200)
132 refute id == third_id
135 test "posting a sensitive status", %{conn: conn} do
140 |> assign(:user, user)
141 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
143 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
144 assert Activity.get_by_id(id)
147 test "posting a fake status", %{conn: conn} do
152 |> assign(:user, user)
153 |> post("/api/v1/statuses", %{
155 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
158 real_status = json_response(real_conn, 200)
161 assert Object.get_by_ap_id(real_status["uri"])
165 |> Map.put("id", nil)
166 |> Map.put("url", nil)
167 |> Map.put("uri", nil)
168 |> Map.put("created_at", nil)
169 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
173 |> assign(:user, user)
174 |> post("/api/v1/statuses", %{
176 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
180 fake_status = json_response(fake_conn, 200)
183 refute Object.get_by_ap_id(fake_status["uri"])
187 |> Map.put("id", nil)
188 |> Map.put("url", nil)
189 |> Map.put("uri", nil)
190 |> Map.put("created_at", nil)
191 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
193 assert real_status == fake_status
196 test "posting a status with OGP link preview", %{conn: conn} do
197 Pleroma.Config.put([:rich_media, :enabled], true)
202 |> assign(:user, user)
203 |> post("/api/v1/statuses", %{
204 "status" => "http://example.com/ogp"
207 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
208 assert Activity.get_by_id(id)
209 Pleroma.Config.put([:rich_media, :enabled], false)
212 test "posting a direct status", %{conn: conn} do
213 user1 = insert(:user)
214 user2 = insert(:user)
215 content = "direct cofe @#{user2.nickname}"
219 |> assign(:user, user1)
220 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
222 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
223 assert activity = Activity.get_by_id(id)
224 assert activity.recipients == [user2.ap_id, user1.ap_id]
225 assert activity.data["to"] == [user2.ap_id]
226 assert activity.data["cc"] == []
229 test "direct timeline", %{conn: conn} do
230 user_one = insert(:user)
231 user_two = insert(:user)
233 {:ok, user_two} = User.follow(user_two, user_one)
236 CommonAPI.post(user_one, %{
237 "status" => "Hi @#{user_two.nickname}!",
238 "visibility" => "direct"
241 {:ok, _follower_only} =
242 CommonAPI.post(user_one, %{
243 "status" => "Hi @#{user_two.nickname}!",
244 "visibility" => "private"
247 # Only direct should be visible here
250 |> assign(:user, user_two)
251 |> get("api/v1/timelines/direct")
253 [status] = json_response(res_conn, 200)
255 assert %{"visibility" => "direct"} = status
256 assert status["url"] != direct.data["id"]
258 # User should be able to see his own direct message
261 |> assign(:user, user_one)
262 |> get("api/v1/timelines/direct")
264 [status] = json_response(res_conn, 200)
266 assert %{"visibility" => "direct"} = status
268 # Both should be visible here
271 |> assign(:user, user_two)
272 |> get("api/v1/timelines/home")
274 [_s1, _s2] = json_response(res_conn, 200)
277 Enum.each(1..20, fn _ ->
279 CommonAPI.post(user_one, %{
280 "status" => "Hi @#{user_two.nickname}!",
281 "visibility" => "direct"
287 |> assign(:user, user_two)
288 |> get("api/v1/timelines/direct")
290 statuses = json_response(res_conn, 200)
291 assert length(statuses) == 20
295 |> assign(:user, user_two)
296 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
298 [status] = json_response(res_conn, 200)
300 assert status["url"] != direct.data["id"]
303 test "Conversations", %{conn: conn} do
304 user_one = insert(:user)
305 user_two = insert(:user)
307 {:ok, user_two} = User.follow(user_two, user_one)
310 CommonAPI.post(user_one, %{
311 "status" => "Hi @#{user_two.nickname}!",
312 "visibility" => "direct"
315 {:ok, _follower_only} =
316 CommonAPI.post(user_one, %{
317 "status" => "Hi @#{user_two.nickname}!",
318 "visibility" => "private"
323 |> assign(:user, user_one)
324 |> get("/api/v1/conversations")
326 assert response = json_response(res_conn, 200)
331 "accounts" => res_accounts,
332 "last_status" => res_last_status,
337 assert length(res_accounts) == 2
338 assert is_binary(res_id)
339 assert unread == true
340 assert res_last_status["id"] == direct.id
342 # Apparently undocumented API endpoint
345 |> assign(:user, user_one)
346 |> post("/api/v1/conversations/#{res_id}/read")
348 assert response = json_response(res_conn, 200)
349 assert response["unread"] == false
351 # (vanilla) Mastodon frontend behaviour
354 |> assign(:user, user_one)
355 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
357 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
360 test "doesn't include DMs from blocked users", %{conn: conn} do
361 blocker = insert(:user)
362 blocked = insert(:user)
364 {:ok, blocker} = User.block(blocker, blocked)
366 {:ok, _blocked_direct} =
367 CommonAPI.post(blocked, %{
368 "status" => "Hi @#{blocker.nickname}!",
369 "visibility" => "direct"
373 CommonAPI.post(user, %{
374 "status" => "Hi @#{blocker.nickname}!",
375 "visibility" => "direct"
380 |> assign(:user, user)
381 |> get("api/v1/timelines/direct")
383 [status] = json_response(res_conn, 200)
384 assert status["id"] == direct.id
387 test "replying to a status", %{conn: conn} do
390 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
394 |> assign(:user, user)
395 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
397 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
399 activity = Activity.get_by_id(id)
401 assert activity.data["context"] == replied_to.data["context"]
402 assert activity.data["object"]["inReplyToStatusId"] == replied_to.id
405 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
410 |> assign(:user, user)
411 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
413 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
415 activity = Activity.get_by_id(id)
420 test "verify_credentials", %{conn: conn} do
425 |> assign(:user, user)
426 |> get("/api/v1/accounts/verify_credentials")
428 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
429 assert id == to_string(user.id)
432 test "verify_credentials default scope unlisted", %{conn: conn} do
433 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "unlisted"}})
437 |> assign(:user, user)
438 |> get("/api/v1/accounts/verify_credentials")
440 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
441 assert id == to_string(user.id)
444 test "apps/verify_credentials", %{conn: conn} do
445 token = insert(:oauth_token)
449 |> assign(:user, token.user)
450 |> assign(:token, token)
451 |> get("/api/v1/apps/verify_credentials")
453 app = Repo.preload(token, :app).app
456 "name" => app.client_name,
457 "website" => app.website,
458 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
461 assert expected == json_response(conn, 200)
464 test "creates an oauth app", %{conn: conn} do
466 app_attrs = build(:oauth_app)
470 |> assign(:user, user)
471 |> post("/api/v1/apps", %{
472 client_name: app_attrs.client_name,
473 redirect_uris: app_attrs.redirect_uris
476 [app] = Repo.all(App)
479 "name" => app.client_name,
480 "website" => app.website,
481 "client_id" => app.client_id,
482 "client_secret" => app.client_secret,
483 "id" => app.id |> to_string(),
484 "redirect_uri" => app.redirect_uris,
485 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
488 assert expected == json_response(conn, 200)
491 test "get a status", %{conn: conn} do
492 activity = insert(:note_activity)
496 |> get("/api/v1/statuses/#{activity.id}")
498 assert %{"id" => id} = json_response(conn, 200)
499 assert id == to_string(activity.id)
502 describe "deleting a status" do
503 test "when you created it", %{conn: conn} do
504 activity = insert(:note_activity)
505 author = User.get_by_ap_id(activity.data["actor"])
509 |> assign(:user, author)
510 |> delete("/api/v1/statuses/#{activity.id}")
512 assert %{} = json_response(conn, 200)
514 refute Activity.get_by_id(activity.id)
517 test "when you didn't create it", %{conn: conn} do
518 activity = insert(:note_activity)
523 |> assign(:user, user)
524 |> delete("/api/v1/statuses/#{activity.id}")
526 assert %{"error" => _} = json_response(conn, 403)
528 assert Activity.get_by_id(activity.id) == activity
531 test "when you're an admin or moderator", %{conn: conn} do
532 activity1 = insert(:note_activity)
533 activity2 = insert(:note_activity)
534 admin = insert(:user, info: %{is_admin: true})
535 moderator = insert(:user, info: %{is_moderator: true})
539 |> assign(:user, admin)
540 |> delete("/api/v1/statuses/#{activity1.id}")
542 assert %{} = json_response(res_conn, 200)
546 |> assign(:user, moderator)
547 |> delete("/api/v1/statuses/#{activity2.id}")
549 assert %{} = json_response(res_conn, 200)
551 refute Activity.get_by_id(activity1.id)
552 refute Activity.get_by_id(activity2.id)
556 describe "filters" do
557 test "creating a filter", %{conn: conn} do
560 filter = %Pleroma.Filter{
567 |> assign(:user, user)
568 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
570 assert response = json_response(conn, 200)
571 assert response["phrase"] == filter.phrase
572 assert response["context"] == filter.context
573 assert response["id"] != nil
574 assert response["id"] != ""
577 test "fetching a list of filters", %{conn: conn} do
580 query_one = %Pleroma.Filter{
587 query_two = %Pleroma.Filter{
594 {:ok, filter_one} = Pleroma.Filter.create(query_one)
595 {:ok, filter_two} = Pleroma.Filter.create(query_two)
599 |> assign(:user, user)
600 |> get("/api/v1/filters")
601 |> json_response(200)
607 filters: [filter_two, filter_one]
611 test "get a filter", %{conn: conn} do
614 query = %Pleroma.Filter{
621 {:ok, filter} = Pleroma.Filter.create(query)
625 |> assign(:user, user)
626 |> get("/api/v1/filters/#{filter.filter_id}")
628 assert _response = json_response(conn, 200)
631 test "update a filter", %{conn: conn} do
634 query = %Pleroma.Filter{
641 {:ok, _filter} = Pleroma.Filter.create(query)
643 new = %Pleroma.Filter{
650 |> assign(:user, user)
651 |> put("/api/v1/filters/#{query.filter_id}", %{
656 assert response = json_response(conn, 200)
657 assert response["phrase"] == new.phrase
658 assert response["context"] == new.context
661 test "delete a filter", %{conn: conn} do
664 query = %Pleroma.Filter{
671 {:ok, filter} = Pleroma.Filter.create(query)
675 |> assign(:user, user)
676 |> delete("/api/v1/filters/#{filter.filter_id}")
678 assert response = json_response(conn, 200)
679 assert response == %{}
684 test "creating a list", %{conn: conn} do
689 |> assign(:user, user)
690 |> post("/api/v1/lists", %{"title" => "cuties"})
692 assert %{"title" => title} = json_response(conn, 200)
693 assert title == "cuties"
696 test "adding users to a list", %{conn: conn} do
698 other_user = insert(:user)
699 {:ok, list} = Pleroma.List.create("name", user)
703 |> assign(:user, user)
704 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
706 assert %{} == json_response(conn, 200)
707 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
708 assert following == [other_user.follower_address]
711 test "removing users from a list", %{conn: conn} do
713 other_user = insert(:user)
714 third_user = insert(:user)
715 {:ok, list} = Pleroma.List.create("name", user)
716 {:ok, list} = Pleroma.List.follow(list, other_user)
717 {:ok, list} = Pleroma.List.follow(list, third_user)
721 |> assign(:user, user)
722 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
724 assert %{} == json_response(conn, 200)
725 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
726 assert following == [third_user.follower_address]
729 test "listing users in a list", %{conn: conn} do
731 other_user = insert(:user)
732 {:ok, list} = Pleroma.List.create("name", user)
733 {:ok, list} = Pleroma.List.follow(list, other_user)
737 |> assign(:user, user)
738 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
740 assert [%{"id" => id}] = json_response(conn, 200)
741 assert id == to_string(other_user.id)
744 test "retrieving a list", %{conn: conn} do
746 {:ok, list} = Pleroma.List.create("name", user)
750 |> assign(:user, user)
751 |> get("/api/v1/lists/#{list.id}")
753 assert %{"id" => id} = json_response(conn, 200)
754 assert id == to_string(list.id)
757 test "renaming a list", %{conn: conn} do
759 {:ok, list} = Pleroma.List.create("name", user)
763 |> assign(:user, user)
764 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
766 assert %{"title" => name} = json_response(conn, 200)
767 assert name == "newname"
770 test "deleting a list", %{conn: conn} do
772 {:ok, list} = Pleroma.List.create("name", user)
776 |> assign(:user, user)
777 |> delete("/api/v1/lists/#{list.id}")
779 assert %{} = json_response(conn, 200)
780 assert is_nil(Repo.get(Pleroma.List, list.id))
783 test "list timeline", %{conn: conn} do
785 other_user = insert(:user)
786 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
787 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
788 {:ok, list} = Pleroma.List.create("name", user)
789 {:ok, list} = Pleroma.List.follow(list, other_user)
793 |> assign(:user, user)
794 |> get("/api/v1/timelines/list/#{list.id}")
796 assert [%{"id" => id}] = json_response(conn, 200)
798 assert id == to_string(activity_two.id)
801 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
803 other_user = insert(:user)
804 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
806 {:ok, _activity_two} =
807 TwitterAPI.create_status(other_user, %{
808 "status" => "Marisa is cute.",
809 "visibility" => "private"
812 {:ok, list} = Pleroma.List.create("name", user)
813 {:ok, list} = Pleroma.List.follow(list, other_user)
817 |> assign(:user, user)
818 |> get("/api/v1/timelines/list/#{list.id}")
820 assert [%{"id" => id}] = json_response(conn, 200)
822 assert id == to_string(activity_one.id)
826 describe "notifications" do
827 test "list of notifications", %{conn: conn} do
829 other_user = insert(:user)
832 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
834 {:ok, [_notification]} = Notification.create_notifications(activity)
838 |> assign(:user, user)
839 |> get("/api/v1/notifications")
842 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
844 }\">@<span>#{user.nickname}</span></a></span>"
846 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
847 assert response == expected_response
850 test "getting a single notification", %{conn: conn} do
852 other_user = insert(:user)
855 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
857 {:ok, [notification]} = Notification.create_notifications(activity)
861 |> assign(:user, user)
862 |> get("/api/v1/notifications/#{notification.id}")
865 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
867 }\">@<span>#{user.nickname}</span></a></span>"
869 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
870 assert response == expected_response
873 test "dismissing a single notification", %{conn: conn} do
875 other_user = insert(:user)
878 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
880 {:ok, [notification]} = Notification.create_notifications(activity)
884 |> assign(:user, user)
885 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
887 assert %{} = json_response(conn, 200)
890 test "clearing all notifications", %{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/clear")
904 assert %{} = json_response(conn, 200)
908 |> assign(:user, user)
909 |> get("/api/v1/notifications")
911 assert all = json_response(conn, 200)
915 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
917 other_user = insert(:user)
919 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
920 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
921 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
922 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
924 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
925 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
926 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
927 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
931 |> assign(:user, user)
936 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
938 result = json_response(conn_res, 200)
939 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
944 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
946 result = json_response(conn_res, 200)
947 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
952 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
954 result = json_response(conn_res, 200)
955 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
958 test "filters notifications using exclude_types", %{conn: conn} do
960 other_user = insert(:user)
962 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
963 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
964 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
965 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
966 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
968 mention_notification_id =
969 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
971 favorite_notification_id =
972 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
974 reblog_notification_id =
975 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
977 follow_notification_id =
978 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
982 |> assign(:user, user)
985 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
987 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
990 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
992 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
995 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
997 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1000 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1002 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1006 describe "reblogging" do
1007 test "reblogs and returns the reblogged status", %{conn: conn} do
1008 activity = insert(:note_activity)
1009 user = insert(:user)
1013 |> assign(:user, user)
1014 |> post("/api/v1/statuses/#{activity.id}/reblog")
1016 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
1017 json_response(conn, 200)
1019 assert to_string(activity.id) == id
1023 describe "unreblogging" do
1024 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1025 activity = insert(:note_activity)
1026 user = insert(:user)
1028 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1032 |> assign(:user, user)
1033 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1035 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1037 assert to_string(activity.id) == id
1041 describe "favoriting" do
1042 test "favs a status and returns it", %{conn: conn} do
1043 activity = insert(:note_activity)
1044 user = insert(:user)
1048 |> assign(:user, user)
1049 |> post("/api/v1/statuses/#{activity.id}/favourite")
1051 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1052 json_response(conn, 200)
1054 assert to_string(activity.id) == id
1057 test "returns 500 for a wrong id", %{conn: conn} do
1058 user = insert(:user)
1062 |> assign(:user, user)
1063 |> post("/api/v1/statuses/1/favourite")
1064 |> json_response(500)
1066 assert resp == "Something went wrong"
1070 describe "unfavoriting" do
1071 test "unfavorites a status and returns it", %{conn: conn} do
1072 activity = insert(:note_activity)
1073 user = insert(:user)
1075 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1079 |> assign(:user, user)
1080 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1082 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1083 json_response(conn, 200)
1085 assert to_string(activity.id) == id
1089 describe "user timelines" do
1090 test "gets a users statuses", %{conn: conn} do
1091 user_one = insert(:user)
1092 user_two = insert(:user)
1093 user_three = insert(:user)
1095 {:ok, user_three} = User.follow(user_three, user_one)
1097 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1099 {:ok, direct_activity} =
1100 CommonAPI.post(user_one, %{
1101 "status" => "Hi, @#{user_two.nickname}.",
1102 "visibility" => "direct"
1105 {:ok, private_activity} =
1106 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1110 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1112 assert [%{"id" => id}] = json_response(resp, 200)
1113 assert id == to_string(activity.id)
1117 |> assign(:user, user_two)
1118 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1120 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1121 assert id_one == to_string(direct_activity.id)
1122 assert id_two == to_string(activity.id)
1126 |> assign(:user, user_three)
1127 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1129 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1130 assert id_one == to_string(private_activity.id)
1131 assert id_two == to_string(activity.id)
1134 test "unimplemented pinned statuses feature", %{conn: conn} do
1135 note = insert(:note_activity)
1136 user = User.get_by_ap_id(note.data["actor"])
1140 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1142 assert json_response(conn, 200) == []
1145 test "gets an users media", %{conn: conn} do
1146 note = insert(:note_activity)
1147 user = User.get_by_ap_id(note.data["actor"])
1149 file = %Plug.Upload{
1150 content_type: "image/jpg",
1151 path: Path.absname("test/fixtures/image.jpg"),
1152 filename: "an_image.jpg"
1156 TwitterAPI.upload(file, user, "json")
1160 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1164 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1166 assert [%{"id" => id}] = json_response(conn, 200)
1167 assert id == to_string(image_post.id)
1171 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1173 assert [%{"id" => id}] = json_response(conn, 200)
1174 assert id == to_string(image_post.id)
1177 test "gets a user's statuses without reblogs", %{conn: conn} do
1178 user = insert(:user)
1179 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1180 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1184 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1186 assert [%{"id" => id}] = json_response(conn, 200)
1187 assert id == to_string(post.id)
1191 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1193 assert [%{"id" => id}] = json_response(conn, 200)
1194 assert id == to_string(post.id)
1198 describe "user relationships" do
1199 test "returns the relationships for the current user", %{conn: conn} do
1200 user = insert(:user)
1201 other_user = insert(:user)
1202 {:ok, user} = User.follow(user, other_user)
1206 |> assign(:user, user)
1207 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1209 assert [relationship] = json_response(conn, 200)
1211 assert to_string(other_user.id) == relationship["id"]
1215 describe "locked accounts" do
1216 test "/api/v1/follow_requests works" do
1217 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1218 other_user = insert(:user)
1220 {:ok, _activity} = ActivityPub.follow(other_user, user)
1222 user = User.get_by_id(user.id)
1223 other_user = User.get_by_id(other_user.id)
1225 assert User.following?(other_user, user) == false
1229 |> assign(:user, user)
1230 |> get("/api/v1/follow_requests")
1232 assert [relationship] = json_response(conn, 200)
1233 assert to_string(other_user.id) == relationship["id"]
1236 test "/api/v1/follow_requests/:id/authorize works" do
1237 user = insert(:user, %{info: %User.Info{locked: true}})
1238 other_user = insert(:user)
1240 {:ok, _activity} = ActivityPub.follow(other_user, user)
1242 user = User.get_by_id(user.id)
1243 other_user = User.get_by_id(other_user.id)
1245 assert User.following?(other_user, user) == false
1249 |> assign(:user, user)
1250 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1252 assert relationship = json_response(conn, 200)
1253 assert to_string(other_user.id) == relationship["id"]
1255 user = User.get_by_id(user.id)
1256 other_user = User.get_by_id(other_user.id)
1258 assert User.following?(other_user, user) == true
1261 test "verify_credentials", %{conn: conn} do
1262 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "private"}})
1266 |> assign(:user, user)
1267 |> get("/api/v1/accounts/verify_credentials")
1269 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1270 assert id == to_string(user.id)
1273 test "/api/v1/follow_requests/:id/reject works" do
1274 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1275 other_user = insert(:user)
1277 {:ok, _activity} = ActivityPub.follow(other_user, user)
1279 user = User.get_by_id(user.id)
1283 |> assign(:user, user)
1284 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1286 assert relationship = json_response(conn, 200)
1287 assert to_string(other_user.id) == relationship["id"]
1289 user = User.get_by_id(user.id)
1290 other_user = User.get_by_id(other_user.id)
1292 assert User.following?(other_user, user) == false
1296 test "account fetching", %{conn: conn} do
1297 user = insert(:user)
1301 |> get("/api/v1/accounts/#{user.id}")
1303 assert %{"id" => id} = json_response(conn, 200)
1304 assert id == to_string(user.id)
1308 |> get("/api/v1/accounts/-1")
1310 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1313 test "account fetching also works nickname", %{conn: conn} do
1314 user = insert(:user)
1318 |> get("/api/v1/accounts/#{user.nickname}")
1320 assert %{"id" => id} = json_response(conn, 200)
1321 assert id == user.id
1324 test "media upload", %{conn: conn} do
1325 file = %Plug.Upload{
1326 content_type: "image/jpg",
1327 path: Path.absname("test/fixtures/image.jpg"),
1328 filename: "an_image.jpg"
1331 desc = "Description of the image"
1333 user = insert(:user)
1337 |> assign(:user, user)
1338 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1340 assert media = json_response(conn, 200)
1342 assert media["type"] == "image"
1343 assert media["description"] == desc
1346 object = Repo.get(Object, media["id"])
1347 assert object.data["actor"] == User.ap_id(user)
1350 test "hashtag timeline", %{conn: conn} do
1351 following = insert(:user)
1354 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1356 {:ok, [_activity]} =
1357 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1361 |> get("/api/v1/timelines/tag/2hu")
1363 assert [%{"id" => id}] = json_response(nconn, 200)
1365 assert id == to_string(activity.id)
1367 # works for different capitalization too
1370 |> get("/api/v1/timelines/tag/2HU")
1372 assert [%{"id" => id}] = json_response(nconn, 200)
1374 assert id == to_string(activity.id)
1378 test "multi-hashtag timeline", %{conn: conn} do
1379 user = insert(:user)
1381 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1382 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1383 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1387 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1389 [status_none, status_test1, status_test] = json_response(any_test, 200)
1391 assert to_string(activity_test.id) == status_test["id"]
1392 assert to_string(activity_test1.id) == status_test1["id"]
1393 assert to_string(activity_none.id) == status_none["id"]
1397 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1399 assert [status_test1] == json_response(restricted_test, 200)
1401 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1403 assert [status_none] == json_response(all_test, 200)
1406 test "getting followers", %{conn: conn} do
1407 user = insert(:user)
1408 other_user = insert(:user)
1409 {:ok, user} = User.follow(user, other_user)
1413 |> get("/api/v1/accounts/#{other_user.id}/followers")
1415 assert [%{"id" => id}] = json_response(conn, 200)
1416 assert id == to_string(user.id)
1419 test "getting followers, hide_followers", %{conn: conn} do
1420 user = insert(:user)
1421 other_user = insert(:user, %{info: %{hide_followers: true}})
1422 {:ok, _user} = User.follow(user, other_user)
1426 |> get("/api/v1/accounts/#{other_user.id}/followers")
1428 assert [] == json_response(conn, 200)
1431 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1432 user = insert(:user)
1433 other_user = insert(:user, %{info: %{hide_followers: true}})
1434 {:ok, _user} = User.follow(user, other_user)
1438 |> assign(:user, other_user)
1439 |> get("/api/v1/accounts/#{other_user.id}/followers")
1441 refute [] == json_response(conn, 200)
1444 test "getting followers, pagination", %{conn: conn} do
1445 user = insert(:user)
1446 follower1 = insert(:user)
1447 follower2 = insert(:user)
1448 follower3 = insert(:user)
1449 {:ok, _} = User.follow(follower1, user)
1450 {:ok, _} = User.follow(follower2, user)
1451 {:ok, _} = User.follow(follower3, user)
1455 |> assign(:user, user)
1459 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1461 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1462 assert id3 == follower3.id
1463 assert id2 == follower2.id
1467 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1469 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1470 assert id2 == follower2.id
1471 assert id1 == follower1.id
1475 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1477 assert [%{"id" => id2}] = json_response(res_conn, 200)
1478 assert id2 == follower2.id
1480 assert [link_header] = get_resp_header(res_conn, "link")
1481 assert link_header =~ ~r/since_id=#{follower2.id}/
1482 assert link_header =~ ~r/max_id=#{follower2.id}/
1485 test "getting following", %{conn: conn} do
1486 user = insert(:user)
1487 other_user = insert(:user)
1488 {:ok, user} = User.follow(user, other_user)
1492 |> get("/api/v1/accounts/#{user.id}/following")
1494 assert [%{"id" => id}] = json_response(conn, 200)
1495 assert id == to_string(other_user.id)
1498 test "getting following, hide_follows", %{conn: conn} do
1499 user = insert(:user, %{info: %{hide_follows: true}})
1500 other_user = insert(:user)
1501 {:ok, user} = User.follow(user, other_user)
1505 |> get("/api/v1/accounts/#{user.id}/following")
1507 assert [] == json_response(conn, 200)
1510 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1511 user = insert(:user, %{info: %{hide_follows: true}})
1512 other_user = insert(:user)
1513 {:ok, user} = User.follow(user, other_user)
1517 |> assign(:user, user)
1518 |> get("/api/v1/accounts/#{user.id}/following")
1520 refute [] == json_response(conn, 200)
1523 test "getting following, pagination", %{conn: conn} do
1524 user = insert(:user)
1525 following1 = insert(:user)
1526 following2 = insert(:user)
1527 following3 = insert(:user)
1528 {:ok, _} = User.follow(user, following1)
1529 {:ok, _} = User.follow(user, following2)
1530 {:ok, _} = User.follow(user, following3)
1534 |> assign(:user, user)
1538 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1540 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1541 assert id3 == following3.id
1542 assert id2 == following2.id
1546 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1548 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1549 assert id2 == following2.id
1550 assert id1 == following1.id
1554 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1556 assert [%{"id" => id2}] = json_response(res_conn, 200)
1557 assert id2 == following2.id
1559 assert [link_header] = get_resp_header(res_conn, "link")
1560 assert link_header =~ ~r/since_id=#{following2.id}/
1561 assert link_header =~ ~r/max_id=#{following2.id}/
1564 test "following / unfollowing a user", %{conn: conn} do
1565 user = insert(:user)
1566 other_user = insert(:user)
1570 |> assign(:user, user)
1571 |> post("/api/v1/accounts/#{other_user.id}/follow")
1573 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1575 user = User.get_by_id(user.id)
1579 |> assign(:user, user)
1580 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1582 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1584 user = User.get_by_id(user.id)
1588 |> assign(:user, user)
1589 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1591 assert %{"id" => id} = json_response(conn, 200)
1592 assert id == to_string(other_user.id)
1595 test "muting / unmuting a user", %{conn: conn} do
1596 user = insert(:user)
1597 other_user = insert(:user)
1601 |> assign(:user, user)
1602 |> post("/api/v1/accounts/#{other_user.id}/mute")
1604 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1606 user = User.get_by_id(user.id)
1610 |> assign(:user, user)
1611 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1613 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1616 test "getting a list of mutes", %{conn: conn} do
1617 user = insert(:user)
1618 other_user = insert(:user)
1620 {:ok, user} = User.mute(user, other_user)
1624 |> assign(:user, user)
1625 |> get("/api/v1/mutes")
1627 other_user_id = to_string(other_user.id)
1628 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1631 test "blocking / unblocking a user", %{conn: conn} do
1632 user = insert(:user)
1633 other_user = insert(:user)
1637 |> assign(:user, user)
1638 |> post("/api/v1/accounts/#{other_user.id}/block")
1640 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1642 user = User.get_by_id(user.id)
1646 |> assign(:user, user)
1647 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1649 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1652 test "getting a list of blocks", %{conn: conn} do
1653 user = insert(:user)
1654 other_user = insert(:user)
1656 {:ok, user} = User.block(user, other_user)
1660 |> assign(:user, user)
1661 |> get("/api/v1/blocks")
1663 other_user_id = to_string(other_user.id)
1664 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1667 test "blocking / unblocking a domain", %{conn: conn} do
1668 user = insert(:user)
1669 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1673 |> assign(:user, user)
1674 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1676 assert %{} = json_response(conn, 200)
1677 user = User.get_cached_by_ap_id(user.ap_id)
1678 assert User.blocks?(user, other_user)
1682 |> assign(:user, user)
1683 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1685 assert %{} = json_response(conn, 200)
1686 user = User.get_cached_by_ap_id(user.ap_id)
1687 refute User.blocks?(user, other_user)
1690 test "getting a list of domain blocks", %{conn: conn} do
1691 user = insert(:user)
1693 {:ok, user} = User.block_domain(user, "bad.site")
1694 {:ok, user} = User.block_domain(user, "even.worse.site")
1698 |> assign(:user, user)
1699 |> get("/api/v1/domain_blocks")
1701 domain_blocks = json_response(conn, 200)
1703 assert "bad.site" in domain_blocks
1704 assert "even.worse.site" in domain_blocks
1707 test "unimplemented follow_requests, blocks, domain blocks" do
1708 user = insert(:user)
1710 ["blocks", "domain_blocks", "follow_requests"]
1711 |> Enum.each(fn endpoint ->
1714 |> assign(:user, user)
1715 |> get("/api/v1/#{endpoint}")
1717 assert [] = json_response(conn, 200)
1721 test "account search", %{conn: conn} do
1722 user = insert(:user)
1723 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1724 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1728 |> assign(:user, user)
1729 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1730 |> json_response(200)
1732 result_ids = for result <- results, do: result["acct"]
1734 assert user_two.nickname in result_ids
1735 assert user_three.nickname in result_ids
1739 |> assign(:user, user)
1740 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
1741 |> json_response(200)
1743 result_ids = for result <- results, do: result["acct"]
1745 assert user_three.nickname in result_ids
1748 test "search", %{conn: conn} do
1749 user = insert(:user)
1750 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1751 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1753 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
1756 CommonAPI.post(user, %{
1757 "status" => "This is about 2hu, but private",
1758 "visibility" => "private"
1761 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
1765 |> get("/api/v1/search", %{"q" => "2hu"})
1767 assert results = json_response(conn, 200)
1769 [account | _] = results["accounts"]
1770 assert account["id"] == to_string(user_three.id)
1772 assert results["hashtags"] == []
1774 [status] = results["statuses"]
1775 assert status["id"] == to_string(activity.id)
1778 test "search fetches remote statuses", %{conn: conn} do
1782 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
1784 assert results = json_response(conn, 200)
1786 [status] = results["statuses"]
1787 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1791 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
1793 CommonAPI.post(insert(:user), %{
1794 "status" => "This is about 2hu, but private",
1795 "visibility" => "private"
1801 |> get("/api/v1/search", %{"q" => activity.data["object"]["id"]})
1803 assert results = json_response(conn, 200)
1805 [] = results["statuses"]
1809 test "search fetches remote accounts", %{conn: conn} do
1812 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
1814 assert results = json_response(conn, 200)
1815 [account] = results["accounts"]
1816 assert account["acct"] == "shp@social.heldscal.la"
1819 test "returns the favorites of a user", %{conn: conn} do
1820 user = insert(:user)
1821 other_user = insert(:user)
1823 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1824 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1826 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1830 |> assign(:user, user)
1831 |> get("/api/v1/favourites")
1833 assert [status] = json_response(first_conn, 200)
1834 assert status["id"] == to_string(activity.id)
1836 assert [{"link", _link_header}] =
1837 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1839 # Honours query params
1840 {:ok, second_activity} =
1841 CommonAPI.post(other_user, %{
1843 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1846 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1848 last_like = status["id"]
1852 |> assign(:user, user)
1853 |> get("/api/v1/favourites?since_id=#{last_like}")
1855 assert [second_status] = json_response(second_conn, 200)
1856 assert second_status["id"] == to_string(second_activity.id)
1860 |> assign(:user, user)
1861 |> get("/api/v1/favourites?limit=0")
1863 assert [] = json_response(third_conn, 200)
1866 describe "updating credentials" do
1867 test "updates the user's bio", %{conn: conn} do
1868 user = insert(:user)
1869 user2 = insert(:user)
1873 |> assign(:user, user)
1874 |> patch("/api/v1/accounts/update_credentials", %{
1875 "note" => "I drink #cofe with @#{user2.nickname}"
1878 assert user = json_response(conn, 200)
1880 assert user["note"] ==
1881 ~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=") <>
1883 ~s(" class="u-url mention" href=") <>
1884 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
1887 test "updates the user's locking status", %{conn: conn} do
1888 user = insert(:user)
1892 |> assign(:user, user)
1893 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
1895 assert user = json_response(conn, 200)
1896 assert user["locked"] == true
1899 test "updates the user's name", %{conn: conn} do
1900 user = insert(:user)
1904 |> assign(:user, user)
1905 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
1907 assert user = json_response(conn, 200)
1908 assert user["display_name"] == "markorepairs"
1911 test "updates the user's avatar", %{conn: conn} do
1912 user = insert(:user)
1914 new_avatar = %Plug.Upload{
1915 content_type: "image/jpg",
1916 path: Path.absname("test/fixtures/image.jpg"),
1917 filename: "an_image.jpg"
1922 |> assign(:user, user)
1923 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
1925 assert user_response = json_response(conn, 200)
1926 assert user_response["avatar"] != User.avatar_url(user)
1929 test "updates the user's banner", %{conn: conn} do
1930 user = insert(:user)
1932 new_header = %Plug.Upload{
1933 content_type: "image/jpg",
1934 path: Path.absname("test/fixtures/image.jpg"),
1935 filename: "an_image.jpg"
1940 |> assign(:user, user)
1941 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
1943 assert user_response = json_response(conn, 200)
1944 assert user_response["header"] != User.banner_url(user)
1947 test "requires 'write' permission", %{conn: conn} do
1948 token1 = insert(:oauth_token, scopes: ["read"])
1949 token2 = insert(:oauth_token, scopes: ["write", "follow"])
1951 for token <- [token1, token2] do
1954 |> put_req_header("authorization", "Bearer #{token.token}")
1955 |> patch("/api/v1/accounts/update_credentials", %{})
1957 if token == token1 do
1958 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
1960 assert json_response(conn, 200)
1966 test "get instance information", %{conn: conn} do
1967 conn = get(conn, "/api/v1/instance")
1968 assert result = json_response(conn, 200)
1970 # Note: not checking for "max_toot_chars" since it's optional
1978 "streaming_api" => _
1983 "registrations" => _
1987 test "get instance stats", %{conn: conn} do
1988 user = insert(:user, %{local: true})
1990 user2 = insert(:user, %{local: true})
1991 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1993 insert(:user, %{local: false, nickname: "u@peer1.com"})
1994 insert(:user, %{local: false, nickname: "u@peer2.com"})
1996 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
1998 # Stats should count users with missing or nil `info.deactivated` value
1999 user = User.get_by_id(user.id)
2000 info_change = Changeset.change(user.info, %{deactivated: nil})
2004 |> Changeset.change()
2005 |> Changeset.put_embed(:info, info_change)
2006 |> User.update_and_set_cache()
2008 Pleroma.Stats.update_stats()
2010 conn = get(conn, "/api/v1/instance")
2012 assert result = json_response(conn, 200)
2014 stats = result["stats"]
2017 assert stats["user_count"] == 1
2018 assert stats["status_count"] == 1
2019 assert stats["domain_count"] == 2
2022 test "get peers", %{conn: conn} do
2023 insert(:user, %{local: false, nickname: "u@peer1.com"})
2024 insert(:user, %{local: false, nickname: "u@peer2.com"})
2026 Pleroma.Stats.update_stats()
2028 conn = get(conn, "/api/v1/instance/peers")
2030 assert result = json_response(conn, 200)
2032 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2035 test "put settings", %{conn: conn} do
2036 user = insert(:user)
2040 |> assign(:user, user)
2041 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2043 assert _result = json_response(conn, 200)
2045 user = User.get_cached_by_ap_id(user.ap_id)
2046 assert user.info.settings == %{"programming" => "socks"}
2049 describe "pinned statuses" do
2051 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2053 user = insert(:user)
2054 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2056 [user: user, activity: activity]
2059 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2060 {:ok, _} = CommonAPI.pin(activity.id, user)
2064 |> assign(:user, user)
2065 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2066 |> json_response(200)
2068 id_str = to_string(activity.id)
2070 assert [%{"id" => ^id_str, "pinned" => true}] = result
2073 test "pin status", %{conn: conn, user: user, activity: activity} do
2074 id_str = to_string(activity.id)
2076 assert %{"id" => ^id_str, "pinned" => true} =
2078 |> assign(:user, user)
2079 |> post("/api/v1/statuses/#{activity.id}/pin")
2080 |> json_response(200)
2082 assert [%{"id" => ^id_str, "pinned" => true}] =
2084 |> assign(:user, user)
2085 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2086 |> json_response(200)
2089 test "unpin status", %{conn: conn, user: user, activity: activity} do
2090 {:ok, _} = CommonAPI.pin(activity.id, user)
2092 id_str = to_string(activity.id)
2093 user = refresh_record(user)
2095 assert %{"id" => ^id_str, "pinned" => false} =
2097 |> assign(:user, user)
2098 |> post("/api/v1/statuses/#{activity.id}/unpin")
2099 |> json_response(200)
2103 |> assign(:user, user)
2104 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2105 |> json_response(200)
2108 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2109 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2111 id_str_one = to_string(activity_one.id)
2113 assert %{"id" => ^id_str_one, "pinned" => true} =
2115 |> assign(:user, user)
2116 |> post("/api/v1/statuses/#{id_str_one}/pin")
2117 |> json_response(200)
2119 user = refresh_record(user)
2121 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2123 |> assign(:user, user)
2124 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2125 |> json_response(400)
2128 test "Status rich-media Card", %{conn: conn, user: user} do
2129 Pleroma.Config.put([:rich_media, :enabled], true)
2130 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2134 |> get("/api/v1/statuses/#{activity.id}/card")
2135 |> json_response(200)
2137 assert response == %{
2138 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2139 "provider_name" => "www.imdb.com",
2140 "provider_url" => "http://www.imdb.com",
2141 "title" => "The Rock",
2143 "url" => "http://www.imdb.com/title/tt0117500/",
2144 "description" => nil,
2147 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2148 "title" => "The Rock",
2149 "type" => "video.movie",
2150 "url" => "http://www.imdb.com/title/tt0117500/"
2155 # works with private posts
2157 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2161 |> assign(:user, user)
2162 |> get("/api/v1/statuses/#{activity.id}/card")
2163 |> json_response(200)
2165 assert response_two == response
2167 Pleroma.Config.put([:rich_media, :enabled], false)
2172 user = insert(:user)
2173 for_user = insert(:user)
2176 CommonAPI.post(user, %{
2177 "status" => "heweoo?"
2181 CommonAPI.post(user, %{
2182 "status" => "heweoo!"
2187 |> assign(:user, for_user)
2188 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2190 assert json_response(response1, 200)["bookmarked"] == true
2194 |> assign(:user, for_user)
2195 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2197 assert json_response(response2, 200)["bookmarked"] == true
2201 |> assign(:user, for_user)
2202 |> get("/api/v1/bookmarks")
2204 assert [json_response(response2, 200), json_response(response1, 200)] ==
2205 json_response(bookmarks, 200)
2209 |> assign(:user, for_user)
2210 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2212 assert json_response(response1, 200)["bookmarked"] == false
2216 |> assign(:user, for_user)
2217 |> get("/api/v1/bookmarks")
2219 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2222 describe "conversation muting" do
2224 user = insert(:user)
2225 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2227 [user: user, activity: activity]
2230 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2231 id_str = to_string(activity.id)
2233 assert %{"id" => ^id_str, "muted" => true} =
2235 |> assign(:user, user)
2236 |> post("/api/v1/statuses/#{activity.id}/mute")
2237 |> json_response(200)
2240 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2241 {:ok, _} = CommonAPI.add_mute(user, activity)
2243 id_str = to_string(activity.id)
2244 user = refresh_record(user)
2246 assert %{"id" => ^id_str, "muted" => false} =
2248 |> assign(:user, user)
2249 |> post("/api/v1/statuses/#{activity.id}/unmute")
2250 |> json_response(200)
2254 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2255 user = insert(:user)
2259 |> assign(:user, user)
2260 |> get("/api/v1/pleroma/flavour")
2262 assert "glitch" == json_response(get_old_flavour, 200)
2266 |> assign(:user, user)
2267 |> post("/api/v1/pleroma/flavour/vanilla")
2269 assert "vanilla" == json_response(set_flavour, 200)
2273 |> assign(:user, user)
2274 |> post("/api/v1/pleroma/flavour/vanilla")
2276 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2279 describe "reports" do
2281 reporter = insert(:user)
2282 target_user = insert(:user)
2284 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2286 [reporter: reporter, target_user: target_user, activity: activity]
2289 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2290 assert %{"action_taken" => false, "id" => _} =
2292 |> assign(:user, reporter)
2293 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2294 |> json_response(200)
2297 test "submit a report with statuses and comment", %{
2300 target_user: target_user,
2303 assert %{"action_taken" => false, "id" => _} =
2305 |> assign(:user, reporter)
2306 |> post("/api/v1/reports", %{
2307 "account_id" => target_user.id,
2308 "status_ids" => [activity.id],
2309 "comment" => "bad status!"
2311 |> json_response(200)
2314 test "account_id is required", %{
2319 assert %{"error" => "Valid `account_id` required"} =
2321 |> assign(:user, reporter)
2322 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2323 |> json_response(400)
2326 test "comment must be up to the size specified in the config", %{
2329 target_user: target_user
2331 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2332 comment = String.pad_trailing("a", max_size + 1, "a")
2334 error = %{"error" => "Comment must be up to #{max_size} characters"}
2338 |> assign(:user, reporter)
2339 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2340 |> json_response(400)
2344 describe "link headers" do
2345 test "preserves parameters in link headers", %{conn: conn} do
2346 user = insert(:user)
2347 other_user = insert(:user)
2350 CommonAPI.post(other_user, %{
2351 "status" => "hi @#{user.nickname}",
2352 "visibility" => "public"
2356 CommonAPI.post(other_user, %{
2357 "status" => "hi @#{user.nickname}",
2358 "visibility" => "public"
2361 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2362 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2366 |> assign(:user, user)
2367 |> get("/api/v1/notifications", %{media_only: true})
2369 assert [link_header] = get_resp_header(conn, "link")
2370 assert link_header =~ ~r/media_only=true/
2371 assert link_header =~ ~r/since_id=#{notification2.id}/
2372 assert link_header =~ ~r/max_id=#{notification1.id}/
2376 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2377 # Need to set an old-style integer ID to reproduce the problem
2378 # (these are no longer assigned to new accounts but were preserved
2379 # for existing accounts during the migration to flakeIDs)
2380 user_one = insert(:user, %{id: 1212})
2381 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2385 |> get("/api/v1/accounts/#{user_one.id}")
2389 |> get("/api/v1/accounts/#{user_two.nickname}")
2393 |> get("/api/v1/accounts/#{user_two.id}")
2395 acc_one = json_response(resp_one, 200)
2396 acc_two = json_response(resp_two, 200)
2397 acc_three = json_response(resp_three, 200)
2398 refute acc_one == acc_two
2399 assert acc_two == acc_three
2402 describe "custom emoji" do
2403 test "with tags", %{conn: conn} do
2406 |> get("/api/v1/custom_emojis")
2407 |> json_response(200)
2409 assert Map.has_key?(emoji, "shortcode")
2410 assert Map.has_key?(emoji, "static_url")
2411 assert Map.has_key?(emoji, "tags")
2412 assert is_list(emoji["tags"])
2413 assert Map.has_key?(emoji, "url")
2414 assert Map.has_key?(emoji, "visible_in_picker")
2418 describe "index/2 redirections" do
2419 setup %{conn: conn} do
2423 signing_salt: "cooldude"
2428 |> Plug.Session.call(Plug.Session.init(session_opts))
2431 test_path = "/web/statuses/test"
2432 %{conn: conn, path: test_path}
2435 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2436 conn = get(conn, path)
2438 assert conn.status == 302
2439 assert redirected_to(conn) == "/web/login"
2442 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2443 token = insert(:oauth_token)
2447 |> assign(:user, token.user)
2448 |> put_session(:oauth_token, token.token)
2451 assert conn.status == 200
2454 test "saves referer path to session", %{conn: conn, path: path} do
2455 conn = get(conn, path)
2456 return_to = Plug.Conn.get_session(conn, :return_to)
2458 assert return_to == path
2461 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2462 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2463 auth = insert(:oauth_authorization, app: app)
2467 |> put_session(:return_to, path)
2468 |> get("/web/login", %{code: auth.token})
2470 assert conn.status == 302
2471 assert redirected_to(conn) == path
2474 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2475 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2476 auth = insert(:oauth_authorization, app: app)
2478 conn = get(conn, "/web/login", %{code: auth.token})
2480 assert conn.status == 302
2481 assert redirected_to(conn) == "/web/getting-started"
2485 describe "scheduled activities" do
2486 test "creates a scheduled activity", %{conn: conn} do
2487 user = insert(:user)
2488 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2492 |> assign(:user, user)
2493 |> post("/api/v1/statuses", %{
2494 "status" => "scheduled",
2495 "scheduled_at" => scheduled_at
2498 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2499 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2500 assert [] == Repo.all(Activity)
2503 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2504 user = insert(:user)
2505 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2507 file = %Plug.Upload{
2508 content_type: "image/jpg",
2509 path: Path.absname("test/fixtures/image.jpg"),
2510 filename: "an_image.jpg"
2513 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
2517 |> assign(:user, user)
2518 |> post("/api/v1/statuses", %{
2519 "media_ids" => [to_string(upload.id)],
2520 "status" => "scheduled",
2521 "scheduled_at" => scheduled_at
2524 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
2525 assert %{"type" => "image"} = media_attachment
2528 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
2530 user = insert(:user)
2533 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
2537 |> assign(:user, user)
2538 |> post("/api/v1/statuses", %{
2539 "status" => "not scheduled",
2540 "scheduled_at" => scheduled_at
2543 assert %{"content" => "not scheduled"} = json_response(conn, 200)
2544 assert [] == Repo.all(ScheduledActivity)
2547 test "returns error when daily user limit is exceeded", %{conn: conn} do
2548 user = insert(:user)
2551 NaiveDateTime.utc_now()
2552 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2553 |> NaiveDateTime.to_iso8601()
2555 attrs = %{params: %{}, scheduled_at: today}
2556 {:ok, _} = ScheduledActivity.create(user, attrs)
2557 {:ok, _} = ScheduledActivity.create(user, attrs)
2561 |> assign(:user, user)
2562 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
2564 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
2567 test "returns error when total user limit is exceeded", %{conn: conn} do
2568 user = insert(:user)
2571 NaiveDateTime.utc_now()
2572 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2573 |> NaiveDateTime.to_iso8601()
2576 NaiveDateTime.utc_now()
2577 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
2578 |> NaiveDateTime.to_iso8601()
2580 attrs = %{params: %{}, scheduled_at: today}
2581 {:ok, _} = ScheduledActivity.create(user, attrs)
2582 {:ok, _} = ScheduledActivity.create(user, attrs)
2583 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
2587 |> assign(:user, user)
2588 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
2590 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
2593 test "shows scheduled activities", %{conn: conn} do
2594 user = insert(:user)
2595 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
2596 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
2597 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
2598 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
2602 |> assign(:user, user)
2607 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
2609 result = json_response(conn_res, 200)
2610 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2615 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
2617 result = json_response(conn_res, 200)
2618 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
2623 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
2625 result = json_response(conn_res, 200)
2626 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2629 test "shows a scheduled activity", %{conn: conn} do
2630 user = insert(:user)
2631 scheduled_activity = insert(:scheduled_activity, user: user)
2635 |> assign(:user, user)
2636 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2638 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
2639 assert scheduled_activity_id == scheduled_activity.id |> to_string()
2643 |> assign(:user, user)
2644 |> get("/api/v1/scheduled_statuses/404")
2646 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2649 test "updates a scheduled activity", %{conn: conn} do
2650 user = insert(:user)
2651 scheduled_activity = insert(:scheduled_activity, user: user)
2654 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2658 |> assign(:user, user)
2659 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
2660 scheduled_at: new_scheduled_at
2663 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
2664 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
2668 |> assign(:user, user)
2669 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
2671 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2674 test "deletes a scheduled activity", %{conn: conn} do
2675 user = insert(:user)
2676 scheduled_activity = insert(:scheduled_activity, user: user)
2680 |> assign(:user, user)
2681 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2683 assert %{} = json_response(res_conn, 200)
2684 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
2688 |> assign(:user, user)
2689 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2691 assert %{"error" => "Record not found"} = json_response(res_conn, 404)