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 describe "posting polls" do
150 test "posting a poll", %{conn: conn} do
152 time = NaiveDateTime.utc_now()
156 |> assign(:user, user)
157 |> post("/api/v1/statuses", %{
158 "status" => "Who is the #bestgrill?",
159 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
162 response = json_response(conn, 200)
164 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
165 title in ["Rei", "Asuka", "Misato"]
168 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
169 refute response["poll"]["expred"]
172 test "option limit is enforced", %{conn: conn} do
174 limit = Pleroma.Config.get([:instance, :poll_limits, :max_options])
178 |> assign(:user, user)
179 |> post("/api/v1/statuses", %{
181 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
184 %{"error" => error} = json_response(conn, 422)
185 assert error == "Poll can't contain more than #{limit} options"
188 test "option character limit is enforced", %{conn: conn} do
190 limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars])
194 |> assign(:user, user)
195 |> post("/api/v1/statuses", %{
198 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
203 %{"error" => error} = json_response(conn, 422)
204 assert error == "Poll options cannot be longer than #{limit} characters each"
207 test "minimal date limit is enforced", %{conn: conn} do
209 limit = Pleroma.Config.get([:instance, :poll_limits, :min_expiration])
213 |> assign(:user, user)
214 |> post("/api/v1/statuses", %{
215 "status" => "imagine arbitrary limits",
217 "options" => ["this post was made by pleroma gang"],
218 "expires_in" => limit - 1
222 %{"error" => error} = json_response(conn, 422)
223 assert error == "Expiration date is too soon"
226 test "maximum date limit is enforced", %{conn: conn} do
228 limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration])
232 |> assign(:user, user)
233 |> post("/api/v1/statuses", %{
234 "status" => "imagine arbitrary limits",
236 "options" => ["this post was made by pleroma gang"],
237 "expires_in" => limit + 1
241 %{"error" => error} = json_response(conn, 422)
242 assert error == "Expiration date is too far in the future"
246 test "posting a sensitive status", %{conn: conn} do
251 |> assign(:user, user)
252 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
254 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
255 assert Activity.get_by_id(id)
258 test "posting a fake status", %{conn: conn} do
263 |> assign(:user, user)
264 |> post("/api/v1/statuses", %{
266 "\"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"
269 real_status = json_response(real_conn, 200)
272 assert Object.get_by_ap_id(real_status["uri"])
276 |> Map.put("id", nil)
277 |> Map.put("url", nil)
278 |> Map.put("uri", nil)
279 |> Map.put("created_at", nil)
280 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
284 |> assign(:user, user)
285 |> post("/api/v1/statuses", %{
287 "\"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",
291 fake_status = json_response(fake_conn, 200)
294 refute Object.get_by_ap_id(fake_status["uri"])
298 |> Map.put("id", nil)
299 |> Map.put("url", nil)
300 |> Map.put("uri", nil)
301 |> Map.put("created_at", nil)
302 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
304 assert real_status == fake_status
307 test "posting a status with OGP link preview", %{conn: conn} do
308 Pleroma.Config.put([:rich_media, :enabled], true)
313 |> assign(:user, user)
314 |> post("/api/v1/statuses", %{
315 "status" => "http://example.com/ogp"
318 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
319 assert Activity.get_by_id(id)
320 Pleroma.Config.put([:rich_media, :enabled], false)
323 test "posting a direct status", %{conn: conn} do
324 user1 = insert(:user)
325 user2 = insert(:user)
326 content = "direct cofe @#{user2.nickname}"
330 |> assign(:user, user1)
331 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
333 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
334 assert activity = Activity.get_by_id(id)
335 assert activity.recipients == [user2.ap_id, user1.ap_id]
336 assert activity.data["to"] == [user2.ap_id]
337 assert activity.data["cc"] == []
340 test "direct timeline", %{conn: conn} do
341 user_one = insert(:user)
342 user_two = insert(:user)
344 {:ok, user_two} = User.follow(user_two, user_one)
347 CommonAPI.post(user_one, %{
348 "status" => "Hi @#{user_two.nickname}!",
349 "visibility" => "direct"
352 {:ok, _follower_only} =
353 CommonAPI.post(user_one, %{
354 "status" => "Hi @#{user_two.nickname}!",
355 "visibility" => "private"
358 # Only direct should be visible here
361 |> assign(:user, user_two)
362 |> get("api/v1/timelines/direct")
364 [status] = json_response(res_conn, 200)
366 assert %{"visibility" => "direct"} = status
367 assert status["url"] != direct.data["id"]
369 # User should be able to see his own direct message
372 |> assign(:user, user_one)
373 |> get("api/v1/timelines/direct")
375 [status] = json_response(res_conn, 200)
377 assert %{"visibility" => "direct"} = status
379 # Both should be visible here
382 |> assign(:user, user_two)
383 |> get("api/v1/timelines/home")
385 [_s1, _s2] = json_response(res_conn, 200)
388 Enum.each(1..20, fn _ ->
390 CommonAPI.post(user_one, %{
391 "status" => "Hi @#{user_two.nickname}!",
392 "visibility" => "direct"
398 |> assign(:user, user_two)
399 |> get("api/v1/timelines/direct")
401 statuses = json_response(res_conn, 200)
402 assert length(statuses) == 20
406 |> assign(:user, user_two)
407 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
409 [status] = json_response(res_conn, 200)
411 assert status["url"] != direct.data["id"]
414 test "Conversations", %{conn: conn} do
415 user_one = insert(:user)
416 user_two = insert(:user)
418 {:ok, user_two} = User.follow(user_two, user_one)
421 CommonAPI.post(user_one, %{
422 "status" => "Hi @#{user_two.nickname}!",
423 "visibility" => "direct"
426 {:ok, _follower_only} =
427 CommonAPI.post(user_one, %{
428 "status" => "Hi @#{user_two.nickname}!",
429 "visibility" => "private"
434 |> assign(:user, user_one)
435 |> get("/api/v1/conversations")
437 assert response = json_response(res_conn, 200)
442 "accounts" => res_accounts,
443 "last_status" => res_last_status,
448 assert length(res_accounts) == 2
449 assert is_binary(res_id)
450 assert unread == true
451 assert res_last_status["id"] == direct.id
453 # Apparently undocumented API endpoint
456 |> assign(:user, user_one)
457 |> post("/api/v1/conversations/#{res_id}/read")
459 assert response = json_response(res_conn, 200)
460 assert length(response["accounts"]) == 2
461 assert response["last_status"]["id"] == direct.id
462 assert response["unread"] == false
464 # (vanilla) Mastodon frontend behaviour
467 |> assign(:user, user_one)
468 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
470 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
473 test "doesn't include DMs from blocked users", %{conn: conn} do
474 blocker = insert(:user)
475 blocked = insert(:user)
477 {:ok, blocker} = User.block(blocker, blocked)
479 {:ok, _blocked_direct} =
480 CommonAPI.post(blocked, %{
481 "status" => "Hi @#{blocker.nickname}!",
482 "visibility" => "direct"
486 CommonAPI.post(user, %{
487 "status" => "Hi @#{blocker.nickname}!",
488 "visibility" => "direct"
493 |> assign(:user, user)
494 |> get("api/v1/timelines/direct")
496 [status] = json_response(res_conn, 200)
497 assert status["id"] == direct.id
500 test "replying to a status", %{conn: conn} do
503 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
507 |> assign(:user, user)
508 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
510 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
512 activity = Activity.get_by_id(id)
514 assert activity.data["context"] == replied_to.data["context"]
515 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
518 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
523 |> assign(:user, user)
524 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
526 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
528 activity = Activity.get_by_id(id)
533 test "verify_credentials", %{conn: conn} do
538 |> assign(:user, user)
539 |> get("/api/v1/accounts/verify_credentials")
541 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
542 assert id == to_string(user.id)
545 test "verify_credentials default scope unlisted", %{conn: conn} do
546 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
550 |> assign(:user, user)
551 |> get("/api/v1/accounts/verify_credentials")
553 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
554 assert id == to_string(user.id)
557 test "apps/verify_credentials", %{conn: conn} do
558 token = insert(:oauth_token)
562 |> assign(:user, token.user)
563 |> assign(:token, token)
564 |> get("/api/v1/apps/verify_credentials")
566 app = Repo.preload(token, :app).app
569 "name" => app.client_name,
570 "website" => app.website,
571 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
574 assert expected == json_response(conn, 200)
577 test "creates an oauth app", %{conn: conn} do
579 app_attrs = build(:oauth_app)
583 |> assign(:user, user)
584 |> post("/api/v1/apps", %{
585 client_name: app_attrs.client_name,
586 redirect_uris: app_attrs.redirect_uris
589 [app] = Repo.all(App)
592 "name" => app.client_name,
593 "website" => app.website,
594 "client_id" => app.client_id,
595 "client_secret" => app.client_secret,
596 "id" => app.id |> to_string(),
597 "redirect_uri" => app.redirect_uris,
598 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
601 assert expected == json_response(conn, 200)
604 test "get a status", %{conn: conn} do
605 activity = insert(:note_activity)
609 |> get("/api/v1/statuses/#{activity.id}")
611 assert %{"id" => id} = json_response(conn, 200)
612 assert id == to_string(activity.id)
615 describe "deleting a status" do
616 test "when you created it", %{conn: conn} do
617 activity = insert(:note_activity)
618 author = User.get_cached_by_ap_id(activity.data["actor"])
622 |> assign(:user, author)
623 |> delete("/api/v1/statuses/#{activity.id}")
625 assert %{} = json_response(conn, 200)
627 refute Activity.get_by_id(activity.id)
630 test "when you didn't create it", %{conn: conn} do
631 activity = insert(:note_activity)
636 |> assign(:user, user)
637 |> delete("/api/v1/statuses/#{activity.id}")
639 assert %{"error" => _} = json_response(conn, 403)
641 assert Activity.get_by_id(activity.id) == activity
644 test "when you're an admin or moderator", %{conn: conn} do
645 activity1 = insert(:note_activity)
646 activity2 = insert(:note_activity)
647 admin = insert(:user, info: %{is_admin: true})
648 moderator = insert(:user, info: %{is_moderator: true})
652 |> assign(:user, admin)
653 |> delete("/api/v1/statuses/#{activity1.id}")
655 assert %{} = json_response(res_conn, 200)
659 |> assign(:user, moderator)
660 |> delete("/api/v1/statuses/#{activity2.id}")
662 assert %{} = json_response(res_conn, 200)
664 refute Activity.get_by_id(activity1.id)
665 refute Activity.get_by_id(activity2.id)
669 describe "filters" do
670 test "creating a filter", %{conn: conn} do
673 filter = %Pleroma.Filter{
680 |> assign(:user, user)
681 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
683 assert response = json_response(conn, 200)
684 assert response["phrase"] == filter.phrase
685 assert response["context"] == filter.context
686 assert response["irreversible"] == false
687 assert response["id"] != nil
688 assert response["id"] != ""
691 test "fetching a list of filters", %{conn: conn} do
694 query_one = %Pleroma.Filter{
701 query_two = %Pleroma.Filter{
708 {:ok, filter_one} = Pleroma.Filter.create(query_one)
709 {:ok, filter_two} = Pleroma.Filter.create(query_two)
713 |> assign(:user, user)
714 |> get("/api/v1/filters")
715 |> json_response(200)
721 filters: [filter_two, filter_one]
725 test "get a filter", %{conn: conn} do
728 query = %Pleroma.Filter{
735 {:ok, filter} = Pleroma.Filter.create(query)
739 |> assign(:user, user)
740 |> get("/api/v1/filters/#{filter.filter_id}")
742 assert _response = json_response(conn, 200)
745 test "update a filter", %{conn: conn} do
748 query = %Pleroma.Filter{
755 {:ok, _filter} = Pleroma.Filter.create(query)
757 new = %Pleroma.Filter{
764 |> assign(:user, user)
765 |> put("/api/v1/filters/#{query.filter_id}", %{
770 assert response = json_response(conn, 200)
771 assert response["phrase"] == new.phrase
772 assert response["context"] == new.context
775 test "delete a filter", %{conn: conn} do
778 query = %Pleroma.Filter{
785 {:ok, filter} = Pleroma.Filter.create(query)
789 |> assign(:user, user)
790 |> delete("/api/v1/filters/#{filter.filter_id}")
792 assert response = json_response(conn, 200)
793 assert response == %{}
798 test "creating a list", %{conn: conn} do
803 |> assign(:user, user)
804 |> post("/api/v1/lists", %{"title" => "cuties"})
806 assert %{"title" => title} = json_response(conn, 200)
807 assert title == "cuties"
810 test "adding users to a list", %{conn: conn} do
812 other_user = insert(:user)
813 {:ok, list} = Pleroma.List.create("name", user)
817 |> assign(:user, user)
818 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
820 assert %{} == json_response(conn, 200)
821 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
822 assert following == [other_user.follower_address]
825 test "removing users from a list", %{conn: conn} do
827 other_user = insert(:user)
828 third_user = insert(:user)
829 {:ok, list} = Pleroma.List.create("name", user)
830 {:ok, list} = Pleroma.List.follow(list, other_user)
831 {:ok, list} = Pleroma.List.follow(list, third_user)
835 |> assign(:user, user)
836 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
838 assert %{} == json_response(conn, 200)
839 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
840 assert following == [third_user.follower_address]
843 test "listing users in a list", %{conn: conn} do
845 other_user = insert(:user)
846 {:ok, list} = Pleroma.List.create("name", user)
847 {:ok, list} = Pleroma.List.follow(list, other_user)
851 |> assign(:user, user)
852 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
854 assert [%{"id" => id}] = json_response(conn, 200)
855 assert id == to_string(other_user.id)
858 test "retrieving a list", %{conn: conn} do
860 {:ok, list} = Pleroma.List.create("name", user)
864 |> assign(:user, user)
865 |> get("/api/v1/lists/#{list.id}")
867 assert %{"id" => id} = json_response(conn, 200)
868 assert id == to_string(list.id)
871 test "renaming a list", %{conn: conn} do
873 {:ok, list} = Pleroma.List.create("name", user)
877 |> assign(:user, user)
878 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
880 assert %{"title" => name} = json_response(conn, 200)
881 assert name == "newname"
884 test "deleting a list", %{conn: conn} do
886 {:ok, list} = Pleroma.List.create("name", user)
890 |> assign(:user, user)
891 |> delete("/api/v1/lists/#{list.id}")
893 assert %{} = json_response(conn, 200)
894 assert is_nil(Repo.get(Pleroma.List, list.id))
897 test "list timeline", %{conn: conn} do
899 other_user = insert(:user)
900 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
901 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
902 {:ok, list} = Pleroma.List.create("name", user)
903 {:ok, list} = Pleroma.List.follow(list, other_user)
907 |> assign(:user, user)
908 |> get("/api/v1/timelines/list/#{list.id}")
910 assert [%{"id" => id}] = json_response(conn, 200)
912 assert id == to_string(activity_two.id)
915 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
917 other_user = insert(:user)
918 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
920 {:ok, _activity_two} =
921 TwitterAPI.create_status(other_user, %{
922 "status" => "Marisa is cute.",
923 "visibility" => "private"
926 {:ok, list} = Pleroma.List.create("name", user)
927 {:ok, list} = Pleroma.List.follow(list, other_user)
931 |> assign(:user, user)
932 |> get("/api/v1/timelines/list/#{list.id}")
934 assert [%{"id" => id}] = json_response(conn, 200)
936 assert id == to_string(activity_one.id)
940 describe "notifications" do
941 test "list of notifications", %{conn: conn} do
943 other_user = insert(:user)
946 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
948 {:ok, [_notification]} = Notification.create_notifications(activity)
952 |> assign(:user, user)
953 |> get("/api/v1/notifications")
956 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
958 }\">@<span>#{user.nickname}</span></a></span>"
960 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
961 assert response == expected_response
964 test "getting a single notification", %{conn: conn} do
966 other_user = insert(:user)
969 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
971 {:ok, [notification]} = Notification.create_notifications(activity)
975 |> assign(:user, user)
976 |> get("/api/v1/notifications/#{notification.id}")
979 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
981 }\">@<span>#{user.nickname}</span></a></span>"
983 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
984 assert response == expected_response
987 test "dismissing a single notification", %{conn: conn} do
989 other_user = insert(:user)
992 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
994 {:ok, [notification]} = Notification.create_notifications(activity)
998 |> assign(:user, user)
999 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1001 assert %{} = json_response(conn, 200)
1004 test "clearing all notifications", %{conn: conn} do
1005 user = insert(:user)
1006 other_user = insert(:user)
1009 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
1011 {:ok, [_notification]} = Notification.create_notifications(activity)
1015 |> assign(:user, user)
1016 |> post("/api/v1/notifications/clear")
1018 assert %{} = json_response(conn, 200)
1022 |> assign(:user, user)
1023 |> get("/api/v1/notifications")
1025 assert all = json_response(conn, 200)
1029 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1030 user = insert(:user)
1031 other_user = insert(:user)
1033 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1034 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1035 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1036 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1038 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1039 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1040 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1041 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1045 |> assign(:user, user)
1050 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1052 result = json_response(conn_res, 200)
1053 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1058 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1060 result = json_response(conn_res, 200)
1061 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1066 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1068 result = json_response(conn_res, 200)
1069 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1072 test "filters notifications using exclude_types", %{conn: conn} do
1073 user = insert(:user)
1074 other_user = insert(:user)
1076 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1077 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1078 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1079 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1080 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1082 mention_notification_id =
1083 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1085 favorite_notification_id =
1086 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1088 reblog_notification_id =
1089 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1091 follow_notification_id =
1092 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1096 |> assign(:user, user)
1099 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1101 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1104 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1106 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1109 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1111 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1114 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1116 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1119 test "destroy multiple", %{conn: conn} do
1120 user = insert(:user)
1121 other_user = insert(:user)
1123 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1124 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1125 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1126 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1128 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1129 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1130 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1131 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1135 |> assign(:user, user)
1139 |> get("/api/v1/notifications")
1141 result = json_response(conn_res, 200)
1142 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1146 |> assign(:user, other_user)
1150 |> get("/api/v1/notifications")
1152 result = json_response(conn_res, 200)
1153 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1157 |> delete("/api/v1/notifications/destroy_multiple", %{
1158 "ids" => [notification1_id, notification2_id]
1161 assert json_response(conn_destroy, 200) == %{}
1165 |> get("/api/v1/notifications")
1167 result = json_response(conn_res, 200)
1168 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1172 describe "reblogging" do
1173 test "reblogs and returns the reblogged status", %{conn: conn} do
1174 activity = insert(:note_activity)
1175 user = insert(:user)
1179 |> assign(:user, user)
1180 |> post("/api/v1/statuses/#{activity.id}/reblog")
1183 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1185 } = json_response(conn, 200)
1187 assert to_string(activity.id) == id
1190 test "reblogged status for another user", %{conn: conn} do
1191 activity = insert(:note_activity)
1192 user1 = insert(:user)
1193 user2 = insert(:user)
1194 user3 = insert(:user)
1195 CommonAPI.favorite(activity.id, user2)
1196 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1197 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1198 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1202 |> assign(:user, user3)
1203 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1206 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1207 "reblogged" => false,
1208 "favourited" => false,
1209 "bookmarked" => false
1210 } = json_response(conn_res, 200)
1214 |> assign(:user, user2)
1215 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1218 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1219 "reblogged" => true,
1220 "favourited" => true,
1221 "bookmarked" => true
1222 } = json_response(conn_res, 200)
1224 assert to_string(activity.id) == id
1228 describe "unreblogging" do
1229 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1230 activity = insert(:note_activity)
1231 user = insert(:user)
1233 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1237 |> assign(:user, user)
1238 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1240 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1242 assert to_string(activity.id) == id
1246 describe "favoriting" do
1247 test "favs a status and returns it", %{conn: conn} do
1248 activity = insert(:note_activity)
1249 user = insert(:user)
1253 |> assign(:user, user)
1254 |> post("/api/v1/statuses/#{activity.id}/favourite")
1256 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1257 json_response(conn, 200)
1259 assert to_string(activity.id) == id
1262 test "returns 500 for a wrong id", %{conn: conn} do
1263 user = insert(:user)
1267 |> assign(:user, user)
1268 |> post("/api/v1/statuses/1/favourite")
1269 |> json_response(500)
1271 assert resp == "Something went wrong"
1275 describe "unfavoriting" do
1276 test "unfavorites a status and returns it", %{conn: conn} do
1277 activity = insert(:note_activity)
1278 user = insert(:user)
1280 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1284 |> assign(:user, user)
1285 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1287 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1288 json_response(conn, 200)
1290 assert to_string(activity.id) == id
1294 describe "user timelines" do
1295 test "gets a users statuses", %{conn: conn} do
1296 user_one = insert(:user)
1297 user_two = insert(:user)
1298 user_three = insert(:user)
1300 {:ok, user_three} = User.follow(user_three, user_one)
1302 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1304 {:ok, direct_activity} =
1305 CommonAPI.post(user_one, %{
1306 "status" => "Hi, @#{user_two.nickname}.",
1307 "visibility" => "direct"
1310 {:ok, private_activity} =
1311 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1315 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1317 assert [%{"id" => id}] = json_response(resp, 200)
1318 assert id == to_string(activity.id)
1322 |> assign(:user, user_two)
1323 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1325 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1326 assert id_one == to_string(direct_activity.id)
1327 assert id_two == to_string(activity.id)
1331 |> assign(:user, user_three)
1332 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1334 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1335 assert id_one == to_string(private_activity.id)
1336 assert id_two == to_string(activity.id)
1339 test "unimplemented pinned statuses feature", %{conn: conn} do
1340 note = insert(:note_activity)
1341 user = User.get_cached_by_ap_id(note.data["actor"])
1345 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1347 assert json_response(conn, 200) == []
1350 test "gets an users media", %{conn: conn} do
1351 note = insert(:note_activity)
1352 user = User.get_cached_by_ap_id(note.data["actor"])
1354 file = %Plug.Upload{
1355 content_type: "image/jpg",
1356 path: Path.absname("test/fixtures/image.jpg"),
1357 filename: "an_image.jpg"
1361 TwitterAPI.upload(file, user, "json")
1365 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1369 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1371 assert [%{"id" => id}] = json_response(conn, 200)
1372 assert id == to_string(image_post.id)
1376 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1378 assert [%{"id" => id}] = json_response(conn, 200)
1379 assert id == to_string(image_post.id)
1382 test "gets a user's statuses without reblogs", %{conn: conn} do
1383 user = insert(:user)
1384 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1385 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1389 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1391 assert [%{"id" => id}] = json_response(conn, 200)
1392 assert id == to_string(post.id)
1396 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1398 assert [%{"id" => id}] = json_response(conn, 200)
1399 assert id == to_string(post.id)
1403 describe "user relationships" do
1404 test "returns the relationships for the current user", %{conn: conn} do
1405 user = insert(:user)
1406 other_user = insert(:user)
1407 {:ok, user} = User.follow(user, other_user)
1411 |> assign(:user, user)
1412 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1414 assert [relationship] = json_response(conn, 200)
1416 assert to_string(other_user.id) == relationship["id"]
1420 describe "locked accounts" do
1421 test "/api/v1/follow_requests works" do
1422 user = insert(:user, %{info: %User.Info{locked: true}})
1423 other_user = insert(:user)
1425 {:ok, _activity} = ActivityPub.follow(other_user, user)
1427 user = User.get_cached_by_id(user.id)
1428 other_user = User.get_cached_by_id(other_user.id)
1430 assert User.following?(other_user, user) == false
1434 |> assign(:user, user)
1435 |> get("/api/v1/follow_requests")
1437 assert [relationship] = json_response(conn, 200)
1438 assert to_string(other_user.id) == relationship["id"]
1441 test "/api/v1/follow_requests/:id/authorize works" do
1442 user = insert(:user, %{info: %User.Info{locked: true}})
1443 other_user = insert(:user)
1445 {:ok, _activity} = ActivityPub.follow(other_user, user)
1447 user = User.get_cached_by_id(user.id)
1448 other_user = User.get_cached_by_id(other_user.id)
1450 assert User.following?(other_user, user) == false
1454 |> assign(:user, user)
1455 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1457 assert relationship = json_response(conn, 200)
1458 assert to_string(other_user.id) == relationship["id"]
1460 user = User.get_cached_by_id(user.id)
1461 other_user = User.get_cached_by_id(other_user.id)
1463 assert User.following?(other_user, user) == true
1466 test "verify_credentials", %{conn: conn} do
1467 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1471 |> assign(:user, user)
1472 |> get("/api/v1/accounts/verify_credentials")
1474 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1475 assert id == to_string(user.id)
1478 test "/api/v1/follow_requests/:id/reject works" do
1479 user = insert(:user, %{info: %User.Info{locked: true}})
1480 other_user = insert(:user)
1482 {:ok, _activity} = ActivityPub.follow(other_user, user)
1484 user = User.get_cached_by_id(user.id)
1488 |> assign(:user, user)
1489 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1491 assert relationship = json_response(conn, 200)
1492 assert to_string(other_user.id) == relationship["id"]
1494 user = User.get_cached_by_id(user.id)
1495 other_user = User.get_cached_by_id(other_user.id)
1497 assert User.following?(other_user, user) == false
1501 test "account fetching", %{conn: conn} do
1502 user = insert(:user)
1506 |> get("/api/v1/accounts/#{user.id}")
1508 assert %{"id" => id} = json_response(conn, 200)
1509 assert id == to_string(user.id)
1513 |> get("/api/v1/accounts/-1")
1515 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1518 test "account fetching also works nickname", %{conn: conn} do
1519 user = insert(:user)
1523 |> get("/api/v1/accounts/#{user.nickname}")
1525 assert %{"id" => id} = json_response(conn, 200)
1526 assert id == user.id
1529 test "media upload", %{conn: conn} do
1530 file = %Plug.Upload{
1531 content_type: "image/jpg",
1532 path: Path.absname("test/fixtures/image.jpg"),
1533 filename: "an_image.jpg"
1536 desc = "Description of the image"
1538 user = insert(:user)
1542 |> assign(:user, user)
1543 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1545 assert media = json_response(conn, 200)
1547 assert media["type"] == "image"
1548 assert media["description"] == desc
1551 object = Repo.get(Object, media["id"])
1552 assert object.data["actor"] == User.ap_id(user)
1555 test "hashtag timeline", %{conn: conn} do
1556 following = insert(:user)
1559 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1561 {:ok, [_activity]} =
1562 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1566 |> get("/api/v1/timelines/tag/2hu")
1568 assert [%{"id" => id}] = json_response(nconn, 200)
1570 assert id == to_string(activity.id)
1572 # works for different capitalization too
1575 |> get("/api/v1/timelines/tag/2HU")
1577 assert [%{"id" => id}] = json_response(nconn, 200)
1579 assert id == to_string(activity.id)
1583 test "multi-hashtag timeline", %{conn: conn} do
1584 user = insert(:user)
1586 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1587 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1588 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1592 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1594 [status_none, status_test1, status_test] = json_response(any_test, 200)
1596 assert to_string(activity_test.id) == status_test["id"]
1597 assert to_string(activity_test1.id) == status_test1["id"]
1598 assert to_string(activity_none.id) == status_none["id"]
1602 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1604 assert [status_test1] == json_response(restricted_test, 200)
1606 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1608 assert [status_none] == json_response(all_test, 200)
1611 test "getting followers", %{conn: conn} do
1612 user = insert(:user)
1613 other_user = insert(:user)
1614 {:ok, user} = User.follow(user, other_user)
1618 |> get("/api/v1/accounts/#{other_user.id}/followers")
1620 assert [%{"id" => id}] = json_response(conn, 200)
1621 assert id == to_string(user.id)
1624 test "getting followers, hide_followers", %{conn: conn} do
1625 user = insert(:user)
1626 other_user = insert(:user, %{info: %{hide_followers: true}})
1627 {:ok, _user} = User.follow(user, other_user)
1631 |> get("/api/v1/accounts/#{other_user.id}/followers")
1633 assert [] == json_response(conn, 200)
1636 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1637 user = insert(:user)
1638 other_user = insert(:user, %{info: %{hide_followers: true}})
1639 {:ok, _user} = User.follow(user, other_user)
1643 |> assign(:user, other_user)
1644 |> get("/api/v1/accounts/#{other_user.id}/followers")
1646 refute [] == json_response(conn, 200)
1649 test "getting followers, pagination", %{conn: conn} do
1650 user = insert(:user)
1651 follower1 = insert(:user)
1652 follower2 = insert(:user)
1653 follower3 = insert(:user)
1654 {:ok, _} = User.follow(follower1, user)
1655 {:ok, _} = User.follow(follower2, user)
1656 {:ok, _} = User.follow(follower3, user)
1660 |> assign(:user, user)
1664 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1666 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1667 assert id3 == follower3.id
1668 assert id2 == follower2.id
1672 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1674 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1675 assert id2 == follower2.id
1676 assert id1 == follower1.id
1680 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1682 assert [%{"id" => id2}] = json_response(res_conn, 200)
1683 assert id2 == follower2.id
1685 assert [link_header] = get_resp_header(res_conn, "link")
1686 assert link_header =~ ~r/min_id=#{follower2.id}/
1687 assert link_header =~ ~r/max_id=#{follower2.id}/
1690 test "getting following", %{conn: conn} do
1691 user = insert(:user)
1692 other_user = insert(:user)
1693 {:ok, user} = User.follow(user, other_user)
1697 |> get("/api/v1/accounts/#{user.id}/following")
1699 assert [%{"id" => id}] = json_response(conn, 200)
1700 assert id == to_string(other_user.id)
1703 test "getting following, hide_follows", %{conn: conn} do
1704 user = insert(:user, %{info: %{hide_follows: true}})
1705 other_user = insert(:user)
1706 {:ok, user} = User.follow(user, other_user)
1710 |> get("/api/v1/accounts/#{user.id}/following")
1712 assert [] == json_response(conn, 200)
1715 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1716 user = insert(:user, %{info: %{hide_follows: true}})
1717 other_user = insert(:user)
1718 {:ok, user} = User.follow(user, other_user)
1722 |> assign(:user, user)
1723 |> get("/api/v1/accounts/#{user.id}/following")
1725 refute [] == json_response(conn, 200)
1728 test "getting following, pagination", %{conn: conn} do
1729 user = insert(:user)
1730 following1 = insert(:user)
1731 following2 = insert(:user)
1732 following3 = insert(:user)
1733 {:ok, _} = User.follow(user, following1)
1734 {:ok, _} = User.follow(user, following2)
1735 {:ok, _} = User.follow(user, following3)
1739 |> assign(:user, user)
1743 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1745 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1746 assert id3 == following3.id
1747 assert id2 == following2.id
1751 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1753 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1754 assert id2 == following2.id
1755 assert id1 == following1.id
1759 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1761 assert [%{"id" => id2}] = json_response(res_conn, 200)
1762 assert id2 == following2.id
1764 assert [link_header] = get_resp_header(res_conn, "link")
1765 assert link_header =~ ~r/min_id=#{following2.id}/
1766 assert link_header =~ ~r/max_id=#{following2.id}/
1769 test "following / unfollowing a user", %{conn: conn} do
1770 user = insert(:user)
1771 other_user = insert(:user)
1775 |> assign(:user, user)
1776 |> post("/api/v1/accounts/#{other_user.id}/follow")
1778 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1780 user = User.get_cached_by_id(user.id)
1784 |> assign(:user, user)
1785 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1787 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1789 user = User.get_cached_by_id(user.id)
1793 |> assign(:user, user)
1794 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1796 assert %{"id" => id} = json_response(conn, 200)
1797 assert id == to_string(other_user.id)
1800 test "following without reblogs" do
1801 follower = insert(:user)
1802 followed = insert(:user)
1803 other_user = insert(:user)
1807 |> assign(:user, follower)
1808 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1810 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1812 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1813 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1817 |> assign(:user, User.get_cached_by_id(follower.id))
1818 |> get("/api/v1/timelines/home")
1820 assert [] == json_response(conn, 200)
1824 |> assign(:user, follower)
1825 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1827 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1831 |> assign(:user, User.get_cached_by_id(follower.id))
1832 |> get("/api/v1/timelines/home")
1834 expected_activity_id = reblog.id
1835 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1838 test "following / unfollowing errors" do
1839 user = insert(:user)
1843 |> assign(:user, user)
1846 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1847 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1850 user = User.get_cached_by_id(user.id)
1851 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1852 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1854 # self follow via uri
1855 user = User.get_cached_by_id(user.id)
1856 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1857 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1859 # follow non existing user
1860 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1861 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1863 # follow non existing user via uri
1864 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1865 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1867 # unfollow non existing user
1868 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1869 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1872 test "muting / unmuting a user", %{conn: conn} do
1873 user = insert(:user)
1874 other_user = insert(:user)
1878 |> assign(:user, user)
1879 |> post("/api/v1/accounts/#{other_user.id}/mute")
1881 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1883 user = User.get_cached_by_id(user.id)
1887 |> assign(:user, user)
1888 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1890 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1893 test "subscribing / unsubscribing to a user", %{conn: conn} do
1894 user = insert(:user)
1895 subscription_target = insert(:user)
1899 |> assign(:user, user)
1900 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1902 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1906 |> assign(:user, user)
1907 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1909 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1912 test "getting a list of mutes", %{conn: conn} do
1913 user = insert(:user)
1914 other_user = insert(:user)
1916 {:ok, user} = User.mute(user, other_user)
1920 |> assign(:user, user)
1921 |> get("/api/v1/mutes")
1923 other_user_id = to_string(other_user.id)
1924 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1927 test "blocking / unblocking a user", %{conn: conn} do
1928 user = insert(:user)
1929 other_user = insert(:user)
1933 |> assign(:user, user)
1934 |> post("/api/v1/accounts/#{other_user.id}/block")
1936 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1938 user = User.get_cached_by_id(user.id)
1942 |> assign(:user, user)
1943 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1945 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1948 test "getting a list of blocks", %{conn: conn} do
1949 user = insert(:user)
1950 other_user = insert(:user)
1952 {:ok, user} = User.block(user, other_user)
1956 |> assign(:user, user)
1957 |> get("/api/v1/blocks")
1959 other_user_id = to_string(other_user.id)
1960 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1963 test "blocking / unblocking a domain", %{conn: conn} do
1964 user = insert(:user)
1965 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1969 |> assign(:user, user)
1970 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1972 assert %{} = json_response(conn, 200)
1973 user = User.get_cached_by_ap_id(user.ap_id)
1974 assert User.blocks?(user, other_user)
1978 |> assign(:user, user)
1979 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1981 assert %{} = json_response(conn, 200)
1982 user = User.get_cached_by_ap_id(user.ap_id)
1983 refute User.blocks?(user, other_user)
1986 test "getting a list of domain blocks", %{conn: conn} do
1987 user = insert(:user)
1989 {:ok, user} = User.block_domain(user, "bad.site")
1990 {:ok, user} = User.block_domain(user, "even.worse.site")
1994 |> assign(:user, user)
1995 |> get("/api/v1/domain_blocks")
1997 domain_blocks = json_response(conn, 200)
1999 assert "bad.site" in domain_blocks
2000 assert "even.worse.site" in domain_blocks
2003 test "unimplemented follow_requests, blocks, domain blocks" do
2004 user = insert(:user)
2006 ["blocks", "domain_blocks", "follow_requests"]
2007 |> Enum.each(fn endpoint ->
2010 |> assign(:user, user)
2011 |> get("/api/v1/#{endpoint}")
2013 assert [] = json_response(conn, 200)
2017 test "account 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"})
2024 |> assign(:user, user)
2025 |> get("/api/v1/accounts/search", %{"q" => "shp"})
2026 |> json_response(200)
2028 result_ids = for result <- results, do: result["acct"]
2030 assert user_two.nickname in result_ids
2031 assert user_three.nickname in result_ids
2035 |> assign(:user, user)
2036 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
2037 |> json_response(200)
2039 result_ids = for result <- results, do: result["acct"]
2041 assert user_three.nickname in result_ids
2044 test "search", %{conn: conn} do
2045 user = insert(:user)
2046 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2047 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2049 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
2052 CommonAPI.post(user, %{
2053 "status" => "This is about 2hu, but private",
2054 "visibility" => "private"
2057 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
2061 |> get("/api/v1/search", %{"q" => "2hu"})
2063 assert results = json_response(conn, 200)
2065 [account | _] = results["accounts"]
2066 assert account["id"] == to_string(user_three.id)
2068 assert results["hashtags"] == []
2070 [status] = results["statuses"]
2071 assert status["id"] == to_string(activity.id)
2074 test "search fetches remote statuses", %{conn: conn} do
2078 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
2080 assert results = json_response(conn, 200)
2082 [status] = results["statuses"]
2083 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
2087 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
2089 CommonAPI.post(insert(:user), %{
2090 "status" => "This is about 2hu, but private",
2091 "visibility" => "private"
2097 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2099 assert results = json_response(conn, 200)
2101 [] = results["statuses"]
2105 test "search fetches remote accounts", %{conn: conn} do
2108 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2110 assert results = json_response(conn, 200)
2111 [account] = results["accounts"]
2112 assert account["acct"] == "shp@social.heldscal.la"
2115 test "returns the favorites of a user", %{conn: conn} do
2116 user = insert(:user)
2117 other_user = insert(:user)
2119 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2120 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2122 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2126 |> assign(:user, user)
2127 |> get("/api/v1/favourites")
2129 assert [status] = json_response(first_conn, 200)
2130 assert status["id"] == to_string(activity.id)
2132 assert [{"link", _link_header}] =
2133 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2135 # Honours query params
2136 {:ok, second_activity} =
2137 CommonAPI.post(other_user, %{
2139 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2142 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2144 last_like = status["id"]
2148 |> assign(:user, user)
2149 |> get("/api/v1/favourites?since_id=#{last_like}")
2151 assert [second_status] = json_response(second_conn, 200)
2152 assert second_status["id"] == to_string(second_activity.id)
2156 |> assign(:user, user)
2157 |> get("/api/v1/favourites?limit=0")
2159 assert [] = json_response(third_conn, 200)
2162 describe "getting favorites timeline of specified user" do
2164 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2165 [current_user: current_user, user: user]
2168 test "returns list of statuses favorited by specified user", %{
2170 current_user: current_user,
2173 [activity | _] = insert_pair(:note_activity)
2174 CommonAPI.favorite(activity.id, user)
2178 |> assign(:user, current_user)
2179 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2180 |> json_response(:ok)
2184 assert length(response) == 1
2185 assert like["id"] == activity.id
2188 test "returns favorites for specified user_id when user is not logged in", %{
2192 activity = insert(:note_activity)
2193 CommonAPI.favorite(activity.id, user)
2197 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2198 |> json_response(:ok)
2200 assert length(response) == 1
2203 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2205 current_user: current_user,
2209 CommonAPI.post(current_user, %{
2210 "status" => "Hi @#{user.nickname}!",
2211 "visibility" => "direct"
2214 CommonAPI.favorite(direct.id, user)
2218 |> assign(:user, current_user)
2219 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2220 |> json_response(:ok)
2222 assert length(response) == 1
2224 anonymous_response =
2226 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2227 |> json_response(:ok)
2229 assert Enum.empty?(anonymous_response)
2232 test "does not return others' favorited DM when user is not one of recipients", %{
2234 current_user: current_user,
2237 user_two = insert(:user)
2240 CommonAPI.post(user_two, %{
2241 "status" => "Hi @#{user.nickname}!",
2242 "visibility" => "direct"
2245 CommonAPI.favorite(direct.id, user)
2249 |> assign(:user, current_user)
2250 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2251 |> json_response(:ok)
2253 assert Enum.empty?(response)
2256 test "paginates favorites using since_id and max_id", %{
2258 current_user: current_user,
2261 activities = insert_list(10, :note_activity)
2263 Enum.each(activities, fn activity ->
2264 CommonAPI.favorite(activity.id, user)
2267 third_activity = Enum.at(activities, 2)
2268 seventh_activity = Enum.at(activities, 6)
2272 |> assign(:user, current_user)
2273 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2274 since_id: third_activity.id,
2275 max_id: seventh_activity.id
2277 |> json_response(:ok)
2279 assert length(response) == 3
2280 refute third_activity in response
2281 refute seventh_activity in response
2284 test "limits favorites using limit parameter", %{
2286 current_user: current_user,
2290 |> insert_list(:note_activity)
2291 |> Enum.each(fn activity ->
2292 CommonAPI.favorite(activity.id, user)
2297 |> assign(:user, current_user)
2298 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2299 |> json_response(:ok)
2301 assert length(response) == 3
2304 test "returns empty response when user does not have any favorited statuses", %{
2306 current_user: current_user,
2311 |> assign(:user, current_user)
2312 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2313 |> json_response(:ok)
2315 assert Enum.empty?(response)
2318 test "returns 404 error when specified user is not exist", %{conn: conn} do
2319 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2321 assert json_response(conn, 404) == %{"error" => "Record not found"}
2324 test "returns 403 error when user has hidden own favorites", %{
2326 current_user: current_user
2328 user = insert(:user, %{info: %{hide_favorites: true}})
2329 activity = insert(:note_activity)
2330 CommonAPI.favorite(activity.id, user)
2334 |> assign(:user, current_user)
2335 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2337 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2340 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2341 user = insert(:user)
2342 activity = insert(:note_activity)
2343 CommonAPI.favorite(activity.id, user)
2347 |> assign(:user, current_user)
2348 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2350 assert user.info.hide_favorites
2351 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2355 describe "updating credentials" do
2356 test "updates the user's bio", %{conn: conn} do
2357 user = insert(:user)
2358 user2 = insert(:user)
2362 |> assign(:user, user)
2363 |> patch("/api/v1/accounts/update_credentials", %{
2364 "note" => "I drink #cofe with @#{user2.nickname}"
2367 assert user = json_response(conn, 200)
2369 assert user["note"] ==
2370 ~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=") <>
2372 ~s(" class="u-url mention" href=") <>
2373 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2376 test "updates the user's locking status", %{conn: conn} do
2377 user = insert(:user)
2381 |> assign(:user, user)
2382 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2384 assert user = json_response(conn, 200)
2385 assert user["locked"] == true
2388 test "updates the user's default scope", %{conn: conn} do
2389 user = insert(:user)
2393 |> assign(:user, user)
2394 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2396 assert user = json_response(conn, 200)
2397 assert user["source"]["privacy"] == "cofe"
2400 test "updates the user's hide_followers status", %{conn: conn} do
2401 user = insert(:user)
2405 |> assign(:user, user)
2406 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2408 assert user = json_response(conn, 200)
2409 assert user["pleroma"]["hide_followers"] == true
2412 test "updates the user's hide_follows status", %{conn: conn} do
2413 user = insert(:user)
2417 |> assign(:user, user)
2418 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2420 assert user = json_response(conn, 200)
2421 assert user["pleroma"]["hide_follows"] == true
2424 test "updates the user's hide_favorites status", %{conn: conn} do
2425 user = insert(:user)
2429 |> assign(:user, user)
2430 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2432 assert user = json_response(conn, 200)
2433 assert user["pleroma"]["hide_favorites"] == true
2436 test "updates the user's show_role status", %{conn: conn} do
2437 user = insert(:user)
2441 |> assign(:user, user)
2442 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2444 assert user = json_response(conn, 200)
2445 assert user["source"]["pleroma"]["show_role"] == false
2448 test "updates the user's no_rich_text status", %{conn: conn} do
2449 user = insert(:user)
2453 |> assign(:user, user)
2454 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2456 assert user = json_response(conn, 200)
2457 assert user["source"]["pleroma"]["no_rich_text"] == true
2460 test "updates the user's name", %{conn: conn} do
2461 user = insert(:user)
2465 |> assign(:user, user)
2466 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2468 assert user = json_response(conn, 200)
2469 assert user["display_name"] == "markorepairs"
2472 test "updates the user's avatar", %{conn: conn} do
2473 user = insert(:user)
2475 new_avatar = %Plug.Upload{
2476 content_type: "image/jpg",
2477 path: Path.absname("test/fixtures/image.jpg"),
2478 filename: "an_image.jpg"
2483 |> assign(:user, user)
2484 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2486 assert user_response = json_response(conn, 200)
2487 assert user_response["avatar"] != User.avatar_url(user)
2490 test "updates the user's banner", %{conn: conn} do
2491 user = insert(:user)
2493 new_header = %Plug.Upload{
2494 content_type: "image/jpg",
2495 path: Path.absname("test/fixtures/image.jpg"),
2496 filename: "an_image.jpg"
2501 |> assign(:user, user)
2502 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2504 assert user_response = json_response(conn, 200)
2505 assert user_response["header"] != User.banner_url(user)
2508 test "requires 'write' permission", %{conn: conn} do
2509 token1 = insert(:oauth_token, scopes: ["read"])
2510 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2512 for token <- [token1, token2] do
2515 |> put_req_header("authorization", "Bearer #{token.token}")
2516 |> patch("/api/v1/accounts/update_credentials", %{})
2518 if token == token1 do
2519 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2521 assert json_response(conn, 200)
2526 test "updates profile emojos", %{conn: conn} do
2527 user = insert(:user)
2529 note = "*sips :blank:*"
2530 name = "I am :firefox:"
2534 |> assign(:user, user)
2535 |> patch("/api/v1/accounts/update_credentials", %{
2537 "display_name" => name
2540 assert json_response(conn, 200)
2544 |> get("/api/v1/accounts/#{user.id}")
2546 assert user = json_response(conn, 200)
2548 assert user["note"] == note
2549 assert user["display_name"] == name
2550 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2554 test "get instance information", %{conn: conn} do
2555 conn = get(conn, "/api/v1/instance")
2556 assert result = json_response(conn, 200)
2558 email = Pleroma.Config.get([:instance, :email])
2559 # Note: not checking for "max_toot_chars" since it's optional
2565 "email" => from_config_email,
2567 "streaming_api" => _
2572 "registrations" => _,
2576 assert email == from_config_email
2579 test "get instance stats", %{conn: conn} do
2580 user = insert(:user, %{local: true})
2582 user2 = insert(:user, %{local: true})
2583 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2585 insert(:user, %{local: false, nickname: "u@peer1.com"})
2586 insert(:user, %{local: false, nickname: "u@peer2.com"})
2588 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2590 # Stats should count users with missing or nil `info.deactivated` value
2591 user = User.get_cached_by_id(user.id)
2592 info_change = Changeset.change(user.info, %{deactivated: nil})
2596 |> Changeset.change()
2597 |> Changeset.put_embed(:info, info_change)
2598 |> User.update_and_set_cache()
2600 Pleroma.Stats.update_stats()
2602 conn = get(conn, "/api/v1/instance")
2604 assert result = json_response(conn, 200)
2606 stats = result["stats"]
2609 assert stats["user_count"] == 1
2610 assert stats["status_count"] == 1
2611 assert stats["domain_count"] == 2
2614 test "get peers", %{conn: conn} do
2615 insert(:user, %{local: false, nickname: "u@peer1.com"})
2616 insert(:user, %{local: false, nickname: "u@peer2.com"})
2618 Pleroma.Stats.update_stats()
2620 conn = get(conn, "/api/v1/instance/peers")
2622 assert result = json_response(conn, 200)
2624 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2627 test "put settings", %{conn: conn} do
2628 user = insert(:user)
2632 |> assign(:user, user)
2633 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2635 assert _result = json_response(conn, 200)
2637 user = User.get_cached_by_ap_id(user.ap_id)
2638 assert user.info.settings == %{"programming" => "socks"}
2641 describe "pinned statuses" do
2643 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2645 user = insert(:user)
2646 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2648 [user: user, activity: activity]
2651 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2652 {:ok, _} = CommonAPI.pin(activity.id, user)
2656 |> assign(:user, user)
2657 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2658 |> json_response(200)
2660 id_str = to_string(activity.id)
2662 assert [%{"id" => ^id_str, "pinned" => true}] = result
2665 test "pin status", %{conn: conn, user: user, activity: activity} do
2666 id_str = to_string(activity.id)
2668 assert %{"id" => ^id_str, "pinned" => true} =
2670 |> assign(:user, user)
2671 |> post("/api/v1/statuses/#{activity.id}/pin")
2672 |> json_response(200)
2674 assert [%{"id" => ^id_str, "pinned" => true}] =
2676 |> assign(:user, user)
2677 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2678 |> json_response(200)
2681 test "unpin status", %{conn: conn, user: user, activity: activity} do
2682 {:ok, _} = CommonAPI.pin(activity.id, user)
2684 id_str = to_string(activity.id)
2685 user = refresh_record(user)
2687 assert %{"id" => ^id_str, "pinned" => false} =
2689 |> assign(:user, user)
2690 |> post("/api/v1/statuses/#{activity.id}/unpin")
2691 |> json_response(200)
2695 |> assign(:user, user)
2696 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2697 |> json_response(200)
2700 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2701 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2703 id_str_one = to_string(activity_one.id)
2705 assert %{"id" => ^id_str_one, "pinned" => true} =
2707 |> assign(:user, user)
2708 |> post("/api/v1/statuses/#{id_str_one}/pin")
2709 |> json_response(200)
2711 user = refresh_record(user)
2713 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2715 |> assign(:user, user)
2716 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2717 |> json_response(400)
2720 test "Status rich-media Card", %{conn: conn, user: user} do
2721 Pleroma.Config.put([:rich_media, :enabled], true)
2722 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2726 |> get("/api/v1/statuses/#{activity.id}/card")
2727 |> json_response(200)
2729 assert response == %{
2730 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2731 "provider_name" => "www.imdb.com",
2732 "provider_url" => "http://www.imdb.com",
2733 "title" => "The Rock",
2735 "url" => "http://www.imdb.com/title/tt0117500/",
2736 "description" => nil,
2739 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2740 "title" => "The Rock",
2741 "type" => "video.movie",
2742 "url" => "http://www.imdb.com/title/tt0117500/"
2747 # works with private posts
2749 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2753 |> assign(:user, user)
2754 |> get("/api/v1/statuses/#{activity.id}/card")
2755 |> json_response(200)
2757 assert response_two == response
2759 Pleroma.Config.put([:rich_media, :enabled], false)
2764 user = insert(:user)
2765 for_user = insert(:user)
2768 CommonAPI.post(user, %{
2769 "status" => "heweoo?"
2773 CommonAPI.post(user, %{
2774 "status" => "heweoo!"
2779 |> assign(:user, for_user)
2780 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2782 assert json_response(response1, 200)["bookmarked"] == true
2786 |> assign(:user, for_user)
2787 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2789 assert json_response(response2, 200)["bookmarked"] == true
2793 |> assign(:user, for_user)
2794 |> get("/api/v1/bookmarks")
2796 assert [json_response(response2, 200), json_response(response1, 200)] ==
2797 json_response(bookmarks, 200)
2801 |> assign(:user, for_user)
2802 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2804 assert json_response(response1, 200)["bookmarked"] == false
2808 |> assign(:user, for_user)
2809 |> get("/api/v1/bookmarks")
2811 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2814 describe "conversation muting" do
2816 user = insert(:user)
2817 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2819 [user: user, activity: activity]
2822 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2823 id_str = to_string(activity.id)
2825 assert %{"id" => ^id_str, "muted" => true} =
2827 |> assign(:user, user)
2828 |> post("/api/v1/statuses/#{activity.id}/mute")
2829 |> json_response(200)
2832 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2833 {:ok, _} = CommonAPI.add_mute(user, activity)
2835 id_str = to_string(activity.id)
2836 user = refresh_record(user)
2838 assert %{"id" => ^id_str, "muted" => false} =
2840 |> assign(:user, user)
2841 |> post("/api/v1/statuses/#{activity.id}/unmute")
2842 |> json_response(200)
2846 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2847 user = insert(:user)
2851 |> assign(:user, user)
2852 |> get("/api/v1/pleroma/flavour")
2854 assert "glitch" == json_response(get_old_flavour, 200)
2858 |> assign(:user, user)
2859 |> post("/api/v1/pleroma/flavour/vanilla")
2861 assert "vanilla" == json_response(set_flavour, 200)
2865 |> assign(:user, user)
2866 |> post("/api/v1/pleroma/flavour/vanilla")
2868 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2871 describe "reports" do
2873 reporter = insert(:user)
2874 target_user = insert(:user)
2876 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2878 [reporter: reporter, target_user: target_user, activity: activity]
2881 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2882 assert %{"action_taken" => false, "id" => _} =
2884 |> assign(:user, reporter)
2885 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2886 |> json_response(200)
2889 test "submit a report with statuses and comment", %{
2892 target_user: target_user,
2895 assert %{"action_taken" => false, "id" => _} =
2897 |> assign(:user, reporter)
2898 |> post("/api/v1/reports", %{
2899 "account_id" => target_user.id,
2900 "status_ids" => [activity.id],
2901 "comment" => "bad status!"
2903 |> json_response(200)
2906 test "account_id is required", %{
2911 assert %{"error" => "Valid `account_id` required"} =
2913 |> assign(:user, reporter)
2914 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2915 |> json_response(400)
2918 test "comment must be up to the size specified in the config", %{
2921 target_user: target_user
2923 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2924 comment = String.pad_trailing("a", max_size + 1, "a")
2926 error = %{"error" => "Comment must be up to #{max_size} characters"}
2930 |> assign(:user, reporter)
2931 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2932 |> json_response(400)
2936 describe "link headers" do
2937 test "preserves parameters in link headers", %{conn: conn} do
2938 user = insert(:user)
2939 other_user = insert(:user)
2942 CommonAPI.post(other_user, %{
2943 "status" => "hi @#{user.nickname}",
2944 "visibility" => "public"
2948 CommonAPI.post(other_user, %{
2949 "status" => "hi @#{user.nickname}",
2950 "visibility" => "public"
2953 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2954 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2958 |> assign(:user, user)
2959 |> get("/api/v1/notifications", %{media_only: true})
2961 assert [link_header] = get_resp_header(conn, "link")
2962 assert link_header =~ ~r/media_only=true/
2963 assert link_header =~ ~r/min_id=#{notification2.id}/
2964 assert link_header =~ ~r/max_id=#{notification1.id}/
2968 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2969 # Need to set an old-style integer ID to reproduce the problem
2970 # (these are no longer assigned to new accounts but were preserved
2971 # for existing accounts during the migration to flakeIDs)
2972 user_one = insert(:user, %{id: 1212})
2973 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2977 |> get("/api/v1/accounts/#{user_one.id}")
2981 |> get("/api/v1/accounts/#{user_two.nickname}")
2985 |> get("/api/v1/accounts/#{user_two.id}")
2987 acc_one = json_response(resp_one, 200)
2988 acc_two = json_response(resp_two, 200)
2989 acc_three = json_response(resp_three, 200)
2990 refute acc_one == acc_two
2991 assert acc_two == acc_three
2994 describe "custom emoji" do
2995 test "with tags", %{conn: conn} do
2998 |> get("/api/v1/custom_emojis")
2999 |> json_response(200)
3001 assert Map.has_key?(emoji, "shortcode")
3002 assert Map.has_key?(emoji, "static_url")
3003 assert Map.has_key?(emoji, "tags")
3004 assert is_list(emoji["tags"])
3005 assert Map.has_key?(emoji, "url")
3006 assert Map.has_key?(emoji, "visible_in_picker")
3010 describe "index/2 redirections" do
3011 setup %{conn: conn} do
3015 signing_salt: "cooldude"
3020 |> Plug.Session.call(Plug.Session.init(session_opts))
3023 test_path = "/web/statuses/test"
3024 %{conn: conn, path: test_path}
3027 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3028 conn = get(conn, path)
3030 assert conn.status == 302
3031 assert redirected_to(conn) == "/web/login"
3034 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3035 token = insert(:oauth_token)
3039 |> assign(:user, token.user)
3040 |> put_session(:oauth_token, token.token)
3043 assert conn.status == 200
3046 test "saves referer path to session", %{conn: conn, path: path} do
3047 conn = get(conn, path)
3048 return_to = Plug.Conn.get_session(conn, :return_to)
3050 assert return_to == path
3053 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3054 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3055 auth = insert(:oauth_authorization, app: app)
3059 |> put_session(:return_to, path)
3060 |> get("/web/login", %{code: auth.token})
3062 assert conn.status == 302
3063 assert redirected_to(conn) == path
3066 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3067 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3068 auth = insert(:oauth_authorization, app: app)
3070 conn = get(conn, "/web/login", %{code: auth.token})
3072 assert conn.status == 302
3073 assert redirected_to(conn) == "/web/getting-started"
3077 describe "scheduled activities" do
3078 test "creates a scheduled activity", %{conn: conn} do
3079 user = insert(:user)
3080 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3084 |> assign(:user, user)
3085 |> post("/api/v1/statuses", %{
3086 "status" => "scheduled",
3087 "scheduled_at" => scheduled_at
3090 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3091 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3092 assert [] == Repo.all(Activity)
3095 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3096 user = insert(:user)
3097 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3099 file = %Plug.Upload{
3100 content_type: "image/jpg",
3101 path: Path.absname("test/fixtures/image.jpg"),
3102 filename: "an_image.jpg"
3105 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3109 |> assign(:user, user)
3110 |> post("/api/v1/statuses", %{
3111 "media_ids" => [to_string(upload.id)],
3112 "status" => "scheduled",
3113 "scheduled_at" => scheduled_at
3116 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3117 assert %{"type" => "image"} = media_attachment
3120 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3122 user = insert(:user)
3125 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3129 |> assign(:user, user)
3130 |> post("/api/v1/statuses", %{
3131 "status" => "not scheduled",
3132 "scheduled_at" => scheduled_at
3135 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3136 assert [] == Repo.all(ScheduledActivity)
3139 test "returns error when daily user limit is exceeded", %{conn: conn} do
3140 user = insert(:user)
3143 NaiveDateTime.utc_now()
3144 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3145 |> NaiveDateTime.to_iso8601()
3147 attrs = %{params: %{}, scheduled_at: today}
3148 {:ok, _} = ScheduledActivity.create(user, attrs)
3149 {:ok, _} = ScheduledActivity.create(user, attrs)
3153 |> assign(:user, user)
3154 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3156 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3159 test "returns error when total user limit is exceeded", %{conn: conn} do
3160 user = insert(:user)
3163 NaiveDateTime.utc_now()
3164 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3165 |> NaiveDateTime.to_iso8601()
3168 NaiveDateTime.utc_now()
3169 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3170 |> NaiveDateTime.to_iso8601()
3172 attrs = %{params: %{}, scheduled_at: today}
3173 {:ok, _} = ScheduledActivity.create(user, attrs)
3174 {:ok, _} = ScheduledActivity.create(user, attrs)
3175 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3179 |> assign(:user, user)
3180 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3182 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3185 test "shows scheduled activities", %{conn: conn} do
3186 user = insert(:user)
3187 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3188 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3189 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3190 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3194 |> assign(:user, user)
3199 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3201 result = json_response(conn_res, 200)
3202 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3207 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3209 result = json_response(conn_res, 200)
3210 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3215 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3217 result = json_response(conn_res, 200)
3218 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3221 test "shows a scheduled activity", %{conn: conn} do
3222 user = insert(:user)
3223 scheduled_activity = insert(:scheduled_activity, user: user)
3227 |> assign(:user, user)
3228 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3230 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3231 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3235 |> assign(:user, user)
3236 |> get("/api/v1/scheduled_statuses/404")
3238 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3241 test "updates a scheduled activity", %{conn: conn} do
3242 user = insert(:user)
3243 scheduled_activity = insert(:scheduled_activity, user: user)
3246 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3250 |> assign(:user, user)
3251 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3252 scheduled_at: new_scheduled_at
3255 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3256 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3260 |> assign(:user, user)
3261 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3263 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3266 test "deletes a scheduled activity", %{conn: conn} do
3267 user = insert(:user)
3268 scheduled_activity = insert(:scheduled_activity, user: user)
3272 |> assign(:user, user)
3273 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3275 assert %{} = json_response(res_conn, 200)
3276 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3280 |> assign(:user, user)
3281 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3283 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3287 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3288 user1 = insert(:user)
3289 user2 = insert(:user)
3290 user3 = insert(:user)
3292 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3294 # Reply to status from another user
3297 |> assign(:user, user2)
3298 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3300 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3302 activity = Activity.get_by_id_with_object(id)
3304 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3305 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3307 # Reblog from the third user
3310 |> assign(:user, user3)
3311 |> post("/api/v1/statuses/#{activity.id}/reblog")
3313 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3314 json_response(conn2, 200)
3316 assert to_string(activity.id) == id
3318 # Getting third user status
3321 |> assign(:user, user3)
3322 |> get("api/v1/timelines/home")
3324 [reblogged_activity] = json_response(conn3, 200)
3326 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3328 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3329 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3332 describe "create account by app" do
3334 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3335 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3336 interval = Pleroma.Config.get([:app_account_creation, :interval])
3338 Pleroma.Config.put([:app_account_creation, :enabled], true)
3339 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3340 Pleroma.Config.put([:app_account_creation, :interval], 1)
3343 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3344 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3345 Pleroma.Config.put([:app_account_creation, :interval], interval)
3351 test "Account registration via Application", %{conn: conn} do
3354 |> post("/api/v1/apps", %{
3355 client_name: "client_name",
3356 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3357 scopes: "read, write, follow"
3361 "client_id" => client_id,
3362 "client_secret" => client_secret,
3364 "name" => "client_name",
3365 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3368 } = json_response(conn, 200)
3372 |> post("/oauth/token", %{
3373 grant_type: "client_credentials",
3374 client_id: client_id,
3375 client_secret: client_secret
3378 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3379 json_response(conn, 200)
3382 token_from_db = Repo.get_by(Token, token: token)
3383 assert token_from_db
3385 assert scope == "read write follow"
3389 |> put_req_header("authorization", "Bearer " <> token)
3390 |> post("/api/v1/accounts", %{
3392 email: "lain@example.org",
3393 password: "PlzDontHackLain",
3398 "access_token" => token,
3399 "created_at" => _created_at,
3401 "token_type" => "Bearer"
3402 } = json_response(conn, 200)
3404 token_from_db = Repo.get_by(Token, token: token)
3405 assert token_from_db
3406 token_from_db = Repo.preload(token_from_db, :user)
3407 assert token_from_db.user
3409 assert token_from_db.user.info.confirmation_pending
3412 test "rate limit", %{conn: conn} do
3413 app_token = insert(:oauth_token, user: nil)
3416 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3417 |> Map.put(:remote_ip, {15, 15, 15, 15})
3422 |> post("/api/v1/accounts", %{
3423 username: "#{i}lain",
3424 email: "#{i}lain@example.org",
3425 password: "PlzDontHackLain",
3430 "access_token" => token,
3431 "created_at" => _created_at,
3433 "token_type" => "Bearer"
3434 } = json_response(conn, 200)
3436 token_from_db = Repo.get_by(Token, token: token)
3437 assert token_from_db
3438 token_from_db = Repo.preload(token_from_db, :user)
3439 assert token_from_db.user
3441 assert token_from_db.user.info.confirmation_pending
3446 |> post("/api/v1/accounts", %{
3448 email: "6lain@example.org",
3449 password: "PlzDontHackLain",
3453 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}