1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.Notification
13 alias Pleroma.ScheduledActivity
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.MastodonAPI.FilterView
18 alias Pleroma.Web.OAuth.App
19 alias Pleroma.Web.OAuth.Token
20 alias Pleroma.Web.OStatus
21 alias Pleroma.Web.Push
22 alias Pleroma.Web.TwitterAPI.TwitterAPI
23 import Pleroma.Factory
24 import ExUnit.CaptureLog
28 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
32 test "the home timeline", %{conn: conn} do
34 following = insert(:user)
36 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
40 |> assign(:user, user)
41 |> get("/api/v1/timelines/home")
43 assert Enum.empty?(json_response(conn, 200))
45 {:ok, user} = User.follow(user, following)
49 |> assign(:user, user)
50 |> get("/api/v1/timelines/home")
52 assert [%{"content" => "test"}] = json_response(conn, 200)
55 test "the public timeline", %{conn: conn} do
56 following = insert(:user)
59 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
62 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
66 |> get("/api/v1/timelines/public", %{"local" => "False"})
68 assert length(json_response(conn, 200)) == 2
72 |> get("/api/v1/timelines/public", %{"local" => "True"})
74 assert [%{"content" => "test"}] = json_response(conn, 200)
78 |> get("/api/v1/timelines/public", %{"local" => "1"})
80 assert [%{"content" => "test"}] = json_response(conn, 200)
84 test "the public timeline when public is set to false", %{conn: conn} do
85 public = Pleroma.Config.get([:instance, :public])
86 Pleroma.Config.put([:instance, :public], false)
89 Pleroma.Config.put([:instance, :public], public)
93 |> get("/api/v1/timelines/public", %{"local" => "False"})
94 |> json_response(403) == %{"error" => "This resource requires authentication."}
97 test "posting a status", %{conn: conn} do
100 idempotency_key = "Pikachu rocks!"
104 |> assign(:user, user)
105 |> put_req_header("idempotency-key", idempotency_key)
106 |> post("/api/v1/statuses", %{
108 "spoiler_text" => "2hu",
109 "sensitive" => "false"
112 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
114 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
116 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
117 json_response(conn_one, 200)
119 assert Activity.get_by_id(id)
123 |> assign(:user, user)
124 |> put_req_header("idempotency-key", idempotency_key)
125 |> post("/api/v1/statuses", %{
127 "spoiler_text" => "2hu",
128 "sensitive" => "false"
131 assert %{"id" => second_id} = json_response(conn_two, 200)
133 assert id == second_id
137 |> assign(:user, user)
138 |> post("/api/v1/statuses", %{
140 "spoiler_text" => "2hu",
141 "sensitive" => "false"
144 assert %{"id" => third_id} = json_response(conn_three, 200)
146 refute id == third_id
149 test "posting a poll", %{conn: conn} do
151 time = NaiveDateTime.utc_now()
155 |> assign(:user, user)
156 |> post("/api/v1/statuses", %{
157 "status" => "Who is the best girl?",
158 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
161 response = json_response(conn, 200)
163 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
164 title in ["Rei", "Asuka", "Misato"]
167 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
168 refute response["poll"]["expred"]
171 test "posting a sensitive status", %{conn: conn} do
176 |> assign(:user, user)
177 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
179 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
180 assert Activity.get_by_id(id)
183 test "posting a fake status", %{conn: conn} do
188 |> assign(:user, user)
189 |> post("/api/v1/statuses", %{
191 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
194 real_status = json_response(real_conn, 200)
197 assert Object.get_by_ap_id(real_status["uri"])
201 |> Map.put("id", nil)
202 |> Map.put("url", nil)
203 |> Map.put("uri", nil)
204 |> Map.put("created_at", nil)
205 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
209 |> assign(:user, user)
210 |> post("/api/v1/statuses", %{
212 "\"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",
216 fake_status = json_response(fake_conn, 200)
219 refute Object.get_by_ap_id(fake_status["uri"])
223 |> Map.put("id", nil)
224 |> Map.put("url", nil)
225 |> Map.put("uri", nil)
226 |> Map.put("created_at", nil)
227 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
229 assert real_status == fake_status
232 test "posting a status with OGP link preview", %{conn: conn} do
233 Pleroma.Config.put([:rich_media, :enabled], true)
238 |> assign(:user, user)
239 |> post("/api/v1/statuses", %{
240 "status" => "http://example.com/ogp"
243 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
244 assert Activity.get_by_id(id)
245 Pleroma.Config.put([:rich_media, :enabled], false)
248 test "posting a direct status", %{conn: conn} do
249 user1 = insert(:user)
250 user2 = insert(:user)
251 content = "direct cofe @#{user2.nickname}"
255 |> assign(:user, user1)
256 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
258 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
259 assert activity = Activity.get_by_id(id)
260 assert activity.recipients == [user2.ap_id, user1.ap_id]
261 assert activity.data["to"] == [user2.ap_id]
262 assert activity.data["cc"] == []
265 test "direct timeline", %{conn: conn} do
266 user_one = insert(:user)
267 user_two = insert(:user)
269 {:ok, user_two} = User.follow(user_two, user_one)
272 CommonAPI.post(user_one, %{
273 "status" => "Hi @#{user_two.nickname}!",
274 "visibility" => "direct"
277 {:ok, _follower_only} =
278 CommonAPI.post(user_one, %{
279 "status" => "Hi @#{user_two.nickname}!",
280 "visibility" => "private"
283 # Only direct should be visible here
286 |> assign(:user, user_two)
287 |> get("api/v1/timelines/direct")
289 [status] = json_response(res_conn, 200)
291 assert %{"visibility" => "direct"} = status
292 assert status["url"] != direct.data["id"]
294 # User should be able to see his own direct message
297 |> assign(:user, user_one)
298 |> get("api/v1/timelines/direct")
300 [status] = json_response(res_conn, 200)
302 assert %{"visibility" => "direct"} = status
304 # Both should be visible here
307 |> assign(:user, user_two)
308 |> get("api/v1/timelines/home")
310 [_s1, _s2] = json_response(res_conn, 200)
313 Enum.each(1..20, fn _ ->
315 CommonAPI.post(user_one, %{
316 "status" => "Hi @#{user_two.nickname}!",
317 "visibility" => "direct"
323 |> assign(:user, user_two)
324 |> get("api/v1/timelines/direct")
326 statuses = json_response(res_conn, 200)
327 assert length(statuses) == 20
331 |> assign(:user, user_two)
332 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
334 [status] = json_response(res_conn, 200)
336 assert status["url"] != direct.data["id"]
339 test "Conversations", %{conn: conn} do
340 user_one = insert(:user)
341 user_two = insert(:user)
343 {:ok, user_two} = User.follow(user_two, user_one)
346 CommonAPI.post(user_one, %{
347 "status" => "Hi @#{user_two.nickname}!",
348 "visibility" => "direct"
351 {:ok, _follower_only} =
352 CommonAPI.post(user_one, %{
353 "status" => "Hi @#{user_two.nickname}!",
354 "visibility" => "private"
359 |> assign(:user, user_one)
360 |> get("/api/v1/conversations")
362 assert response = json_response(res_conn, 200)
367 "accounts" => res_accounts,
368 "last_status" => res_last_status,
373 assert length(res_accounts) == 2
374 assert is_binary(res_id)
375 assert unread == true
376 assert res_last_status["id"] == direct.id
378 # Apparently undocumented API endpoint
381 |> assign(:user, user_one)
382 |> post("/api/v1/conversations/#{res_id}/read")
384 assert response = json_response(res_conn, 200)
385 assert length(response["accounts"]) == 2
386 assert response["last_status"]["id"] == direct.id
387 assert response["unread"] == false
389 # (vanilla) Mastodon frontend behaviour
392 |> assign(:user, user_one)
393 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
395 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
398 test "doesn't include DMs from blocked users", %{conn: conn} do
399 blocker = insert(:user)
400 blocked = insert(:user)
402 {:ok, blocker} = User.block(blocker, blocked)
404 {:ok, _blocked_direct} =
405 CommonAPI.post(blocked, %{
406 "status" => "Hi @#{blocker.nickname}!",
407 "visibility" => "direct"
411 CommonAPI.post(user, %{
412 "status" => "Hi @#{blocker.nickname}!",
413 "visibility" => "direct"
418 |> assign(:user, user)
419 |> get("api/v1/timelines/direct")
421 [status] = json_response(res_conn, 200)
422 assert status["id"] == direct.id
425 test "replying to a status", %{conn: conn} do
428 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
432 |> assign(:user, user)
433 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
435 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
437 activity = Activity.get_by_id(id)
439 assert activity.data["context"] == replied_to.data["context"]
440 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
443 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
448 |> assign(:user, user)
449 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
451 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
453 activity = Activity.get_by_id(id)
458 test "verify_credentials", %{conn: conn} do
463 |> assign(:user, user)
464 |> get("/api/v1/accounts/verify_credentials")
466 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
467 assert id == to_string(user.id)
470 test "verify_credentials default scope unlisted", %{conn: conn} do
471 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
475 |> assign(:user, user)
476 |> get("/api/v1/accounts/verify_credentials")
478 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
479 assert id == to_string(user.id)
482 test "apps/verify_credentials", %{conn: conn} do
483 token = insert(:oauth_token)
487 |> assign(:user, token.user)
488 |> assign(:token, token)
489 |> get("/api/v1/apps/verify_credentials")
491 app = Repo.preload(token, :app).app
494 "name" => app.client_name,
495 "website" => app.website,
496 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
499 assert expected == json_response(conn, 200)
502 test "creates an oauth app", %{conn: conn} do
504 app_attrs = build(:oauth_app)
508 |> assign(:user, user)
509 |> post("/api/v1/apps", %{
510 client_name: app_attrs.client_name,
511 redirect_uris: app_attrs.redirect_uris
514 [app] = Repo.all(App)
517 "name" => app.client_name,
518 "website" => app.website,
519 "client_id" => app.client_id,
520 "client_secret" => app.client_secret,
521 "id" => app.id |> to_string(),
522 "redirect_uri" => app.redirect_uris,
523 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
526 assert expected == json_response(conn, 200)
529 test "get a status", %{conn: conn} do
530 activity = insert(:note_activity)
534 |> get("/api/v1/statuses/#{activity.id}")
536 assert %{"id" => id} = json_response(conn, 200)
537 assert id == to_string(activity.id)
540 describe "deleting a status" do
541 test "when you created it", %{conn: conn} do
542 activity = insert(:note_activity)
543 author = User.get_cached_by_ap_id(activity.data["actor"])
547 |> assign(:user, author)
548 |> delete("/api/v1/statuses/#{activity.id}")
550 assert %{} = json_response(conn, 200)
552 refute Activity.get_by_id(activity.id)
555 test "when you didn't create it", %{conn: conn} do
556 activity = insert(:note_activity)
561 |> assign(:user, user)
562 |> delete("/api/v1/statuses/#{activity.id}")
564 assert %{"error" => _} = json_response(conn, 403)
566 assert Activity.get_by_id(activity.id) == activity
569 test "when you're an admin or moderator", %{conn: conn} do
570 activity1 = insert(:note_activity)
571 activity2 = insert(:note_activity)
572 admin = insert(:user, info: %{is_admin: true})
573 moderator = insert(:user, info: %{is_moderator: true})
577 |> assign(:user, admin)
578 |> delete("/api/v1/statuses/#{activity1.id}")
580 assert %{} = json_response(res_conn, 200)
584 |> assign(:user, moderator)
585 |> delete("/api/v1/statuses/#{activity2.id}")
587 assert %{} = json_response(res_conn, 200)
589 refute Activity.get_by_id(activity1.id)
590 refute Activity.get_by_id(activity2.id)
594 describe "filters" do
595 test "creating a filter", %{conn: conn} do
598 filter = %Pleroma.Filter{
605 |> assign(:user, user)
606 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
608 assert response = json_response(conn, 200)
609 assert response["phrase"] == filter.phrase
610 assert response["context"] == filter.context
611 assert response["irreversible"] == false
612 assert response["id"] != nil
613 assert response["id"] != ""
616 test "fetching a list of filters", %{conn: conn} do
619 query_one = %Pleroma.Filter{
626 query_two = %Pleroma.Filter{
633 {:ok, filter_one} = Pleroma.Filter.create(query_one)
634 {:ok, filter_two} = Pleroma.Filter.create(query_two)
638 |> assign(:user, user)
639 |> get("/api/v1/filters")
640 |> json_response(200)
646 filters: [filter_two, filter_one]
650 test "get a filter", %{conn: conn} do
653 query = %Pleroma.Filter{
660 {:ok, filter} = Pleroma.Filter.create(query)
664 |> assign(:user, user)
665 |> get("/api/v1/filters/#{filter.filter_id}")
667 assert _response = json_response(conn, 200)
670 test "update a filter", %{conn: conn} do
673 query = %Pleroma.Filter{
680 {:ok, _filter} = Pleroma.Filter.create(query)
682 new = %Pleroma.Filter{
689 |> assign(:user, user)
690 |> put("/api/v1/filters/#{query.filter_id}", %{
695 assert response = json_response(conn, 200)
696 assert response["phrase"] == new.phrase
697 assert response["context"] == new.context
700 test "delete a filter", %{conn: conn} do
703 query = %Pleroma.Filter{
710 {:ok, filter} = Pleroma.Filter.create(query)
714 |> assign(:user, user)
715 |> delete("/api/v1/filters/#{filter.filter_id}")
717 assert response = json_response(conn, 200)
718 assert response == %{}
723 test "creating a list", %{conn: conn} do
728 |> assign(:user, user)
729 |> post("/api/v1/lists", %{"title" => "cuties"})
731 assert %{"title" => title} = json_response(conn, 200)
732 assert title == "cuties"
735 test "adding users to a list", %{conn: conn} do
737 other_user = insert(:user)
738 {:ok, list} = Pleroma.List.create("name", user)
742 |> assign(:user, user)
743 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
745 assert %{} == json_response(conn, 200)
746 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
747 assert following == [other_user.follower_address]
750 test "removing users from a list", %{conn: conn} do
752 other_user = insert(:user)
753 third_user = insert(:user)
754 {:ok, list} = Pleroma.List.create("name", user)
755 {:ok, list} = Pleroma.List.follow(list, other_user)
756 {:ok, list} = Pleroma.List.follow(list, third_user)
760 |> assign(:user, user)
761 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
763 assert %{} == json_response(conn, 200)
764 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
765 assert following == [third_user.follower_address]
768 test "listing users in a list", %{conn: conn} do
770 other_user = insert(:user)
771 {:ok, list} = Pleroma.List.create("name", user)
772 {:ok, list} = Pleroma.List.follow(list, other_user)
776 |> assign(:user, user)
777 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
779 assert [%{"id" => id}] = json_response(conn, 200)
780 assert id == to_string(other_user.id)
783 test "retrieving a list", %{conn: conn} do
785 {:ok, list} = Pleroma.List.create("name", user)
789 |> assign(:user, user)
790 |> get("/api/v1/lists/#{list.id}")
792 assert %{"id" => id} = json_response(conn, 200)
793 assert id == to_string(list.id)
796 test "renaming a list", %{conn: conn} do
798 {:ok, list} = Pleroma.List.create("name", user)
802 |> assign(:user, user)
803 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
805 assert %{"title" => name} = json_response(conn, 200)
806 assert name == "newname"
809 test "deleting a list", %{conn: conn} do
811 {:ok, list} = Pleroma.List.create("name", user)
815 |> assign(:user, user)
816 |> delete("/api/v1/lists/#{list.id}")
818 assert %{} = json_response(conn, 200)
819 assert is_nil(Repo.get(Pleroma.List, list.id))
822 test "list timeline", %{conn: conn} do
824 other_user = insert(:user)
825 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
826 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
827 {:ok, list} = Pleroma.List.create("name", user)
828 {:ok, list} = Pleroma.List.follow(list, other_user)
832 |> assign(:user, user)
833 |> get("/api/v1/timelines/list/#{list.id}")
835 assert [%{"id" => id}] = json_response(conn, 200)
837 assert id == to_string(activity_two.id)
840 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
842 other_user = insert(:user)
843 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
845 {:ok, _activity_two} =
846 TwitterAPI.create_status(other_user, %{
847 "status" => "Marisa is cute.",
848 "visibility" => "private"
851 {:ok, list} = Pleroma.List.create("name", user)
852 {:ok, list} = Pleroma.List.follow(list, other_user)
856 |> assign(:user, user)
857 |> get("/api/v1/timelines/list/#{list.id}")
859 assert [%{"id" => id}] = json_response(conn, 200)
861 assert id == to_string(activity_one.id)
865 describe "notifications" do
866 test "list of notifications", %{conn: conn} do
868 other_user = insert(:user)
871 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
873 {:ok, [_notification]} = Notification.create_notifications(activity)
877 |> assign(:user, user)
878 |> get("/api/v1/notifications")
881 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
883 }\">@<span>#{user.nickname}</span></a></span>"
885 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
886 assert response == expected_response
889 test "getting a single notification", %{conn: conn} do
891 other_user = insert(:user)
894 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
896 {:ok, [notification]} = Notification.create_notifications(activity)
900 |> assign(:user, user)
901 |> get("/api/v1/notifications/#{notification.id}")
904 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
906 }\">@<span>#{user.nickname}</span></a></span>"
908 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
909 assert response == expected_response
912 test "dismissing a single notification", %{conn: conn} do
914 other_user = insert(:user)
917 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
919 {:ok, [notification]} = Notification.create_notifications(activity)
923 |> assign(:user, user)
924 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
926 assert %{} = json_response(conn, 200)
929 test "clearing all notifications", %{conn: conn} do
931 other_user = insert(:user)
934 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
936 {:ok, [_notification]} = Notification.create_notifications(activity)
940 |> assign(:user, user)
941 |> post("/api/v1/notifications/clear")
943 assert %{} = json_response(conn, 200)
947 |> assign(:user, user)
948 |> get("/api/v1/notifications")
950 assert all = json_response(conn, 200)
954 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
956 other_user = insert(:user)
958 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
959 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
960 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
961 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
963 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
964 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
965 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
966 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
970 |> assign(:user, user)
975 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
977 result = json_response(conn_res, 200)
978 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
983 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
985 result = json_response(conn_res, 200)
986 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
991 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
993 result = json_response(conn_res, 200)
994 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
997 test "filters notifications using exclude_types", %{conn: conn} do
999 other_user = insert(:user)
1001 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1002 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1003 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1004 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1005 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1007 mention_notification_id =
1008 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1010 favorite_notification_id =
1011 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1013 reblog_notification_id =
1014 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1016 follow_notification_id =
1017 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1021 |> assign(:user, user)
1024 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1026 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1029 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1031 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1034 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1036 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1039 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1041 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1044 test "destroy multiple", %{conn: conn} do
1045 user = insert(:user)
1046 other_user = insert(:user)
1048 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1049 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1050 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1051 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1053 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1054 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1055 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1056 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1060 |> assign(:user, user)
1064 |> get("/api/v1/notifications")
1066 result = json_response(conn_res, 200)
1067 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1071 |> assign(:user, other_user)
1075 |> get("/api/v1/notifications")
1077 result = json_response(conn_res, 200)
1078 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1082 |> delete("/api/v1/notifications/destroy_multiple", %{
1083 "ids" => [notification1_id, notification2_id]
1086 assert json_response(conn_destroy, 200) == %{}
1090 |> get("/api/v1/notifications")
1092 result = json_response(conn_res, 200)
1093 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1097 describe "reblogging" do
1098 test "reblogs and returns the reblogged status", %{conn: conn} do
1099 activity = insert(:note_activity)
1100 user = insert(:user)
1104 |> assign(:user, user)
1105 |> post("/api/v1/statuses/#{activity.id}/reblog")
1108 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1110 } = json_response(conn, 200)
1112 assert to_string(activity.id) == id
1115 test "reblogged status for another user", %{conn: conn} do
1116 activity = insert(:note_activity)
1117 user1 = insert(:user)
1118 user2 = insert(:user)
1119 user3 = insert(:user)
1120 CommonAPI.favorite(activity.id, user2)
1121 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1122 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1123 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1127 |> assign(:user, user3)
1128 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1131 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1132 "reblogged" => false,
1133 "favourited" => false,
1134 "bookmarked" => false
1135 } = json_response(conn_res, 200)
1139 |> assign(:user, user2)
1140 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1143 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1144 "reblogged" => true,
1145 "favourited" => true,
1146 "bookmarked" => true
1147 } = json_response(conn_res, 200)
1149 assert to_string(activity.id) == id
1153 describe "unreblogging" do
1154 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1155 activity = insert(:note_activity)
1156 user = insert(:user)
1158 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1162 |> assign(:user, user)
1163 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1165 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1167 assert to_string(activity.id) == id
1171 describe "favoriting" do
1172 test "favs a status and returns it", %{conn: conn} do
1173 activity = insert(:note_activity)
1174 user = insert(:user)
1178 |> assign(:user, user)
1179 |> post("/api/v1/statuses/#{activity.id}/favourite")
1181 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1182 json_response(conn, 200)
1184 assert to_string(activity.id) == id
1187 test "returns 500 for a wrong id", %{conn: conn} do
1188 user = insert(:user)
1192 |> assign(:user, user)
1193 |> post("/api/v1/statuses/1/favourite")
1194 |> json_response(500)
1196 assert resp == "Something went wrong"
1200 describe "unfavoriting" do
1201 test "unfavorites a status and returns it", %{conn: conn} do
1202 activity = insert(:note_activity)
1203 user = insert(:user)
1205 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1209 |> assign(:user, user)
1210 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1212 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1213 json_response(conn, 200)
1215 assert to_string(activity.id) == id
1219 describe "user timelines" do
1220 test "gets a users statuses", %{conn: conn} do
1221 user_one = insert(:user)
1222 user_two = insert(:user)
1223 user_three = insert(:user)
1225 {:ok, user_three} = User.follow(user_three, user_one)
1227 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1229 {:ok, direct_activity} =
1230 CommonAPI.post(user_one, %{
1231 "status" => "Hi, @#{user_two.nickname}.",
1232 "visibility" => "direct"
1235 {:ok, private_activity} =
1236 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1240 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1242 assert [%{"id" => id}] = json_response(resp, 200)
1243 assert id == to_string(activity.id)
1247 |> assign(:user, user_two)
1248 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1250 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1251 assert id_one == to_string(direct_activity.id)
1252 assert id_two == to_string(activity.id)
1256 |> assign(:user, user_three)
1257 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1259 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1260 assert id_one == to_string(private_activity.id)
1261 assert id_two == to_string(activity.id)
1264 test "unimplemented pinned statuses feature", %{conn: conn} do
1265 note = insert(:note_activity)
1266 user = User.get_cached_by_ap_id(note.data["actor"])
1270 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1272 assert json_response(conn, 200) == []
1275 test "gets an users media", %{conn: conn} do
1276 note = insert(:note_activity)
1277 user = User.get_cached_by_ap_id(note.data["actor"])
1279 file = %Plug.Upload{
1280 content_type: "image/jpg",
1281 path: Path.absname("test/fixtures/image.jpg"),
1282 filename: "an_image.jpg"
1286 TwitterAPI.upload(file, user, "json")
1290 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1294 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1296 assert [%{"id" => id}] = json_response(conn, 200)
1297 assert id == to_string(image_post.id)
1301 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1303 assert [%{"id" => id}] = json_response(conn, 200)
1304 assert id == to_string(image_post.id)
1307 test "gets a user's statuses without reblogs", %{conn: conn} do
1308 user = insert(:user)
1309 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1310 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1314 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1316 assert [%{"id" => id}] = json_response(conn, 200)
1317 assert id == to_string(post.id)
1321 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1323 assert [%{"id" => id}] = json_response(conn, 200)
1324 assert id == to_string(post.id)
1328 describe "user relationships" do
1329 test "returns the relationships for the current user", %{conn: conn} do
1330 user = insert(:user)
1331 other_user = insert(:user)
1332 {:ok, user} = User.follow(user, other_user)
1336 |> assign(:user, user)
1337 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1339 assert [relationship] = json_response(conn, 200)
1341 assert to_string(other_user.id) == relationship["id"]
1345 describe "locked accounts" do
1346 test "/api/v1/follow_requests works" do
1347 user = insert(:user, %{info: %User.Info{locked: true}})
1348 other_user = insert(:user)
1350 {:ok, _activity} = ActivityPub.follow(other_user, user)
1352 user = User.get_cached_by_id(user.id)
1353 other_user = User.get_cached_by_id(other_user.id)
1355 assert User.following?(other_user, user) == false
1359 |> assign(:user, user)
1360 |> get("/api/v1/follow_requests")
1362 assert [relationship] = json_response(conn, 200)
1363 assert to_string(other_user.id) == relationship["id"]
1366 test "/api/v1/follow_requests/:id/authorize works" do
1367 user = insert(:user, %{info: %User.Info{locked: true}})
1368 other_user = insert(:user)
1370 {:ok, _activity} = ActivityPub.follow(other_user, user)
1372 user = User.get_cached_by_id(user.id)
1373 other_user = User.get_cached_by_id(other_user.id)
1375 assert User.following?(other_user, user) == false
1379 |> assign(:user, user)
1380 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1382 assert relationship = json_response(conn, 200)
1383 assert to_string(other_user.id) == relationship["id"]
1385 user = User.get_cached_by_id(user.id)
1386 other_user = User.get_cached_by_id(other_user.id)
1388 assert User.following?(other_user, user) == true
1391 test "verify_credentials", %{conn: conn} do
1392 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1396 |> assign(:user, user)
1397 |> get("/api/v1/accounts/verify_credentials")
1399 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1400 assert id == to_string(user.id)
1403 test "/api/v1/follow_requests/:id/reject works" do
1404 user = insert(:user, %{info: %User.Info{locked: true}})
1405 other_user = insert(:user)
1407 {:ok, _activity} = ActivityPub.follow(other_user, user)
1409 user = User.get_cached_by_id(user.id)
1413 |> assign(:user, user)
1414 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1416 assert relationship = json_response(conn, 200)
1417 assert to_string(other_user.id) == relationship["id"]
1419 user = User.get_cached_by_id(user.id)
1420 other_user = User.get_cached_by_id(other_user.id)
1422 assert User.following?(other_user, user) == false
1426 test "account fetching", %{conn: conn} do
1427 user = insert(:user)
1431 |> get("/api/v1/accounts/#{user.id}")
1433 assert %{"id" => id} = json_response(conn, 200)
1434 assert id == to_string(user.id)
1438 |> get("/api/v1/accounts/-1")
1440 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1443 test "account fetching also works nickname", %{conn: conn} do
1444 user = insert(:user)
1448 |> get("/api/v1/accounts/#{user.nickname}")
1450 assert %{"id" => id} = json_response(conn, 200)
1451 assert id == user.id
1454 test "media upload", %{conn: conn} do
1455 file = %Plug.Upload{
1456 content_type: "image/jpg",
1457 path: Path.absname("test/fixtures/image.jpg"),
1458 filename: "an_image.jpg"
1461 desc = "Description of the image"
1463 user = insert(:user)
1467 |> assign(:user, user)
1468 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1470 assert media = json_response(conn, 200)
1472 assert media["type"] == "image"
1473 assert media["description"] == desc
1476 object = Repo.get(Object, media["id"])
1477 assert object.data["actor"] == User.ap_id(user)
1480 test "hashtag timeline", %{conn: conn} do
1481 following = insert(:user)
1484 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1486 {:ok, [_activity]} =
1487 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1491 |> get("/api/v1/timelines/tag/2hu")
1493 assert [%{"id" => id}] = json_response(nconn, 200)
1495 assert id == to_string(activity.id)
1497 # works for different capitalization too
1500 |> get("/api/v1/timelines/tag/2HU")
1502 assert [%{"id" => id}] = json_response(nconn, 200)
1504 assert id == to_string(activity.id)
1508 test "multi-hashtag timeline", %{conn: conn} do
1509 user = insert(:user)
1511 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1512 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1513 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1517 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1519 [status_none, status_test1, status_test] = json_response(any_test, 200)
1521 assert to_string(activity_test.id) == status_test["id"]
1522 assert to_string(activity_test1.id) == status_test1["id"]
1523 assert to_string(activity_none.id) == status_none["id"]
1527 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1529 assert [status_test1] == json_response(restricted_test, 200)
1531 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1533 assert [status_none] == json_response(all_test, 200)
1536 test "getting followers", %{conn: conn} do
1537 user = insert(:user)
1538 other_user = insert(:user)
1539 {:ok, user} = User.follow(user, other_user)
1543 |> get("/api/v1/accounts/#{other_user.id}/followers")
1545 assert [%{"id" => id}] = json_response(conn, 200)
1546 assert id == to_string(user.id)
1549 test "getting followers, hide_followers", %{conn: conn} do
1550 user = insert(:user)
1551 other_user = insert(:user, %{info: %{hide_followers: true}})
1552 {:ok, _user} = User.follow(user, other_user)
1556 |> get("/api/v1/accounts/#{other_user.id}/followers")
1558 assert [] == json_response(conn, 200)
1561 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1562 user = insert(:user)
1563 other_user = insert(:user, %{info: %{hide_followers: true}})
1564 {:ok, _user} = User.follow(user, other_user)
1568 |> assign(:user, other_user)
1569 |> get("/api/v1/accounts/#{other_user.id}/followers")
1571 refute [] == json_response(conn, 200)
1574 test "getting followers, pagination", %{conn: conn} do
1575 user = insert(:user)
1576 follower1 = insert(:user)
1577 follower2 = insert(:user)
1578 follower3 = insert(:user)
1579 {:ok, _} = User.follow(follower1, user)
1580 {:ok, _} = User.follow(follower2, user)
1581 {:ok, _} = User.follow(follower3, user)
1585 |> assign(:user, user)
1589 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1591 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1592 assert id3 == follower3.id
1593 assert id2 == follower2.id
1597 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1599 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1600 assert id2 == follower2.id
1601 assert id1 == follower1.id
1605 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1607 assert [%{"id" => id2}] = json_response(res_conn, 200)
1608 assert id2 == follower2.id
1610 assert [link_header] = get_resp_header(res_conn, "link")
1611 assert link_header =~ ~r/min_id=#{follower2.id}/
1612 assert link_header =~ ~r/max_id=#{follower2.id}/
1615 test "getting following", %{conn: conn} do
1616 user = insert(:user)
1617 other_user = insert(:user)
1618 {:ok, user} = User.follow(user, other_user)
1622 |> get("/api/v1/accounts/#{user.id}/following")
1624 assert [%{"id" => id}] = json_response(conn, 200)
1625 assert id == to_string(other_user.id)
1628 test "getting following, hide_follows", %{conn: conn} do
1629 user = insert(:user, %{info: %{hide_follows: true}})
1630 other_user = insert(:user)
1631 {:ok, user} = User.follow(user, other_user)
1635 |> get("/api/v1/accounts/#{user.id}/following")
1637 assert [] == json_response(conn, 200)
1640 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1641 user = insert(:user, %{info: %{hide_follows: true}})
1642 other_user = insert(:user)
1643 {:ok, user} = User.follow(user, other_user)
1647 |> assign(:user, user)
1648 |> get("/api/v1/accounts/#{user.id}/following")
1650 refute [] == json_response(conn, 200)
1653 test "getting following, pagination", %{conn: conn} do
1654 user = insert(:user)
1655 following1 = insert(:user)
1656 following2 = insert(:user)
1657 following3 = insert(:user)
1658 {:ok, _} = User.follow(user, following1)
1659 {:ok, _} = User.follow(user, following2)
1660 {:ok, _} = User.follow(user, following3)
1664 |> assign(:user, user)
1668 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1670 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1671 assert id3 == following3.id
1672 assert id2 == following2.id
1676 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1678 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1679 assert id2 == following2.id
1680 assert id1 == following1.id
1684 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1686 assert [%{"id" => id2}] = json_response(res_conn, 200)
1687 assert id2 == following2.id
1689 assert [link_header] = get_resp_header(res_conn, "link")
1690 assert link_header =~ ~r/min_id=#{following2.id}/
1691 assert link_header =~ ~r/max_id=#{following2.id}/
1694 test "following / unfollowing a user", %{conn: conn} do
1695 user = insert(:user)
1696 other_user = insert(:user)
1700 |> assign(:user, user)
1701 |> post("/api/v1/accounts/#{other_user.id}/follow")
1703 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1705 user = User.get_cached_by_id(user.id)
1709 |> assign(:user, user)
1710 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1712 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1714 user = User.get_cached_by_id(user.id)
1718 |> assign(:user, user)
1719 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1721 assert %{"id" => id} = json_response(conn, 200)
1722 assert id == to_string(other_user.id)
1725 test "following without reblogs" do
1726 follower = insert(:user)
1727 followed = insert(:user)
1728 other_user = insert(:user)
1732 |> assign(:user, follower)
1733 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1735 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1737 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1738 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1742 |> assign(:user, User.get_cached_by_id(follower.id))
1743 |> get("/api/v1/timelines/home")
1745 assert [] == json_response(conn, 200)
1749 |> assign(:user, follower)
1750 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1752 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1756 |> assign(:user, User.get_cached_by_id(follower.id))
1757 |> get("/api/v1/timelines/home")
1759 expected_activity_id = reblog.id
1760 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1763 test "following / unfollowing errors" do
1764 user = insert(:user)
1768 |> assign(:user, user)
1771 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1772 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1775 user = User.get_cached_by_id(user.id)
1776 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1777 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1779 # self follow via uri
1780 user = User.get_cached_by_id(user.id)
1781 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1782 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1784 # follow non existing user
1785 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1786 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1788 # follow non existing user via uri
1789 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1790 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1792 # unfollow non existing user
1793 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1794 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1797 test "muting / unmuting a user", %{conn: conn} do
1798 user = insert(:user)
1799 other_user = insert(:user)
1803 |> assign(:user, user)
1804 |> post("/api/v1/accounts/#{other_user.id}/mute")
1806 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1808 user = User.get_cached_by_id(user.id)
1812 |> assign(:user, user)
1813 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1815 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1818 test "subscribing / unsubscribing to a user", %{conn: conn} do
1819 user = insert(:user)
1820 subscription_target = insert(:user)
1824 |> assign(:user, user)
1825 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1827 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1831 |> assign(:user, user)
1832 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1834 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1837 test "getting a list of mutes", %{conn: conn} do
1838 user = insert(:user)
1839 other_user = insert(:user)
1841 {:ok, user} = User.mute(user, other_user)
1845 |> assign(:user, user)
1846 |> get("/api/v1/mutes")
1848 other_user_id = to_string(other_user.id)
1849 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1852 test "blocking / unblocking a user", %{conn: conn} do
1853 user = insert(:user)
1854 other_user = insert(:user)
1858 |> assign(:user, user)
1859 |> post("/api/v1/accounts/#{other_user.id}/block")
1861 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1863 user = User.get_cached_by_id(user.id)
1867 |> assign(:user, user)
1868 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1870 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1873 test "getting a list of blocks", %{conn: conn} do
1874 user = insert(:user)
1875 other_user = insert(:user)
1877 {:ok, user} = User.block(user, other_user)
1881 |> assign(:user, user)
1882 |> get("/api/v1/blocks")
1884 other_user_id = to_string(other_user.id)
1885 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1888 test "blocking / unblocking a domain", %{conn: conn} do
1889 user = insert(:user)
1890 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1894 |> assign(:user, user)
1895 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1897 assert %{} = json_response(conn, 200)
1898 user = User.get_cached_by_ap_id(user.ap_id)
1899 assert User.blocks?(user, other_user)
1903 |> assign(:user, user)
1904 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1906 assert %{} = json_response(conn, 200)
1907 user = User.get_cached_by_ap_id(user.ap_id)
1908 refute User.blocks?(user, other_user)
1911 test "getting a list of domain blocks", %{conn: conn} do
1912 user = insert(:user)
1914 {:ok, user} = User.block_domain(user, "bad.site")
1915 {:ok, user} = User.block_domain(user, "even.worse.site")
1919 |> assign(:user, user)
1920 |> get("/api/v1/domain_blocks")
1922 domain_blocks = json_response(conn, 200)
1924 assert "bad.site" in domain_blocks
1925 assert "even.worse.site" in domain_blocks
1928 test "unimplemented follow_requests, blocks, domain blocks" do
1929 user = insert(:user)
1931 ["blocks", "domain_blocks", "follow_requests"]
1932 |> Enum.each(fn endpoint ->
1935 |> assign(:user, user)
1936 |> get("/api/v1/#{endpoint}")
1938 assert [] = json_response(conn, 200)
1942 test "account search", %{conn: conn} do
1943 user = insert(:user)
1944 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1945 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1949 |> assign(:user, user)
1950 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1951 |> json_response(200)
1953 result_ids = for result <- results, do: result["acct"]
1955 assert user_two.nickname in result_ids
1956 assert user_three.nickname in result_ids
1960 |> assign(:user, user)
1961 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
1962 |> json_response(200)
1964 result_ids = for result <- results, do: result["acct"]
1966 assert user_three.nickname in result_ids
1969 test "search", %{conn: conn} do
1970 user = insert(:user)
1971 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1972 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1974 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
1977 CommonAPI.post(user, %{
1978 "status" => "This is about 2hu, but private",
1979 "visibility" => "private"
1982 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
1986 |> get("/api/v1/search", %{"q" => "2hu"})
1988 assert results = json_response(conn, 200)
1990 [account | _] = results["accounts"]
1991 assert account["id"] == to_string(user_three.id)
1993 assert results["hashtags"] == []
1995 [status] = results["statuses"]
1996 assert status["id"] == to_string(activity.id)
1999 test "search fetches remote statuses", %{conn: conn} do
2003 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
2005 assert results = json_response(conn, 200)
2007 [status] = results["statuses"]
2008 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
2012 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
2014 CommonAPI.post(insert(:user), %{
2015 "status" => "This is about 2hu, but private",
2016 "visibility" => "private"
2022 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2024 assert results = json_response(conn, 200)
2026 [] = results["statuses"]
2030 test "search fetches remote accounts", %{conn: conn} do
2033 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2035 assert results = json_response(conn, 200)
2036 [account] = results["accounts"]
2037 assert account["acct"] == "shp@social.heldscal.la"
2040 test "returns the favorites of a user", %{conn: conn} do
2041 user = insert(:user)
2042 other_user = insert(:user)
2044 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2045 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2047 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2051 |> assign(:user, user)
2052 |> get("/api/v1/favourites")
2054 assert [status] = json_response(first_conn, 200)
2055 assert status["id"] == to_string(activity.id)
2057 assert [{"link", _link_header}] =
2058 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2060 # Honours query params
2061 {:ok, second_activity} =
2062 CommonAPI.post(other_user, %{
2064 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2067 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2069 last_like = status["id"]
2073 |> assign(:user, user)
2074 |> get("/api/v1/favourites?since_id=#{last_like}")
2076 assert [second_status] = json_response(second_conn, 200)
2077 assert second_status["id"] == to_string(second_activity.id)
2081 |> assign(:user, user)
2082 |> get("/api/v1/favourites?limit=0")
2084 assert [] = json_response(third_conn, 200)
2087 describe "getting favorites timeline of specified user" do
2089 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2090 [current_user: current_user, user: user]
2093 test "returns list of statuses favorited by specified user", %{
2095 current_user: current_user,
2098 [activity | _] = insert_pair(:note_activity)
2099 CommonAPI.favorite(activity.id, user)
2103 |> assign(:user, current_user)
2104 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2105 |> json_response(:ok)
2109 assert length(response) == 1
2110 assert like["id"] == activity.id
2113 test "returns favorites for specified user_id when user is not logged in", %{
2117 activity = insert(:note_activity)
2118 CommonAPI.favorite(activity.id, user)
2122 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2123 |> json_response(:ok)
2125 assert length(response) == 1
2128 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2130 current_user: current_user,
2134 CommonAPI.post(current_user, %{
2135 "status" => "Hi @#{user.nickname}!",
2136 "visibility" => "direct"
2139 CommonAPI.favorite(direct.id, user)
2143 |> assign(:user, current_user)
2144 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2145 |> json_response(:ok)
2147 assert length(response) == 1
2149 anonymous_response =
2151 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2152 |> json_response(:ok)
2154 assert Enum.empty?(anonymous_response)
2157 test "does not return others' favorited DM when user is not one of recipients", %{
2159 current_user: current_user,
2162 user_two = insert(:user)
2165 CommonAPI.post(user_two, %{
2166 "status" => "Hi @#{user.nickname}!",
2167 "visibility" => "direct"
2170 CommonAPI.favorite(direct.id, user)
2174 |> assign(:user, current_user)
2175 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2176 |> json_response(:ok)
2178 assert Enum.empty?(response)
2181 test "paginates favorites using since_id and max_id", %{
2183 current_user: current_user,
2186 activities = insert_list(10, :note_activity)
2188 Enum.each(activities, fn activity ->
2189 CommonAPI.favorite(activity.id, user)
2192 third_activity = Enum.at(activities, 2)
2193 seventh_activity = Enum.at(activities, 6)
2197 |> assign(:user, current_user)
2198 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2199 since_id: third_activity.id,
2200 max_id: seventh_activity.id
2202 |> json_response(:ok)
2204 assert length(response) == 3
2205 refute third_activity in response
2206 refute seventh_activity in response
2209 test "limits favorites using limit parameter", %{
2211 current_user: current_user,
2215 |> insert_list(:note_activity)
2216 |> Enum.each(fn activity ->
2217 CommonAPI.favorite(activity.id, user)
2222 |> assign(:user, current_user)
2223 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2224 |> json_response(:ok)
2226 assert length(response) == 3
2229 test "returns empty response when user does not have any favorited statuses", %{
2231 current_user: current_user,
2236 |> assign(:user, current_user)
2237 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2238 |> json_response(:ok)
2240 assert Enum.empty?(response)
2243 test "returns 404 error when specified user is not exist", %{conn: conn} do
2244 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2246 assert json_response(conn, 404) == %{"error" => "Record not found"}
2249 test "returns 403 error when user has hidden own favorites", %{
2251 current_user: current_user
2253 user = insert(:user, %{info: %{hide_favorites: true}})
2254 activity = insert(:note_activity)
2255 CommonAPI.favorite(activity.id, user)
2259 |> assign(:user, current_user)
2260 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2262 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2265 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2266 user = insert(:user)
2267 activity = insert(:note_activity)
2268 CommonAPI.favorite(activity.id, user)
2272 |> assign(:user, current_user)
2273 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2275 assert user.info.hide_favorites
2276 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2280 describe "updating credentials" do
2281 test "updates the user's bio", %{conn: conn} do
2282 user = insert(:user)
2283 user2 = insert(:user)
2287 |> assign(:user, user)
2288 |> patch("/api/v1/accounts/update_credentials", %{
2289 "note" => "I drink #cofe with @#{user2.nickname}"
2292 assert user = json_response(conn, 200)
2294 assert user["note"] ==
2295 ~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=") <>
2297 ~s(" class="u-url mention" href=") <>
2298 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2301 test "updates the user's locking status", %{conn: conn} do
2302 user = insert(:user)
2306 |> assign(:user, user)
2307 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2309 assert user = json_response(conn, 200)
2310 assert user["locked"] == true
2313 test "updates the user's default scope", %{conn: conn} do
2314 user = insert(:user)
2318 |> assign(:user, user)
2319 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2321 assert user = json_response(conn, 200)
2322 assert user["source"]["privacy"] == "cofe"
2325 test "updates the user's hide_followers status", %{conn: conn} do
2326 user = insert(:user)
2330 |> assign(:user, user)
2331 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2333 assert user = json_response(conn, 200)
2334 assert user["pleroma"]["hide_followers"] == true
2337 test "updates the user's hide_follows status", %{conn: conn} do
2338 user = insert(:user)
2342 |> assign(:user, user)
2343 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2345 assert user = json_response(conn, 200)
2346 assert user["pleroma"]["hide_follows"] == true
2349 test "updates the user's hide_favorites status", %{conn: conn} do
2350 user = insert(:user)
2354 |> assign(:user, user)
2355 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2357 assert user = json_response(conn, 200)
2358 assert user["pleroma"]["hide_favorites"] == true
2361 test "updates the user's show_role status", %{conn: conn} do
2362 user = insert(:user)
2366 |> assign(:user, user)
2367 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2369 assert user = json_response(conn, 200)
2370 assert user["source"]["pleroma"]["show_role"] == false
2373 test "updates the user's no_rich_text status", %{conn: conn} do
2374 user = insert(:user)
2378 |> assign(:user, user)
2379 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2381 assert user = json_response(conn, 200)
2382 assert user["source"]["pleroma"]["no_rich_text"] == true
2385 test "updates the user's name", %{conn: conn} do
2386 user = insert(:user)
2390 |> assign(:user, user)
2391 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2393 assert user = json_response(conn, 200)
2394 assert user["display_name"] == "markorepairs"
2397 test "updates the user's avatar", %{conn: conn} do
2398 user = insert(:user)
2400 new_avatar = %Plug.Upload{
2401 content_type: "image/jpg",
2402 path: Path.absname("test/fixtures/image.jpg"),
2403 filename: "an_image.jpg"
2408 |> assign(:user, user)
2409 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2411 assert user_response = json_response(conn, 200)
2412 assert user_response["avatar"] != User.avatar_url(user)
2415 test "updates the user's banner", %{conn: conn} do
2416 user = insert(:user)
2418 new_header = %Plug.Upload{
2419 content_type: "image/jpg",
2420 path: Path.absname("test/fixtures/image.jpg"),
2421 filename: "an_image.jpg"
2426 |> assign(:user, user)
2427 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2429 assert user_response = json_response(conn, 200)
2430 assert user_response["header"] != User.banner_url(user)
2433 test "requires 'write' permission", %{conn: conn} do
2434 token1 = insert(:oauth_token, scopes: ["read"])
2435 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2437 for token <- [token1, token2] do
2440 |> put_req_header("authorization", "Bearer #{token.token}")
2441 |> patch("/api/v1/accounts/update_credentials", %{})
2443 if token == token1 do
2444 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2446 assert json_response(conn, 200)
2451 test "updates profile emojos", %{conn: conn} do
2452 user = insert(:user)
2454 note = "*sips :blank:*"
2455 name = "I am :firefox:"
2459 |> assign(:user, user)
2460 |> patch("/api/v1/accounts/update_credentials", %{
2462 "display_name" => name
2465 assert json_response(conn, 200)
2469 |> get("/api/v1/accounts/#{user.id}")
2471 assert user = json_response(conn, 200)
2473 assert user["note"] == note
2474 assert user["display_name"] == name
2475 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2479 test "get instance information", %{conn: conn} do
2480 conn = get(conn, "/api/v1/instance")
2481 assert result = json_response(conn, 200)
2483 email = Pleroma.Config.get([:instance, :email])
2484 # Note: not checking for "max_toot_chars" since it's optional
2490 "email" => from_config_email,
2492 "streaming_api" => _
2497 "registrations" => _
2500 assert email == from_config_email
2503 test "get instance stats", %{conn: conn} do
2504 user = insert(:user, %{local: true})
2506 user2 = insert(:user, %{local: true})
2507 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2509 insert(:user, %{local: false, nickname: "u@peer1.com"})
2510 insert(:user, %{local: false, nickname: "u@peer2.com"})
2512 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2514 # Stats should count users with missing or nil `info.deactivated` value
2515 user = User.get_cached_by_id(user.id)
2516 info_change = Changeset.change(user.info, %{deactivated: nil})
2520 |> Changeset.change()
2521 |> Changeset.put_embed(:info, info_change)
2522 |> User.update_and_set_cache()
2524 Pleroma.Stats.update_stats()
2526 conn = get(conn, "/api/v1/instance")
2528 assert result = json_response(conn, 200)
2530 stats = result["stats"]
2533 assert stats["user_count"] == 1
2534 assert stats["status_count"] == 1
2535 assert stats["domain_count"] == 2
2538 test "get peers", %{conn: conn} do
2539 insert(:user, %{local: false, nickname: "u@peer1.com"})
2540 insert(:user, %{local: false, nickname: "u@peer2.com"})
2542 Pleroma.Stats.update_stats()
2544 conn = get(conn, "/api/v1/instance/peers")
2546 assert result = json_response(conn, 200)
2548 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2551 test "put settings", %{conn: conn} do
2552 user = insert(:user)
2556 |> assign(:user, user)
2557 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2559 assert _result = json_response(conn, 200)
2561 user = User.get_cached_by_ap_id(user.ap_id)
2562 assert user.info.settings == %{"programming" => "socks"}
2565 describe "pinned statuses" do
2567 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2569 user = insert(:user)
2570 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2572 [user: user, activity: activity]
2575 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2576 {:ok, _} = CommonAPI.pin(activity.id, user)
2580 |> assign(:user, user)
2581 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2582 |> json_response(200)
2584 id_str = to_string(activity.id)
2586 assert [%{"id" => ^id_str, "pinned" => true}] = result
2589 test "pin status", %{conn: conn, user: user, activity: activity} do
2590 id_str = to_string(activity.id)
2592 assert %{"id" => ^id_str, "pinned" => true} =
2594 |> assign(:user, user)
2595 |> post("/api/v1/statuses/#{activity.id}/pin")
2596 |> json_response(200)
2598 assert [%{"id" => ^id_str, "pinned" => true}] =
2600 |> assign(:user, user)
2601 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2602 |> json_response(200)
2605 test "unpin status", %{conn: conn, user: user, activity: activity} do
2606 {:ok, _} = CommonAPI.pin(activity.id, user)
2608 id_str = to_string(activity.id)
2609 user = refresh_record(user)
2611 assert %{"id" => ^id_str, "pinned" => false} =
2613 |> assign(:user, user)
2614 |> post("/api/v1/statuses/#{activity.id}/unpin")
2615 |> json_response(200)
2619 |> assign(:user, user)
2620 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2621 |> json_response(200)
2624 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2625 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2627 id_str_one = to_string(activity_one.id)
2629 assert %{"id" => ^id_str_one, "pinned" => true} =
2631 |> assign(:user, user)
2632 |> post("/api/v1/statuses/#{id_str_one}/pin")
2633 |> json_response(200)
2635 user = refresh_record(user)
2637 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2639 |> assign(:user, user)
2640 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2641 |> json_response(400)
2644 test "Status rich-media Card", %{conn: conn, user: user} do
2645 Pleroma.Config.put([:rich_media, :enabled], true)
2646 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2650 |> get("/api/v1/statuses/#{activity.id}/card")
2651 |> json_response(200)
2653 assert response == %{
2654 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2655 "provider_name" => "www.imdb.com",
2656 "provider_url" => "http://www.imdb.com",
2657 "title" => "The Rock",
2659 "url" => "http://www.imdb.com/title/tt0117500/",
2660 "description" => nil,
2663 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2664 "title" => "The Rock",
2665 "type" => "video.movie",
2666 "url" => "http://www.imdb.com/title/tt0117500/"
2671 # works with private posts
2673 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2677 |> assign(:user, user)
2678 |> get("/api/v1/statuses/#{activity.id}/card")
2679 |> json_response(200)
2681 assert response_two == response
2683 Pleroma.Config.put([:rich_media, :enabled], false)
2688 user = insert(:user)
2689 for_user = insert(:user)
2692 CommonAPI.post(user, %{
2693 "status" => "heweoo?"
2697 CommonAPI.post(user, %{
2698 "status" => "heweoo!"
2703 |> assign(:user, for_user)
2704 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2706 assert json_response(response1, 200)["bookmarked"] == true
2710 |> assign(:user, for_user)
2711 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2713 assert json_response(response2, 200)["bookmarked"] == true
2717 |> assign(:user, for_user)
2718 |> get("/api/v1/bookmarks")
2720 assert [json_response(response2, 200), json_response(response1, 200)] ==
2721 json_response(bookmarks, 200)
2725 |> assign(:user, for_user)
2726 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2728 assert json_response(response1, 200)["bookmarked"] == false
2732 |> assign(:user, for_user)
2733 |> get("/api/v1/bookmarks")
2735 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2738 describe "conversation muting" do
2740 user = insert(:user)
2741 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2743 [user: user, activity: activity]
2746 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2747 id_str = to_string(activity.id)
2749 assert %{"id" => ^id_str, "muted" => true} =
2751 |> assign(:user, user)
2752 |> post("/api/v1/statuses/#{activity.id}/mute")
2753 |> json_response(200)
2756 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2757 {:ok, _} = CommonAPI.add_mute(user, activity)
2759 id_str = to_string(activity.id)
2760 user = refresh_record(user)
2762 assert %{"id" => ^id_str, "muted" => false} =
2764 |> assign(:user, user)
2765 |> post("/api/v1/statuses/#{activity.id}/unmute")
2766 |> json_response(200)
2770 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2771 user = insert(:user)
2775 |> assign(:user, user)
2776 |> get("/api/v1/pleroma/flavour")
2778 assert "glitch" == json_response(get_old_flavour, 200)
2782 |> assign(:user, user)
2783 |> post("/api/v1/pleroma/flavour/vanilla")
2785 assert "vanilla" == json_response(set_flavour, 200)
2789 |> assign(:user, user)
2790 |> post("/api/v1/pleroma/flavour/vanilla")
2792 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2795 describe "reports" do
2797 reporter = insert(:user)
2798 target_user = insert(:user)
2800 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2802 [reporter: reporter, target_user: target_user, activity: activity]
2805 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2806 assert %{"action_taken" => false, "id" => _} =
2808 |> assign(:user, reporter)
2809 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2810 |> json_response(200)
2813 test "submit a report with statuses and comment", %{
2816 target_user: target_user,
2819 assert %{"action_taken" => false, "id" => _} =
2821 |> assign(:user, reporter)
2822 |> post("/api/v1/reports", %{
2823 "account_id" => target_user.id,
2824 "status_ids" => [activity.id],
2825 "comment" => "bad status!"
2827 |> json_response(200)
2830 test "account_id is required", %{
2835 assert %{"error" => "Valid `account_id` required"} =
2837 |> assign(:user, reporter)
2838 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2839 |> json_response(400)
2842 test "comment must be up to the size specified in the config", %{
2845 target_user: target_user
2847 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2848 comment = String.pad_trailing("a", max_size + 1, "a")
2850 error = %{"error" => "Comment must be up to #{max_size} characters"}
2854 |> assign(:user, reporter)
2855 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2856 |> json_response(400)
2860 describe "link headers" do
2861 test "preserves parameters in link headers", %{conn: conn} do
2862 user = insert(:user)
2863 other_user = insert(:user)
2866 CommonAPI.post(other_user, %{
2867 "status" => "hi @#{user.nickname}",
2868 "visibility" => "public"
2872 CommonAPI.post(other_user, %{
2873 "status" => "hi @#{user.nickname}",
2874 "visibility" => "public"
2877 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2878 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2882 |> assign(:user, user)
2883 |> get("/api/v1/notifications", %{media_only: true})
2885 assert [link_header] = get_resp_header(conn, "link")
2886 assert link_header =~ ~r/media_only=true/
2887 assert link_header =~ ~r/min_id=#{notification2.id}/
2888 assert link_header =~ ~r/max_id=#{notification1.id}/
2892 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2893 # Need to set an old-style integer ID to reproduce the problem
2894 # (these are no longer assigned to new accounts but were preserved
2895 # for existing accounts during the migration to flakeIDs)
2896 user_one = insert(:user, %{id: 1212})
2897 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2901 |> get("/api/v1/accounts/#{user_one.id}")
2905 |> get("/api/v1/accounts/#{user_two.nickname}")
2909 |> get("/api/v1/accounts/#{user_two.id}")
2911 acc_one = json_response(resp_one, 200)
2912 acc_two = json_response(resp_two, 200)
2913 acc_three = json_response(resp_three, 200)
2914 refute acc_one == acc_two
2915 assert acc_two == acc_three
2918 describe "custom emoji" do
2919 test "with tags", %{conn: conn} do
2922 |> get("/api/v1/custom_emojis")
2923 |> json_response(200)
2925 assert Map.has_key?(emoji, "shortcode")
2926 assert Map.has_key?(emoji, "static_url")
2927 assert Map.has_key?(emoji, "tags")
2928 assert is_list(emoji["tags"])
2929 assert Map.has_key?(emoji, "url")
2930 assert Map.has_key?(emoji, "visible_in_picker")
2934 describe "index/2 redirections" do
2935 setup %{conn: conn} do
2939 signing_salt: "cooldude"
2944 |> Plug.Session.call(Plug.Session.init(session_opts))
2947 test_path = "/web/statuses/test"
2948 %{conn: conn, path: test_path}
2951 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2952 conn = get(conn, path)
2954 assert conn.status == 302
2955 assert redirected_to(conn) == "/web/login"
2958 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2959 token = insert(:oauth_token)
2963 |> assign(:user, token.user)
2964 |> put_session(:oauth_token, token.token)
2967 assert conn.status == 200
2970 test "saves referer path to session", %{conn: conn, path: path} do
2971 conn = get(conn, path)
2972 return_to = Plug.Conn.get_session(conn, :return_to)
2974 assert return_to == path
2977 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2978 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2979 auth = insert(:oauth_authorization, app: app)
2983 |> put_session(:return_to, path)
2984 |> get("/web/login", %{code: auth.token})
2986 assert conn.status == 302
2987 assert redirected_to(conn) == path
2990 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2991 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2992 auth = insert(:oauth_authorization, app: app)
2994 conn = get(conn, "/web/login", %{code: auth.token})
2996 assert conn.status == 302
2997 assert redirected_to(conn) == "/web/getting-started"
3001 describe "scheduled activities" do
3002 test "creates a scheduled activity", %{conn: conn} do
3003 user = insert(:user)
3004 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3008 |> assign(:user, user)
3009 |> post("/api/v1/statuses", %{
3010 "status" => "scheduled",
3011 "scheduled_at" => scheduled_at
3014 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3015 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3016 assert [] == Repo.all(Activity)
3019 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3020 user = insert(:user)
3021 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3023 file = %Plug.Upload{
3024 content_type: "image/jpg",
3025 path: Path.absname("test/fixtures/image.jpg"),
3026 filename: "an_image.jpg"
3029 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3033 |> assign(:user, user)
3034 |> post("/api/v1/statuses", %{
3035 "media_ids" => [to_string(upload.id)],
3036 "status" => "scheduled",
3037 "scheduled_at" => scheduled_at
3040 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3041 assert %{"type" => "image"} = media_attachment
3044 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3046 user = insert(:user)
3049 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3053 |> assign(:user, user)
3054 |> post("/api/v1/statuses", %{
3055 "status" => "not scheduled",
3056 "scheduled_at" => scheduled_at
3059 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3060 assert [] == Repo.all(ScheduledActivity)
3063 test "returns error when daily user limit is exceeded", %{conn: conn} do
3064 user = insert(:user)
3067 NaiveDateTime.utc_now()
3068 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3069 |> NaiveDateTime.to_iso8601()
3071 attrs = %{params: %{}, scheduled_at: today}
3072 {:ok, _} = ScheduledActivity.create(user, attrs)
3073 {:ok, _} = ScheduledActivity.create(user, attrs)
3077 |> assign(:user, user)
3078 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3080 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3083 test "returns error when total user limit is exceeded", %{conn: conn} do
3084 user = insert(:user)
3087 NaiveDateTime.utc_now()
3088 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3089 |> NaiveDateTime.to_iso8601()
3092 NaiveDateTime.utc_now()
3093 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3094 |> NaiveDateTime.to_iso8601()
3096 attrs = %{params: %{}, scheduled_at: today}
3097 {:ok, _} = ScheduledActivity.create(user, attrs)
3098 {:ok, _} = ScheduledActivity.create(user, attrs)
3099 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3103 |> assign(:user, user)
3104 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3106 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3109 test "shows scheduled activities", %{conn: conn} do
3110 user = insert(:user)
3111 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3112 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3113 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3114 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3118 |> assign(:user, user)
3123 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3125 result = json_response(conn_res, 200)
3126 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3131 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3133 result = json_response(conn_res, 200)
3134 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3139 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3141 result = json_response(conn_res, 200)
3142 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3145 test "shows a scheduled activity", %{conn: conn} do
3146 user = insert(:user)
3147 scheduled_activity = insert(:scheduled_activity, user: user)
3151 |> assign(:user, user)
3152 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3154 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3155 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3159 |> assign(:user, user)
3160 |> get("/api/v1/scheduled_statuses/404")
3162 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3165 test "updates a scheduled activity", %{conn: conn} do
3166 user = insert(:user)
3167 scheduled_activity = insert(:scheduled_activity, user: user)
3170 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3174 |> assign(:user, user)
3175 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3176 scheduled_at: new_scheduled_at
3179 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3180 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3184 |> assign(:user, user)
3185 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3187 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3190 test "deletes a scheduled activity", %{conn: conn} do
3191 user = insert(:user)
3192 scheduled_activity = insert(:scheduled_activity, user: user)
3196 |> assign(:user, user)
3197 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3199 assert %{} = json_response(res_conn, 200)
3200 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3204 |> assign(:user, user)
3205 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3207 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3211 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3212 user1 = insert(:user)
3213 user2 = insert(:user)
3214 user3 = insert(:user)
3216 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3218 # Reply to status from another user
3221 |> assign(:user, user2)
3222 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3224 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3226 activity = Activity.get_by_id_with_object(id)
3228 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3229 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3231 # Reblog from the third user
3234 |> assign(:user, user3)
3235 |> post("/api/v1/statuses/#{activity.id}/reblog")
3237 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3238 json_response(conn2, 200)
3240 assert to_string(activity.id) == id
3242 # Getting third user status
3245 |> assign(:user, user3)
3246 |> get("api/v1/timelines/home")
3248 [reblogged_activity] = json_response(conn3, 200)
3250 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3252 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3253 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3256 describe "create account by app" do
3258 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3259 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3260 interval = Pleroma.Config.get([:app_account_creation, :interval])
3262 Pleroma.Config.put([:app_account_creation, :enabled], true)
3263 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3264 Pleroma.Config.put([:app_account_creation, :interval], 1)
3267 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3268 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3269 Pleroma.Config.put([:app_account_creation, :interval], interval)
3275 test "Account registration via Application", %{conn: conn} do
3278 |> post("/api/v1/apps", %{
3279 client_name: "client_name",
3280 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3281 scopes: "read, write, follow"
3285 "client_id" => client_id,
3286 "client_secret" => client_secret,
3288 "name" => "client_name",
3289 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3292 } = json_response(conn, 200)
3296 |> post("/oauth/token", %{
3297 grant_type: "client_credentials",
3298 client_id: client_id,
3299 client_secret: client_secret
3302 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3303 json_response(conn, 200)
3306 token_from_db = Repo.get_by(Token, token: token)
3307 assert token_from_db
3309 assert scope == "read write follow"
3313 |> put_req_header("authorization", "Bearer " <> token)
3314 |> post("/api/v1/accounts", %{
3316 email: "lain@example.org",
3317 password: "PlzDontHackLain",
3322 "access_token" => token,
3323 "created_at" => _created_at,
3325 "token_type" => "Bearer"
3326 } = json_response(conn, 200)
3328 token_from_db = Repo.get_by(Token, token: token)
3329 assert token_from_db
3330 token_from_db = Repo.preload(token_from_db, :user)
3331 assert token_from_db.user
3333 assert token_from_db.user.info.confirmation_pending
3336 test "rate limit", %{conn: conn} do
3337 app_token = insert(:oauth_token, user: nil)
3340 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3341 |> Map.put(:remote_ip, {15, 15, 15, 15})
3346 |> post("/api/v1/accounts", %{
3347 username: "#{i}lain",
3348 email: "#{i}lain@example.org",
3349 password: "PlzDontHackLain",
3354 "access_token" => token,
3355 "created_at" => _created_at,
3357 "token_type" => "Bearer"
3358 } = json_response(conn, 200)
3360 token_from_db = Repo.get_by(Token, token: token)
3361 assert token_from_db
3362 token_from_db = Repo.preload(token_from_db, :user)
3363 assert token_from_db.user
3365 assert token_from_db.user.info.confirmation_pending
3370 |> post("/api/v1/accounts", %{
3372 email: "6lain@example.org",
3373 password: "PlzDontHackLain",
3377 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}