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.ActivityExpiration
12 alias Pleroma.Notification
15 alias Pleroma.ScheduledActivity
16 alias Pleroma.SubscriptionNotification
18 alias Pleroma.Web.ActivityPub.ActivityPub
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.MastodonAPI.FilterView
21 alias Pleroma.Web.OAuth.App
22 alias Pleroma.Web.OAuth.Token
23 alias Pleroma.Web.OStatus
24 alias Pleroma.Web.Push
25 import Pleroma.Factory
26 import ExUnit.CaptureLog
28 import Swoosh.TestAssertions
30 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
33 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
37 clear_config([:instance, :public])
38 clear_config([:rich_media, :enabled])
40 test "the home timeline", %{conn: conn} do
42 following = insert(:user)
44 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
51 assert Enum.empty?(json_response(conn, 200))
53 {:ok, user} = User.follow(user, following)
57 |> assign(:user, user)
58 |> get("/api/v1/timelines/home")
60 assert [%{"content" => "test"}] = json_response(conn, 200)
63 test "the public timeline", %{conn: conn} do
64 following = insert(:user)
67 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
70 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
74 |> get("/api/v1/timelines/public", %{"local" => "False"})
76 assert length(json_response(conn, 200)) == 2
80 |> get("/api/v1/timelines/public", %{"local" => "True"})
82 assert [%{"content" => "test"}] = json_response(conn, 200)
86 |> get("/api/v1/timelines/public", %{"local" => "1"})
88 assert [%{"content" => "test"}] = json_response(conn, 200)
92 test "the public timeline when public is set to false", %{conn: conn} do
93 Config.put([:instance, :public], false)
96 |> get("/api/v1/timelines/public", %{"local" => "False"})
97 |> json_response(403) == %{"error" => "This resource requires authentication."}
100 describe "posting statuses" do
106 |> assign(:user, user)
111 test "posting a status", %{conn: conn} do
112 idempotency_key = "Pikachu rocks!"
116 |> put_req_header("idempotency-key", idempotency_key)
117 |> post("/api/v1/statuses", %{
119 "spoiler_text" => "2hu",
120 "sensitive" => "false"
123 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
125 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
127 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
128 json_response(conn_one, 200)
130 assert Activity.get_by_id(id)
134 |> put_req_header("idempotency-key", idempotency_key)
135 |> post("/api/v1/statuses", %{
137 "spoiler_text" => "2hu",
138 "sensitive" => "false"
141 assert %{"id" => second_id} = json_response(conn_two, 200)
142 assert id == second_id
146 |> post("/api/v1/statuses", %{
148 "spoiler_text" => "2hu",
149 "sensitive" => "false"
152 assert %{"id" => third_id} = json_response(conn_three, 200)
153 refute id == third_id
155 # An activity that will expire:
157 expires_in = 120 * 60
161 |> post("api/v1/statuses", %{
162 "status" => "oolong",
163 "expires_in" => expires_in
166 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
167 assert activity = Activity.get_by_id(fourth_id)
168 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
170 estimated_expires_at =
171 NaiveDateTime.utc_now()
172 |> NaiveDateTime.add(expires_in)
173 |> NaiveDateTime.truncate(:second)
175 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
176 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
178 assert fourth_response["pleroma"]["expires_at"] ==
179 NaiveDateTime.to_iso8601(expiration.scheduled_at)
182 test "replying to a status", %{conn: conn} do
184 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
188 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
190 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
192 activity = Activity.get_by_id(id)
194 assert activity.data["context"] == replied_to.data["context"]
195 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
198 test "replying to a direct message with visibility other than direct", %{conn: conn} do
200 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
202 Enum.each(["public", "private", "unlisted"], fn visibility ->
205 |> post("/api/v1/statuses", %{
206 "status" => "@#{user.nickname} hey",
207 "in_reply_to_id" => replied_to.id,
208 "visibility" => visibility
211 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
215 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
218 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
220 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
221 assert Activity.get_by_id(id)
224 test "posting a sensitive status", %{conn: conn} do
227 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
229 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
230 assert Activity.get_by_id(id)
233 test "posting a fake status", %{conn: conn} do
236 |> post("/api/v1/statuses", %{
238 "\"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"
241 real_status = json_response(real_conn, 200)
244 assert Object.get_by_ap_id(real_status["uri"])
248 |> Map.put("id", nil)
249 |> Map.put("url", nil)
250 |> Map.put("uri", nil)
251 |> Map.put("created_at", nil)
252 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
256 |> post("/api/v1/statuses", %{
258 "\"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",
262 fake_status = json_response(fake_conn, 200)
265 refute Object.get_by_ap_id(fake_status["uri"])
269 |> Map.put("id", nil)
270 |> Map.put("url", nil)
271 |> Map.put("uri", nil)
272 |> Map.put("created_at", nil)
273 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
275 assert real_status == fake_status
278 test "posting a status with OGP link preview", %{conn: conn} do
279 Config.put([:rich_media, :enabled], true)
283 |> post("/api/v1/statuses", %{
284 "status" => "https://example.com/ogp"
287 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
288 assert Activity.get_by_id(id)
291 test "posting a direct status", %{conn: conn} do
292 user2 = insert(:user)
293 content = "direct cofe @#{user2.nickname}"
297 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
299 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
300 assert activity = Activity.get_by_id(id)
301 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
302 assert activity.data["to"] == [user2.ap_id]
303 assert activity.data["cc"] == []
307 describe "posting polls" do
308 test "posting a poll", %{conn: conn} do
310 time = NaiveDateTime.utc_now()
314 |> assign(:user, user)
315 |> post("/api/v1/statuses", %{
316 "status" => "Who is the #bestgrill?",
317 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
320 response = json_response(conn, 200)
322 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
323 title in ["Rei", "Asuka", "Misato"]
326 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
327 refute response["poll"]["expred"]
330 test "option limit is enforced", %{conn: conn} do
332 limit = Config.get([:instance, :poll_limits, :max_options])
336 |> assign(:user, user)
337 |> post("/api/v1/statuses", %{
339 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
342 %{"error" => error} = json_response(conn, 422)
343 assert error == "Poll can't contain more than #{limit} options"
346 test "option character limit is enforced", %{conn: conn} do
348 limit = Config.get([:instance, :poll_limits, :max_option_chars])
352 |> assign(:user, user)
353 |> post("/api/v1/statuses", %{
356 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
361 %{"error" => error} = json_response(conn, 422)
362 assert error == "Poll options cannot be longer than #{limit} characters each"
365 test "minimal date limit is enforced", %{conn: conn} do
367 limit = Config.get([:instance, :poll_limits, :min_expiration])
371 |> assign(:user, user)
372 |> post("/api/v1/statuses", %{
373 "status" => "imagine arbitrary limits",
375 "options" => ["this post was made by pleroma gang"],
376 "expires_in" => limit - 1
380 %{"error" => error} = json_response(conn, 422)
381 assert error == "Expiration date is too soon"
384 test "maximum date limit is enforced", %{conn: conn} do
386 limit = Config.get([:instance, :poll_limits, :max_expiration])
390 |> assign(:user, user)
391 |> post("/api/v1/statuses", %{
392 "status" => "imagine arbitrary limits",
394 "options" => ["this post was made by pleroma gang"],
395 "expires_in" => limit + 1
399 %{"error" => error} = json_response(conn, 422)
400 assert error == "Expiration date is too far in the future"
404 test "direct timeline", %{conn: conn} do
405 user_one = insert(:user)
406 user_two = insert(:user)
408 {:ok, user_two} = User.follow(user_two, user_one)
411 CommonAPI.post(user_one, %{
412 "status" => "Hi @#{user_two.nickname}!",
413 "visibility" => "direct"
416 {:ok, _follower_only} =
417 CommonAPI.post(user_one, %{
418 "status" => "Hi @#{user_two.nickname}!",
419 "visibility" => "private"
422 # Only direct should be visible here
425 |> assign(:user, user_two)
426 |> get("api/v1/timelines/direct")
428 [status] = json_response(res_conn, 200)
430 assert %{"visibility" => "direct"} = status
431 assert status["url"] != direct.data["id"]
433 # User should be able to see their own direct message
436 |> assign(:user, user_one)
437 |> get("api/v1/timelines/direct")
439 [status] = json_response(res_conn, 200)
441 assert %{"visibility" => "direct"} = status
443 # Both should be visible here
446 |> assign(:user, user_two)
447 |> get("api/v1/timelines/home")
449 [_s1, _s2] = json_response(res_conn, 200)
452 Enum.each(1..20, fn _ ->
454 CommonAPI.post(user_one, %{
455 "status" => "Hi @#{user_two.nickname}!",
456 "visibility" => "direct"
462 |> assign(:user, user_two)
463 |> get("api/v1/timelines/direct")
465 statuses = json_response(res_conn, 200)
466 assert length(statuses) == 20
470 |> assign(:user, user_two)
471 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
473 [status] = json_response(res_conn, 200)
475 assert status["url"] != direct.data["id"]
478 test "Conversations", %{conn: conn} do
479 user_one = insert(:user)
480 user_two = insert(:user)
481 user_three = insert(:user)
483 {:ok, user_two} = User.follow(user_two, user_one)
486 CommonAPI.post(user_one, %{
487 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
488 "visibility" => "direct"
491 {:ok, _follower_only} =
492 CommonAPI.post(user_one, %{
493 "status" => "Hi @#{user_two.nickname}!",
494 "visibility" => "private"
499 |> assign(:user, user_one)
500 |> get("/api/v1/conversations")
502 assert response = json_response(res_conn, 200)
507 "accounts" => res_accounts,
508 "last_status" => res_last_status,
513 account_ids = Enum.map(res_accounts, & &1["id"])
514 assert length(res_accounts) == 2
515 assert user_two.id in account_ids
516 assert user_three.id in account_ids
517 assert is_binary(res_id)
518 assert unread == true
519 assert res_last_status["id"] == direct.id
521 # Apparently undocumented API endpoint
524 |> assign(:user, user_one)
525 |> post("/api/v1/conversations/#{res_id}/read")
527 assert response = json_response(res_conn, 200)
528 assert length(response["accounts"]) == 2
529 assert response["last_status"]["id"] == direct.id
530 assert response["unread"] == false
532 # (vanilla) Mastodon frontend behaviour
535 |> assign(:user, user_one)
536 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
538 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
541 test "doesn't include DMs from blocked users", %{conn: conn} do
542 blocker = insert(:user)
543 blocked = insert(:user)
545 {:ok, blocker} = User.block(blocker, blocked)
547 {:ok, _blocked_direct} =
548 CommonAPI.post(blocked, %{
549 "status" => "Hi @#{blocker.nickname}!",
550 "visibility" => "direct"
554 CommonAPI.post(user, %{
555 "status" => "Hi @#{blocker.nickname}!",
556 "visibility" => "direct"
561 |> assign(:user, user)
562 |> get("api/v1/timelines/direct")
564 [status] = json_response(res_conn, 200)
565 assert status["id"] == direct.id
568 test "verify_credentials", %{conn: conn} do
573 |> assign(:user, user)
574 |> get("/api/v1/accounts/verify_credentials")
576 response = json_response(conn, 200)
578 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
579 assert response["pleroma"]["chat_token"]
580 assert id == to_string(user.id)
583 test "verify_credentials default scope unlisted", %{conn: conn} do
584 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
588 |> assign(:user, user)
589 |> get("/api/v1/accounts/verify_credentials")
591 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
592 assert id == to_string(user.id)
595 test "apps/verify_credentials", %{conn: conn} do
596 token = insert(:oauth_token)
600 |> assign(:user, token.user)
601 |> assign(:token, token)
602 |> get("/api/v1/apps/verify_credentials")
604 app = Repo.preload(token, :app).app
607 "name" => app.client_name,
608 "website" => app.website,
609 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
612 assert expected == json_response(conn, 200)
615 test "user avatar can be set", %{conn: conn} do
617 avatar_image = File.read!("test/fixtures/avatar_data_uri")
621 |> assign(:user, user)
622 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
624 user = refresh_record(user)
638 assert %{"url" => _} = json_response(conn, 200)
641 test "user avatar can be reset", %{conn: conn} do
646 |> assign(:user, user)
647 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
649 user = User.get_cached_by_id(user.id)
651 assert user.avatar == nil
653 assert %{"url" => nil} = json_response(conn, 200)
656 test "can set profile banner", %{conn: conn} do
661 |> assign(:user, user)
662 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
664 user = refresh_record(user)
665 assert user.info.banner["type"] == "Image"
667 assert %{"url" => _} = json_response(conn, 200)
670 test "can reset profile banner", %{conn: conn} do
675 |> assign(:user, user)
676 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
678 user = refresh_record(user)
679 assert user.info.banner == %{}
681 assert %{"url" => nil} = json_response(conn, 200)
684 test "background image can be set", %{conn: conn} do
689 |> assign(:user, user)
690 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
692 user = refresh_record(user)
693 assert user.info.background["type"] == "Image"
694 assert %{"url" => _} = json_response(conn, 200)
697 test "background image can be reset", %{conn: conn} do
702 |> assign(:user, user)
703 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
705 user = refresh_record(user)
706 assert user.info.background == %{}
707 assert %{"url" => nil} = json_response(conn, 200)
710 test "creates an oauth app", %{conn: conn} do
712 app_attrs = build(:oauth_app)
716 |> assign(:user, user)
717 |> post("/api/v1/apps", %{
718 client_name: app_attrs.client_name,
719 redirect_uris: app_attrs.redirect_uris
722 [app] = Repo.all(App)
725 "name" => app.client_name,
726 "website" => app.website,
727 "client_id" => app.client_id,
728 "client_secret" => app.client_secret,
729 "id" => app.id |> to_string(),
730 "redirect_uri" => app.redirect_uris,
731 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
734 assert expected == json_response(conn, 200)
737 test "get a status", %{conn: conn} do
738 activity = insert(:note_activity)
742 |> get("/api/v1/statuses/#{activity.id}")
744 assert %{"id" => id} = json_response(conn, 200)
745 assert id == to_string(activity.id)
748 test "get statuses by IDs", %{conn: conn} do
749 %{id: id1} = insert(:note_activity)
750 %{id: id2} = insert(:note_activity)
752 query_string = "ids[]=#{id1}&ids[]=#{id2}"
753 conn = get(conn, "/api/v1/statuses/?#{query_string}")
755 assert [%{"id" => ^id1}, %{"id" => ^id2}] = json_response(conn, :ok)
758 describe "deleting a status" do
759 test "when you created it", %{conn: conn} do
760 activity = insert(:note_activity)
761 author = User.get_cached_by_ap_id(activity.data["actor"])
765 |> assign(:user, author)
766 |> delete("/api/v1/statuses/#{activity.id}")
768 assert %{} = json_response(conn, 200)
770 refute Activity.get_by_id(activity.id)
773 test "when you didn't create it", %{conn: conn} do
774 activity = insert(:note_activity)
779 |> assign(:user, user)
780 |> delete("/api/v1/statuses/#{activity.id}")
782 assert %{"error" => _} = json_response(conn, 403)
784 assert Activity.get_by_id(activity.id) == activity
787 test "when you're an admin or moderator", %{conn: conn} do
788 activity1 = insert(:note_activity)
789 activity2 = insert(:note_activity)
790 admin = insert(:user, info: %{is_admin: true})
791 moderator = insert(:user, info: %{is_moderator: true})
795 |> assign(:user, admin)
796 |> delete("/api/v1/statuses/#{activity1.id}")
798 assert %{} = json_response(res_conn, 200)
802 |> assign(:user, moderator)
803 |> delete("/api/v1/statuses/#{activity2.id}")
805 assert %{} = json_response(res_conn, 200)
807 refute Activity.get_by_id(activity1.id)
808 refute Activity.get_by_id(activity2.id)
812 describe "filters" do
813 test "creating a filter", %{conn: conn} do
816 filter = %Pleroma.Filter{
823 |> assign(:user, user)
824 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
826 assert response = json_response(conn, 200)
827 assert response["phrase"] == filter.phrase
828 assert response["context"] == filter.context
829 assert response["irreversible"] == false
830 assert response["id"] != nil
831 assert response["id"] != ""
834 test "fetching a list of filters", %{conn: conn} do
837 query_one = %Pleroma.Filter{
844 query_two = %Pleroma.Filter{
851 {:ok, filter_one} = Pleroma.Filter.create(query_one)
852 {:ok, filter_two} = Pleroma.Filter.create(query_two)
856 |> assign(:user, user)
857 |> get("/api/v1/filters")
858 |> json_response(200)
864 filters: [filter_two, filter_one]
868 test "get a filter", %{conn: conn} do
871 query = %Pleroma.Filter{
878 {:ok, filter} = Pleroma.Filter.create(query)
882 |> assign(:user, user)
883 |> get("/api/v1/filters/#{filter.filter_id}")
885 assert _response = json_response(conn, 200)
888 test "update a filter", %{conn: conn} do
891 query = %Pleroma.Filter{
898 {:ok, _filter} = Pleroma.Filter.create(query)
900 new = %Pleroma.Filter{
907 |> assign(:user, user)
908 |> put("/api/v1/filters/#{query.filter_id}", %{
913 assert response = json_response(conn, 200)
914 assert response["phrase"] == new.phrase
915 assert response["context"] == new.context
918 test "delete a filter", %{conn: conn} do
921 query = %Pleroma.Filter{
928 {:ok, filter} = Pleroma.Filter.create(query)
932 |> assign(:user, user)
933 |> delete("/api/v1/filters/#{filter.filter_id}")
935 assert response = json_response(conn, 200)
936 assert response == %{}
940 describe "list timelines" do
941 test "list timeline", %{conn: conn} do
943 other_user = insert(:user)
944 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
945 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
946 {:ok, list} = Pleroma.List.create("name", user)
947 {:ok, list} = Pleroma.List.follow(list, other_user)
951 |> assign(:user, user)
952 |> get("/api/v1/timelines/list/#{list.id}")
954 assert [%{"id" => id}] = json_response(conn, 200)
956 assert id == to_string(activity_two.id)
959 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
961 other_user = insert(:user)
962 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
964 {:ok, _activity_two} =
965 CommonAPI.post(other_user, %{
966 "status" => "Marisa is cute.",
967 "visibility" => "private"
970 {:ok, list} = Pleroma.List.create("name", user)
971 {:ok, list} = Pleroma.List.follow(list, other_user)
975 |> assign(:user, user)
976 |> get("/api/v1/timelines/list/#{list.id}")
978 assert [%{"id" => id}] = json_response(conn, 200)
980 assert id == to_string(activity_one.id)
984 describe "notifications" do
985 test "list of notifications", %{conn: conn} do
987 other_user = insert(:user)
989 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
991 {:ok, [_notification]} = Notification.create_notifications(activity)
995 |> assign(:user, user)
996 |> get("/api/v1/notifications")
999 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1001 }\">@<span>#{user.nickname}</span></a></span>"
1003 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1004 assert response == expected_response
1007 test "getting a single notification", %{conn: conn} do
1008 user = insert(:user)
1009 other_user = insert(:user)
1011 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1013 {:ok, [notification]} = Notification.create_notifications(activity)
1017 |> assign(:user, user)
1018 |> get("/api/v1/notifications/#{notification.id}")
1021 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1023 }\">@<span>#{user.nickname}</span></a></span>"
1025 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1026 assert response == expected_response
1029 test "dismissing a single notification", %{conn: conn} do
1030 user = insert(:user)
1031 other_user = insert(:user)
1033 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1035 {:ok, [notification]} = Notification.create_notifications(activity)
1039 |> assign(:user, user)
1040 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1042 assert %{} = json_response(conn, 200)
1045 test "clearing all notifications", %{conn: conn} do
1046 user = insert(:user)
1047 other_user = insert(:user)
1049 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1051 {:ok, [_notification]} = Notification.create_notifications(activity)
1055 |> assign(:user, user)
1056 |> post("/api/v1/notifications/clear")
1058 assert %{} = json_response(conn, 200)
1062 |> assign(:user, user)
1063 |> get("/api/v1/notifications")
1065 assert all = json_response(conn, 200)
1069 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1070 user = insert(:user)
1071 other_user = insert(:user)
1073 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1074 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1075 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1076 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1078 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1079 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1080 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1081 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1085 |> assign(:user, user)
1090 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1092 result = json_response(conn_res, 200)
1093 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1098 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1100 result = json_response(conn_res, 200)
1101 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1106 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1108 result = json_response(conn_res, 200)
1109 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1112 test "filters notifications using exclude_types", %{conn: conn} do
1113 user = insert(:user)
1114 other_user = insert(:user)
1116 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1117 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1118 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1119 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1120 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1122 mention_notification_id =
1123 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1125 favorite_notification_id =
1126 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1128 reblog_notification_id =
1129 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1131 follow_notification_id =
1132 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1136 |> assign(:user, user)
1139 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1141 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1144 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1146 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1149 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1151 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1154 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1156 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1159 test "destroy multiple", %{conn: conn} do
1160 user = insert(:user)
1161 other_user = insert(:user)
1163 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1164 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1165 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1166 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1168 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1169 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1170 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1171 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1175 |> assign(:user, user)
1179 |> get("/api/v1/notifications")
1181 result = json_response(conn_res, 200)
1182 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1186 |> assign(:user, other_user)
1190 |> get("/api/v1/notifications")
1192 result = json_response(conn_res, 200)
1193 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1197 |> delete("/api/v1/notifications/destroy_multiple", %{
1198 "ids" => [notification1_id, notification2_id]
1201 assert json_response(conn_destroy, 200) == %{}
1205 |> get("/api/v1/notifications")
1207 result = json_response(conn_res, 200)
1208 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1211 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1212 user = insert(:user)
1213 user2 = insert(:user)
1215 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1216 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1218 conn = assign(conn, :user, user)
1220 conn = get(conn, "/api/v1/notifications")
1222 assert length(json_response(conn, 200)) == 1
1224 {:ok, user} = User.mute(user, user2)
1226 conn = assign(build_conn(), :user, user)
1227 conn = get(conn, "/api/v1/notifications")
1229 assert json_response(conn, 200) == []
1232 test "see notifications after muting user without notifications", %{conn: conn} do
1233 user = insert(:user)
1234 user2 = insert(:user)
1236 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1237 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1239 conn = assign(conn, :user, user)
1241 conn = get(conn, "/api/v1/notifications")
1243 assert length(json_response(conn, 200)) == 1
1245 {:ok, user} = User.mute(user, user2, false)
1247 conn = assign(build_conn(), :user, user)
1248 conn = get(conn, "/api/v1/notifications")
1250 assert length(json_response(conn, 200)) == 1
1253 test "see notifications after muting user with notifications and with_muted parameter", %{
1256 user = insert(:user)
1257 user2 = insert(:user)
1259 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1260 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1262 conn = assign(conn, :user, user)
1264 conn = get(conn, "/api/v1/notifications")
1266 assert length(json_response(conn, 200)) == 1
1268 {:ok, user} = User.mute(user, user2)
1270 conn = assign(build_conn(), :user, user)
1271 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1273 assert length(json_response(conn, 200)) == 1
1277 describe "subscription_notifications" do
1279 user = insert(:user)
1280 subscriber = insert(:user)
1282 User.subscribe(subscriber, user)
1284 {:ok, %{user: user, subscriber: subscriber}}
1287 test "list of notifications", %{conn: conn, user: user, subscriber: subscriber} do
1288 status_text = "Hello"
1289 {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text})
1293 |> assign(:user, subscriber)
1294 |> get("/api/v1/notifications/subscription")
1296 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1297 assert response == status_text
1300 test "getting a single notification", %{conn: conn, user: user, subscriber: subscriber} do
1301 status_text = "Hello"
1303 {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text})
1304 [notification] = Repo.all(SubscriptionNotification)
1308 |> assign(:user, subscriber)
1309 |> get("/api/v1/notifications/subscription/#{notification.id}")
1311 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1312 assert response == status_text
1315 test "dismissing a single notification also deletes it", %{
1318 subscriber: subscriber
1320 status_text = "Hello"
1321 {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text})
1323 [notification] = Repo.all(SubscriptionNotification)
1327 |> assign(:user, subscriber)
1328 |> post("/api/v1/notifications/subscription/dismiss", %{"id" => notification.id})
1330 assert %{} = json_response(conn, 200)
1332 assert Repo.all(SubscriptionNotification) == []
1335 test "clearing all notifications also deletes them", %{
1338 subscriber: subscriber
1340 status_text1 = "Hello"
1341 status_text2 = "Hello again"
1342 {:ok, _activity1} = CommonAPI.post(user, %{"status" => status_text1})
1343 {:ok, _activity2} = CommonAPI.post(user, %{"status" => status_text2})
1347 |> assign(:user, subscriber)
1348 |> post("/api/v1/notifications/subscription/clear")
1350 assert %{} = json_response(conn, 200)
1354 |> assign(:user, subscriber)
1355 |> get("/api/v1/notifications/subscription")
1357 assert json_response(conn, 200) == []
1359 assert Repo.all(SubscriptionNotification) == []
1362 test "paginates notifications using min_id, since_id, max_id, and limit", %{
1365 subscriber: subscriber
1367 {:ok, activity1} = CommonAPI.post(user, %{"status" => "Hello 1"})
1368 {:ok, activity2} = CommonAPI.post(user, %{"status" => "Hello 2"})
1369 {:ok, activity3} = CommonAPI.post(user, %{"status" => "Hello 3"})
1370 {:ok, activity4} = CommonAPI.post(user, %{"status" => "Hello 4"})
1373 Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string()
1376 Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string()
1379 Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string()
1382 Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string()
1384 conn = assign(conn, :user, subscriber)
1388 get(conn, "/api/v1/notifications/subscription?limit=2&min_id=#{notification1_id}")
1390 result = json_response(conn_res, 200)
1391 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1395 get(conn, "/api/v1/notifications/subscription?limit=2&since_id=#{notification1_id}")
1397 result = json_response(conn_res, 200)
1398 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1402 get(conn, "/api/v1/notifications/subscription?limit=2&max_id=#{notification4_id}")
1404 result = json_response(conn_res, 200)
1405 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1408 test "destroy multiple", %{conn: conn, user: user1, subscriber: user2} do
1409 # mutual subscription
1410 User.subscribe(user1, user2)
1412 {:ok, activity1} = CommonAPI.post(user1, %{"status" => "Hello 1"})
1413 {:ok, activity2} = CommonAPI.post(user1, %{"status" => "World 1"})
1414 {:ok, activity3} = CommonAPI.post(user2, %{"status" => "Hello 2"})
1415 {:ok, activity4} = CommonAPI.post(user2, %{"status" => "World 2"})
1418 Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string()
1421 Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string()
1424 Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string()
1427 Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string()
1429 conn = assign(conn, :user, user1)
1431 conn_res = get(conn, "/api/v1/notifications/subscription")
1433 result = json_response(conn_res, 200)
1435 Enum.each(result, fn %{"id" => id} ->
1436 assert id in [notification3_id, notification4_id]
1439 conn2 = assign(conn, :user, user2)
1441 conn_res = get(conn2, "/api/v1/notifications/subscription")
1443 result = json_response(conn_res, 200)
1445 Enum.each(result, fn %{"id" => id} ->
1446 assert id in [notification1_id, notification2_id]
1450 delete(conn, "/api/v1/notifications/subscription/destroy_multiple", %{
1451 "ids" => [notification3_id, notification4_id]
1454 assert json_response(conn_destroy, 200) == %{}
1456 conn_res = get(conn2, "/api/v1/notifications/subscription")
1458 result = json_response(conn_res, 200)
1460 Enum.each(result, fn %{"id" => id} ->
1461 assert id in [notification1_id, notification2_id]
1464 assert length(Repo.all(SubscriptionNotification)) == 2
1468 describe "reblogging" do
1469 test "reblogs and returns the reblogged status", %{conn: conn} do
1470 activity = insert(:note_activity)
1471 user = insert(:user)
1475 |> assign(:user, user)
1476 |> post("/api/v1/statuses/#{activity.id}/reblog")
1479 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1481 } = json_response(conn, 200)
1483 assert to_string(activity.id) == id
1486 test "reblogged status for another user", %{conn: conn} do
1487 activity = insert(:note_activity)
1488 user1 = insert(:user)
1489 user2 = insert(:user)
1490 user3 = insert(:user)
1491 CommonAPI.favorite(activity.id, user2)
1492 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1493 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1494 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1498 |> assign(:user, user3)
1499 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1502 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1503 "reblogged" => false,
1504 "favourited" => false,
1505 "bookmarked" => false
1506 } = json_response(conn_res, 200)
1510 |> assign(:user, user2)
1511 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1514 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1515 "reblogged" => true,
1516 "favourited" => true,
1517 "bookmarked" => true
1518 } = json_response(conn_res, 200)
1520 assert to_string(activity.id) == id
1523 test "returns 400 error when activity is not exist", %{conn: conn} do
1524 user = insert(:user)
1528 |> assign(:user, user)
1529 |> post("/api/v1/statuses/foo/reblog")
1531 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1535 describe "unreblogging" do
1536 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1537 activity = insert(:note_activity)
1538 user = insert(:user)
1540 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1544 |> assign(:user, user)
1545 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1547 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1549 assert to_string(activity.id) == id
1552 test "returns 400 error when activity is not exist", %{conn: conn} do
1553 user = insert(:user)
1557 |> assign(:user, user)
1558 |> post("/api/v1/statuses/foo/unreblog")
1560 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1564 describe "favoriting" do
1565 test "favs a status and returns it", %{conn: conn} do
1566 activity = insert(:note_activity)
1567 user = insert(:user)
1571 |> assign(:user, user)
1572 |> post("/api/v1/statuses/#{activity.id}/favourite")
1574 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1575 json_response(conn, 200)
1577 assert to_string(activity.id) == id
1580 test "returns 400 error for a wrong id", %{conn: conn} do
1581 user = insert(:user)
1585 |> assign(:user, user)
1586 |> post("/api/v1/statuses/1/favourite")
1588 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1592 describe "unfavoriting" do
1593 test "unfavorites a status and returns it", %{conn: conn} do
1594 activity = insert(:note_activity)
1595 user = insert(:user)
1597 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1601 |> assign(:user, user)
1602 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1604 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1605 json_response(conn, 200)
1607 assert to_string(activity.id) == id
1610 test "returns 400 error for a wrong id", %{conn: conn} do
1611 user = insert(:user)
1615 |> assign(:user, user)
1616 |> post("/api/v1/statuses/1/unfavourite")
1618 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1622 describe "user timelines" do
1623 test "gets a users statuses", %{conn: conn} do
1624 user_one = insert(:user)
1625 user_two = insert(:user)
1626 user_three = insert(:user)
1628 {:ok, user_three} = User.follow(user_three, user_one)
1630 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1632 {:ok, direct_activity} =
1633 CommonAPI.post(user_one, %{
1634 "status" => "Hi, @#{user_two.nickname}.",
1635 "visibility" => "direct"
1638 {:ok, private_activity} =
1639 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1643 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1645 assert [%{"id" => id}] = json_response(resp, 200)
1646 assert id == to_string(activity.id)
1650 |> assign(:user, user_two)
1651 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1653 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1654 assert id_one == to_string(direct_activity.id)
1655 assert id_two == to_string(activity.id)
1659 |> assign(:user, user_three)
1660 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1662 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1663 assert id_one == to_string(private_activity.id)
1664 assert id_two == to_string(activity.id)
1667 test "unimplemented pinned statuses feature", %{conn: conn} do
1668 note = insert(:note_activity)
1669 user = User.get_cached_by_ap_id(note.data["actor"])
1673 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1675 assert json_response(conn, 200) == []
1678 test "gets an users media", %{conn: conn} do
1679 note = insert(:note_activity)
1680 user = User.get_cached_by_ap_id(note.data["actor"])
1682 file = %Plug.Upload{
1683 content_type: "image/jpg",
1684 path: Path.absname("test/fixtures/image.jpg"),
1685 filename: "an_image.jpg"
1688 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1690 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1694 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1696 assert [%{"id" => id}] = json_response(conn, 200)
1697 assert id == to_string(image_post.id)
1701 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1703 assert [%{"id" => id}] = json_response(conn, 200)
1704 assert id == to_string(image_post.id)
1707 test "gets a user's statuses without reblogs", %{conn: conn} do
1708 user = insert(:user)
1709 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1710 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1714 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1716 assert [%{"id" => id}] = json_response(conn, 200)
1717 assert id == to_string(post.id)
1721 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1723 assert [%{"id" => id}] = json_response(conn, 200)
1724 assert id == to_string(post.id)
1727 test "filters user's statuses by a hashtag", %{conn: conn} do
1728 user = insert(:user)
1729 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1730 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1734 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1736 assert [%{"id" => id}] = json_response(conn, 200)
1737 assert id == to_string(post.id)
1741 describe "user relationships" do
1742 test "returns the relationships for the current user", %{conn: conn} do
1743 user = insert(:user)
1744 other_user = insert(:user)
1745 {:ok, user} = User.follow(user, other_user)
1749 |> assign(:user, user)
1750 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1752 assert [relationship] = json_response(conn, 200)
1754 assert to_string(other_user.id) == relationship["id"]
1758 describe "media upload" do
1760 user = insert(:user)
1764 |> assign(:user, user)
1766 image = %Plug.Upload{
1767 content_type: "image/jpg",
1768 path: Path.absname("test/fixtures/image.jpg"),
1769 filename: "an_image.jpg"
1772 [conn: conn, image: image]
1775 clear_config([:media_proxy])
1776 clear_config([Pleroma.Upload])
1778 test "returns uploaded image", %{conn: conn, image: image} do
1779 desc = "Description of the image"
1783 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1784 |> json_response(:ok)
1786 assert media["type"] == "image"
1787 assert media["description"] == desc
1790 object = Repo.get(Object, media["id"])
1791 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1795 describe "locked accounts" do
1796 test "/api/v1/follow_requests works" do
1797 user = insert(:user, %{info: %User.Info{locked: true}})
1798 other_user = insert(:user)
1800 {:ok, _activity} = ActivityPub.follow(other_user, user)
1802 user = User.get_cached_by_id(user.id)
1803 other_user = User.get_cached_by_id(other_user.id)
1805 assert User.following?(other_user, user) == false
1809 |> assign(:user, user)
1810 |> get("/api/v1/follow_requests")
1812 assert [relationship] = json_response(conn, 200)
1813 assert to_string(other_user.id) == relationship["id"]
1816 test "/api/v1/follow_requests/:id/authorize works" do
1817 user = insert(:user, %{info: %User.Info{locked: true}})
1818 other_user = insert(:user)
1820 {:ok, _activity} = ActivityPub.follow(other_user, user)
1822 user = User.get_cached_by_id(user.id)
1823 other_user = User.get_cached_by_id(other_user.id)
1825 assert User.following?(other_user, user) == false
1829 |> assign(:user, user)
1830 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1832 assert relationship = json_response(conn, 200)
1833 assert to_string(other_user.id) == relationship["id"]
1835 user = User.get_cached_by_id(user.id)
1836 other_user = User.get_cached_by_id(other_user.id)
1838 assert User.following?(other_user, user) == true
1841 test "verify_credentials", %{conn: conn} do
1842 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1846 |> assign(:user, user)
1847 |> get("/api/v1/accounts/verify_credentials")
1849 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1850 assert id == to_string(user.id)
1853 test "/api/v1/follow_requests/:id/reject works" do
1854 user = insert(:user, %{info: %User.Info{locked: true}})
1855 other_user = insert(:user)
1857 {:ok, _activity} = ActivityPub.follow(other_user, user)
1859 user = User.get_cached_by_id(user.id)
1863 |> assign(:user, user)
1864 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1866 assert relationship = json_response(conn, 200)
1867 assert to_string(other_user.id) == relationship["id"]
1869 user = User.get_cached_by_id(user.id)
1870 other_user = User.get_cached_by_id(other_user.id)
1872 assert User.following?(other_user, user) == false
1876 describe "account fetching" do
1877 test "works by id" do
1878 user = insert(:user)
1882 |> get("/api/v1/accounts/#{user.id}")
1884 assert %{"id" => id} = json_response(conn, 200)
1885 assert id == to_string(user.id)
1889 |> get("/api/v1/accounts/-1")
1891 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1894 test "works by nickname" do
1895 user = insert(:user)
1899 |> get("/api/v1/accounts/#{user.nickname}")
1901 assert %{"id" => id} = json_response(conn, 200)
1902 assert id == user.id
1905 test "works by nickname for remote users" do
1906 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1907 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1908 user = insert(:user, nickname: "user@example.com", local: false)
1912 |> get("/api/v1/accounts/#{user.nickname}")
1914 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1915 assert %{"id" => id} = json_response(conn, 200)
1916 assert id == user.id
1919 test "respects limit_to_local_content == :all for remote user nicknames" do
1920 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1921 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1923 user = insert(:user, nickname: "user@example.com", local: false)
1927 |> get("/api/v1/accounts/#{user.nickname}")
1929 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1930 assert json_response(conn, 404)
1933 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1934 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1935 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1937 user = insert(:user, nickname: "user@example.com", local: false)
1938 reading_user = insert(:user)
1942 |> get("/api/v1/accounts/#{user.nickname}")
1944 assert json_response(conn, 404)
1948 |> assign(:user, reading_user)
1949 |> get("/api/v1/accounts/#{user.nickname}")
1951 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1952 assert %{"id" => id} = json_response(conn, 200)
1953 assert id == user.id
1957 test "mascot upload", %{conn: conn} do
1958 user = insert(:user)
1960 non_image_file = %Plug.Upload{
1961 content_type: "audio/mpeg",
1962 path: Path.absname("test/fixtures/sound.mp3"),
1963 filename: "sound.mp3"
1968 |> assign(:user, user)
1969 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1971 assert json_response(conn, 415)
1973 file = %Plug.Upload{
1974 content_type: "image/jpg",
1975 path: Path.absname("test/fixtures/image.jpg"),
1976 filename: "an_image.jpg"
1981 |> assign(:user, user)
1982 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1984 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1987 test "mascot retrieving", %{conn: conn} do
1988 user = insert(:user)
1989 # When user hasn't set a mascot, we should just get pleroma tan back
1992 |> assign(:user, user)
1993 |> get("/api/v1/pleroma/mascot")
1995 assert %{"url" => url} = json_response(conn, 200)
1996 assert url =~ "pleroma-fox-tan-smol"
1998 # When a user sets their mascot, we should get that back
1999 file = %Plug.Upload{
2000 content_type: "image/jpg",
2001 path: Path.absname("test/fixtures/image.jpg"),
2002 filename: "an_image.jpg"
2007 |> assign(:user, user)
2008 |> put("/api/v1/pleroma/mascot", %{"file" => file})
2010 assert json_response(conn, 200)
2012 user = User.get_cached_by_id(user.id)
2016 |> assign(:user, user)
2017 |> get("/api/v1/pleroma/mascot")
2019 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
2020 assert url =~ "an_image"
2023 test "hashtag timeline", %{conn: conn} do
2024 following = insert(:user)
2027 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
2029 {:ok, [_activity]} =
2030 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
2034 |> get("/api/v1/timelines/tag/2hu")
2036 assert [%{"id" => id}] = json_response(nconn, 200)
2038 assert id == to_string(activity.id)
2040 # works for different capitalization too
2043 |> get("/api/v1/timelines/tag/2HU")
2045 assert [%{"id" => id}] = json_response(nconn, 200)
2047 assert id == to_string(activity.id)
2051 test "multi-hashtag timeline", %{conn: conn} do
2052 user = insert(:user)
2054 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
2055 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
2056 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
2060 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
2062 [status_none, status_test1, status_test] = json_response(any_test, 200)
2064 assert to_string(activity_test.id) == status_test["id"]
2065 assert to_string(activity_test1.id) == status_test1["id"]
2066 assert to_string(activity_none.id) == status_none["id"]
2070 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
2072 assert [status_test1] == json_response(restricted_test, 200)
2074 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
2076 assert [status_none] == json_response(all_test, 200)
2079 test "getting followers", %{conn: conn} do
2080 user = insert(:user)
2081 other_user = insert(:user)
2082 {:ok, user} = User.follow(user, other_user)
2086 |> get("/api/v1/accounts/#{other_user.id}/followers")
2088 assert [%{"id" => id}] = json_response(conn, 200)
2089 assert id == to_string(user.id)
2092 test "getting followers, hide_followers", %{conn: conn} do
2093 user = insert(:user)
2094 other_user = insert(:user, %{info: %{hide_followers: true}})
2095 {:ok, _user} = User.follow(user, other_user)
2099 |> get("/api/v1/accounts/#{other_user.id}/followers")
2101 assert [] == json_response(conn, 200)
2104 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
2105 user = insert(:user)
2106 other_user = insert(:user, %{info: %{hide_followers: true}})
2107 {:ok, _user} = User.follow(user, other_user)
2111 |> assign(:user, other_user)
2112 |> get("/api/v1/accounts/#{other_user.id}/followers")
2114 refute [] == json_response(conn, 200)
2117 test "getting followers, pagination", %{conn: conn} do
2118 user = insert(:user)
2119 follower1 = insert(:user)
2120 follower2 = insert(:user)
2121 follower3 = insert(:user)
2122 {:ok, _} = User.follow(follower1, user)
2123 {:ok, _} = User.follow(follower2, user)
2124 {:ok, _} = User.follow(follower3, user)
2128 |> assign(:user, user)
2132 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
2134 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2135 assert id3 == follower3.id
2136 assert id2 == follower2.id
2140 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
2142 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2143 assert id2 == follower2.id
2144 assert id1 == follower1.id
2148 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
2150 assert [%{"id" => id2}] = json_response(res_conn, 200)
2151 assert id2 == follower2.id
2153 assert [link_header] = get_resp_header(res_conn, "link")
2154 assert link_header =~ ~r/min_id=#{follower2.id}/
2155 assert link_header =~ ~r/max_id=#{follower2.id}/
2158 test "getting following", %{conn: conn} do
2159 user = insert(:user)
2160 other_user = insert(:user)
2161 {:ok, user} = User.follow(user, other_user)
2165 |> get("/api/v1/accounts/#{user.id}/following")
2167 assert [%{"id" => id}] = json_response(conn, 200)
2168 assert id == to_string(other_user.id)
2171 test "getting following, hide_follows", %{conn: conn} do
2172 user = insert(:user, %{info: %{hide_follows: true}})
2173 other_user = insert(:user)
2174 {:ok, user} = User.follow(user, other_user)
2178 |> get("/api/v1/accounts/#{user.id}/following")
2180 assert [] == json_response(conn, 200)
2183 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2184 user = insert(:user, %{info: %{hide_follows: true}})
2185 other_user = insert(:user)
2186 {:ok, user} = User.follow(user, other_user)
2190 |> assign(:user, user)
2191 |> get("/api/v1/accounts/#{user.id}/following")
2193 refute [] == json_response(conn, 200)
2196 test "getting following, pagination", %{conn: conn} do
2197 user = insert(:user)
2198 following1 = insert(:user)
2199 following2 = insert(:user)
2200 following3 = insert(:user)
2201 {:ok, _} = User.follow(user, following1)
2202 {:ok, _} = User.follow(user, following2)
2203 {:ok, _} = User.follow(user, following3)
2207 |> assign(:user, user)
2211 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2213 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2214 assert id3 == following3.id
2215 assert id2 == following2.id
2219 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2221 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2222 assert id2 == following2.id
2223 assert id1 == following1.id
2227 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2229 assert [%{"id" => id2}] = json_response(res_conn, 200)
2230 assert id2 == following2.id
2232 assert [link_header] = get_resp_header(res_conn, "link")
2233 assert link_header =~ ~r/min_id=#{following2.id}/
2234 assert link_header =~ ~r/max_id=#{following2.id}/
2237 test "following / unfollowing a user", %{conn: conn} do
2238 user = insert(:user)
2239 other_user = insert(:user)
2243 |> assign(:user, user)
2244 |> post("/api/v1/accounts/#{other_user.id}/follow")
2246 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2248 user = User.get_cached_by_id(user.id)
2252 |> assign(:user, user)
2253 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2255 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2257 user = User.get_cached_by_id(user.id)
2261 |> assign(:user, user)
2262 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2264 assert %{"id" => id} = json_response(conn, 200)
2265 assert id == to_string(other_user.id)
2268 test "following without reblogs" do
2269 follower = insert(:user)
2270 followed = insert(:user)
2271 other_user = insert(:user)
2275 |> assign(:user, follower)
2276 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2278 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2280 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2281 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2285 |> assign(:user, User.get_cached_by_id(follower.id))
2286 |> get("/api/v1/timelines/home")
2288 assert [] == json_response(conn, 200)
2292 |> assign(:user, follower)
2293 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2295 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2299 |> assign(:user, User.get_cached_by_id(follower.id))
2300 |> get("/api/v1/timelines/home")
2302 expected_activity_id = reblog.id
2303 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2306 test "following / unfollowing errors" do
2307 user = insert(:user)
2311 |> assign(:user, user)
2314 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2315 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2318 user = User.get_cached_by_id(user.id)
2319 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2320 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2322 # self follow via uri
2323 user = User.get_cached_by_id(user.id)
2324 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2325 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2327 # follow non existing user
2328 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2329 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2331 # follow non existing user via uri
2332 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2333 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2335 # unfollow non existing user
2336 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2337 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2340 describe "mute/unmute" do
2341 test "with notifications", %{conn: conn} do
2342 user = insert(:user)
2343 other_user = insert(:user)
2347 |> assign(:user, user)
2348 |> post("/api/v1/accounts/#{other_user.id}/mute")
2350 response = json_response(conn, 200)
2352 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2353 user = User.get_cached_by_id(user.id)
2357 |> assign(:user, user)
2358 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2360 response = json_response(conn, 200)
2361 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2364 test "without notifications", %{conn: conn} do
2365 user = insert(:user)
2366 other_user = insert(:user)
2370 |> assign(:user, user)
2371 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2373 response = json_response(conn, 200)
2375 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2376 user = User.get_cached_by_id(user.id)
2380 |> assign(:user, user)
2381 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2383 response = json_response(conn, 200)
2384 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2388 test "subscribing / unsubscribing to a user", %{conn: conn} do
2389 user = insert(:user)
2390 subscription_target = insert(:user)
2394 |> assign(:user, user)
2395 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2397 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2401 |> assign(:user, user)
2402 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2404 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2407 test "getting a list of mutes", %{conn: conn} do
2408 user = insert(:user)
2409 other_user = insert(:user)
2411 {:ok, user} = User.mute(user, other_user)
2415 |> assign(:user, user)
2416 |> get("/api/v1/mutes")
2418 other_user_id = to_string(other_user.id)
2419 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2422 test "blocking / unblocking a user", %{conn: conn} do
2423 user = insert(:user)
2424 other_user = insert(:user)
2428 |> assign(:user, user)
2429 |> post("/api/v1/accounts/#{other_user.id}/block")
2431 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2433 user = User.get_cached_by_id(user.id)
2437 |> assign(:user, user)
2438 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2440 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2443 test "getting a list of blocks", %{conn: conn} do
2444 user = insert(:user)
2445 other_user = insert(:user)
2447 {:ok, user} = User.block(user, other_user)
2451 |> assign(:user, user)
2452 |> get("/api/v1/blocks")
2454 other_user_id = to_string(other_user.id)
2455 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2458 test "blocking / unblocking a domain", %{conn: conn} do
2459 user = insert(:user)
2460 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2464 |> assign(:user, user)
2465 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2467 assert %{} = json_response(conn, 200)
2468 user = User.get_cached_by_ap_id(user.ap_id)
2469 assert User.blocks?(user, other_user)
2473 |> assign(:user, user)
2474 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2476 assert %{} = json_response(conn, 200)
2477 user = User.get_cached_by_ap_id(user.ap_id)
2478 refute User.blocks?(user, other_user)
2481 test "getting a list of domain blocks", %{conn: conn} do
2482 user = insert(:user)
2484 {:ok, user} = User.block_domain(user, "bad.site")
2485 {:ok, user} = User.block_domain(user, "even.worse.site")
2489 |> assign(:user, user)
2490 |> get("/api/v1/domain_blocks")
2492 domain_blocks = json_response(conn, 200)
2494 assert "bad.site" in domain_blocks
2495 assert "even.worse.site" in domain_blocks
2498 test "unimplemented follow_requests, blocks, domain blocks" do
2499 user = insert(:user)
2501 ["blocks", "domain_blocks", "follow_requests"]
2502 |> Enum.each(fn endpoint ->
2505 |> assign(:user, user)
2506 |> get("/api/v1/#{endpoint}")
2508 assert [] = json_response(conn, 200)
2512 test "returns the favorites of a user", %{conn: conn} do
2513 user = insert(:user)
2514 other_user = insert(:user)
2516 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2517 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2519 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2523 |> assign(:user, user)
2524 |> get("/api/v1/favourites")
2526 assert [status] = json_response(first_conn, 200)
2527 assert status["id"] == to_string(activity.id)
2529 assert [{"link", _link_header}] =
2530 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2532 # Honours query params
2533 {:ok, second_activity} =
2534 CommonAPI.post(other_user, %{
2536 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2539 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2541 last_like = status["id"]
2545 |> assign(:user, user)
2546 |> get("/api/v1/favourites?since_id=#{last_like}")
2548 assert [second_status] = json_response(second_conn, 200)
2549 assert second_status["id"] == to_string(second_activity.id)
2553 |> assign(:user, user)
2554 |> get("/api/v1/favourites?limit=0")
2556 assert [] = json_response(third_conn, 200)
2559 describe "getting favorites timeline of specified user" do
2561 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2562 [current_user: current_user, user: user]
2565 test "returns list of statuses favorited by specified user", %{
2567 current_user: current_user,
2570 [activity | _] = insert_pair(:note_activity)
2571 CommonAPI.favorite(activity.id, user)
2575 |> assign(:user, current_user)
2576 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2577 |> json_response(:ok)
2581 assert length(response) == 1
2582 assert like["id"] == activity.id
2585 test "returns favorites for specified user_id when user is not logged in", %{
2589 activity = insert(:note_activity)
2590 CommonAPI.favorite(activity.id, user)
2594 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2595 |> json_response(:ok)
2597 assert length(response) == 1
2600 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2602 current_user: current_user,
2606 CommonAPI.post(current_user, %{
2607 "status" => "Hi @#{user.nickname}!",
2608 "visibility" => "direct"
2611 CommonAPI.favorite(direct.id, user)
2615 |> assign(:user, current_user)
2616 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2617 |> json_response(:ok)
2619 assert length(response) == 1
2621 anonymous_response =
2623 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2624 |> json_response(:ok)
2626 assert Enum.empty?(anonymous_response)
2629 test "does not return others' favorited DM when user is not one of recipients", %{
2631 current_user: current_user,
2634 user_two = insert(:user)
2637 CommonAPI.post(user_two, %{
2638 "status" => "Hi @#{user.nickname}!",
2639 "visibility" => "direct"
2642 CommonAPI.favorite(direct.id, user)
2646 |> assign(:user, current_user)
2647 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2648 |> json_response(:ok)
2650 assert Enum.empty?(response)
2653 test "paginates favorites using since_id and max_id", %{
2655 current_user: current_user,
2658 activities = insert_list(10, :note_activity)
2660 Enum.each(activities, fn activity ->
2661 CommonAPI.favorite(activity.id, user)
2664 third_activity = Enum.at(activities, 2)
2665 seventh_activity = Enum.at(activities, 6)
2669 |> assign(:user, current_user)
2670 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2671 since_id: third_activity.id,
2672 max_id: seventh_activity.id
2674 |> json_response(:ok)
2676 assert length(response) == 3
2677 refute third_activity in response
2678 refute seventh_activity in response
2681 test "limits favorites using limit parameter", %{
2683 current_user: current_user,
2687 |> insert_list(:note_activity)
2688 |> Enum.each(fn activity ->
2689 CommonAPI.favorite(activity.id, user)
2694 |> assign(:user, current_user)
2695 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2696 |> json_response(:ok)
2698 assert length(response) == 3
2701 test "returns empty response when user does not have any favorited statuses", %{
2703 current_user: current_user,
2708 |> assign(:user, current_user)
2709 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2710 |> json_response(:ok)
2712 assert Enum.empty?(response)
2715 test "returns 404 error when specified user is not exist", %{conn: conn} do
2716 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2718 assert json_response(conn, 404) == %{"error" => "Record not found"}
2721 test "returns 403 error when user has hidden own favorites", %{
2723 current_user: current_user
2725 user = insert(:user, %{info: %{hide_favorites: true}})
2726 activity = insert(:note_activity)
2727 CommonAPI.favorite(activity.id, user)
2731 |> assign(:user, current_user)
2732 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2734 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2737 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2738 user = insert(:user)
2739 activity = insert(:note_activity)
2740 CommonAPI.favorite(activity.id, user)
2744 |> assign(:user, current_user)
2745 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2747 assert user.info.hide_favorites
2748 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2752 test "get instance information", %{conn: conn} do
2753 conn = get(conn, "/api/v1/instance")
2754 assert result = json_response(conn, 200)
2756 email = Config.get([:instance, :email])
2757 # Note: not checking for "max_toot_chars" since it's optional
2763 "email" => from_config_email,
2765 "streaming_api" => _
2770 "registrations" => _,
2774 assert email == from_config_email
2777 test "get instance stats", %{conn: conn} do
2778 user = insert(:user, %{local: true})
2780 user2 = insert(:user, %{local: true})
2781 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2783 insert(:user, %{local: false, nickname: "u@peer1.com"})
2784 insert(:user, %{local: false, nickname: "u@peer2.com"})
2786 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2788 # Stats should count users with missing or nil `info.deactivated` value
2789 user = User.get_cached_by_id(user.id)
2790 info_change = Changeset.change(user.info, %{deactivated: nil})
2794 |> Changeset.change()
2795 |> Changeset.put_embed(:info, info_change)
2796 |> User.update_and_set_cache()
2798 Pleroma.Stats.force_update()
2800 conn = get(conn, "/api/v1/instance")
2802 assert result = json_response(conn, 200)
2804 stats = result["stats"]
2807 assert stats["user_count"] == 1
2808 assert stats["status_count"] == 1
2809 assert stats["domain_count"] == 2
2812 test "get peers", %{conn: conn} do
2813 insert(:user, %{local: false, nickname: "u@peer1.com"})
2814 insert(:user, %{local: false, nickname: "u@peer2.com"})
2816 Pleroma.Stats.force_update()
2818 conn = get(conn, "/api/v1/instance/peers")
2820 assert result = json_response(conn, 200)
2822 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2825 test "put settings", %{conn: conn} do
2826 user = insert(:user)
2830 |> assign(:user, user)
2831 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2833 assert _result = json_response(conn, 200)
2835 user = User.get_cached_by_ap_id(user.ap_id)
2836 assert user.info.settings == %{"programming" => "socks"}
2839 describe "pinned statuses" do
2841 user = insert(:user)
2842 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2844 [user: user, activity: activity]
2847 clear_config([:instance, :max_pinned_statuses]) do
2848 Config.put([:instance, :max_pinned_statuses], 1)
2851 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2852 {:ok, _} = CommonAPI.pin(activity.id, user)
2856 |> assign(:user, user)
2857 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2858 |> json_response(200)
2860 id_str = to_string(activity.id)
2862 assert [%{"id" => ^id_str, "pinned" => true}] = result
2865 test "pin status", %{conn: conn, user: user, activity: activity} do
2866 id_str = to_string(activity.id)
2868 assert %{"id" => ^id_str, "pinned" => true} =
2870 |> assign(:user, user)
2871 |> post("/api/v1/statuses/#{activity.id}/pin")
2872 |> json_response(200)
2874 assert [%{"id" => ^id_str, "pinned" => true}] =
2876 |> assign(:user, user)
2877 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2878 |> json_response(200)
2881 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2882 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2886 |> assign(:user, user)
2887 |> post("/api/v1/statuses/#{dm.id}/pin")
2889 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2892 test "unpin status", %{conn: conn, user: user, activity: activity} do
2893 {:ok, _} = CommonAPI.pin(activity.id, user)
2895 id_str = to_string(activity.id)
2896 user = refresh_record(user)
2898 assert %{"id" => ^id_str, "pinned" => false} =
2900 |> assign(:user, user)
2901 |> post("/api/v1/statuses/#{activity.id}/unpin")
2902 |> json_response(200)
2906 |> assign(:user, user)
2907 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2908 |> json_response(200)
2911 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2914 |> assign(:user, user)
2915 |> post("/api/v1/statuses/1/unpin")
2917 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2920 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2921 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2923 id_str_one = to_string(activity_one.id)
2925 assert %{"id" => ^id_str_one, "pinned" => true} =
2927 |> assign(:user, user)
2928 |> post("/api/v1/statuses/#{id_str_one}/pin")
2929 |> json_response(200)
2931 user = refresh_record(user)
2933 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2935 |> assign(:user, user)
2936 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2937 |> json_response(400)
2943 Config.put([:rich_media, :enabled], true)
2945 user = insert(:user)
2949 test "returns rich-media card", %{conn: conn, user: user} do
2950 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2953 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2954 "provider_name" => "example.com",
2955 "provider_url" => "https://example.com",
2956 "title" => "The Rock",
2958 "url" => "https://example.com/ogp",
2960 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2963 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2964 "title" => "The Rock",
2965 "type" => "video.movie",
2966 "url" => "https://example.com/ogp",
2968 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2975 |> get("/api/v1/statuses/#{activity.id}/card")
2976 |> json_response(200)
2978 assert response == card_data
2980 # works with private posts
2982 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2986 |> assign(:user, user)
2987 |> get("/api/v1/statuses/#{activity.id}/card")
2988 |> json_response(200)
2990 assert response_two == card_data
2993 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2995 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2999 |> get("/api/v1/statuses/#{activity.id}/card")
3000 |> json_response(:ok)
3002 assert response == %{
3004 "title" => "Pleroma",
3005 "description" => "",
3007 "provider_name" => "example.com",
3008 "provider_url" => "https://example.com",
3009 "url" => "https://example.com/ogp-missing-data",
3012 "title" => "Pleroma",
3013 "type" => "website",
3014 "url" => "https://example.com/ogp-missing-data"
3022 user = insert(:user)
3023 for_user = insert(:user)
3026 CommonAPI.post(user, %{
3027 "status" => "heweoo?"
3031 CommonAPI.post(user, %{
3032 "status" => "heweoo!"
3037 |> assign(:user, for_user)
3038 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
3040 assert json_response(response1, 200)["bookmarked"] == true
3044 |> assign(:user, for_user)
3045 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
3047 assert json_response(response2, 200)["bookmarked"] == true
3051 |> assign(:user, for_user)
3052 |> get("/api/v1/bookmarks")
3054 assert [json_response(response2, 200), json_response(response1, 200)] ==
3055 json_response(bookmarks, 200)
3059 |> assign(:user, for_user)
3060 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
3062 assert json_response(response1, 200)["bookmarked"] == false
3066 |> assign(:user, for_user)
3067 |> get("/api/v1/bookmarks")
3069 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
3072 describe "conversation muting" do
3074 post_user = insert(:user)
3075 user = insert(:user)
3077 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
3079 [user: user, activity: activity]
3082 test "mute conversation", %{conn: conn, user: user, activity: activity} do
3083 id_str = to_string(activity.id)
3085 assert %{"id" => ^id_str, "muted" => true} =
3087 |> assign(:user, user)
3088 |> post("/api/v1/statuses/#{activity.id}/mute")
3089 |> json_response(200)
3092 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
3093 {:ok, _} = CommonAPI.add_mute(user, activity)
3097 |> assign(:user, user)
3098 |> post("/api/v1/statuses/#{activity.id}/mute")
3100 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
3103 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
3104 {:ok, _} = CommonAPI.add_mute(user, activity)
3106 id_str = to_string(activity.id)
3107 user = refresh_record(user)
3109 assert %{"id" => ^id_str, "muted" => false} =
3111 |> assign(:user, user)
3112 |> post("/api/v1/statuses/#{activity.id}/unmute")
3113 |> json_response(200)
3117 describe "reports" do
3119 reporter = insert(:user)
3120 target_user = insert(:user)
3122 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
3124 [reporter: reporter, target_user: target_user, activity: activity]
3127 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
3128 assert %{"action_taken" => false, "id" => _} =
3130 |> assign(:user, reporter)
3131 |> post("/api/v1/reports", %{"account_id" => target_user.id})
3132 |> json_response(200)
3135 test "submit a report with statuses and comment", %{
3138 target_user: target_user,
3141 assert %{"action_taken" => false, "id" => _} =
3143 |> assign(:user, reporter)
3144 |> post("/api/v1/reports", %{
3145 "account_id" => target_user.id,
3146 "status_ids" => [activity.id],
3147 "comment" => "bad status!",
3148 "forward" => "false"
3150 |> json_response(200)
3153 test "account_id is required", %{
3158 assert %{"error" => "Valid `account_id` required"} =
3160 |> assign(:user, reporter)
3161 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3162 |> json_response(400)
3165 test "comment must be up to the size specified in the config", %{
3168 target_user: target_user
3170 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3171 comment = String.pad_trailing("a", max_size + 1, "a")
3173 error = %{"error" => "Comment must be up to #{max_size} characters"}
3177 |> assign(:user, reporter)
3178 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3179 |> json_response(400)
3182 test "returns error when account is not exist", %{
3189 |> assign(:user, reporter)
3190 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3192 assert json_response(conn, 400) == %{"error" => "Account not found"}
3196 describe "link headers" do
3197 test "preserves parameters in link headers", %{conn: conn} do
3198 user = insert(:user)
3199 other_user = insert(:user)
3202 CommonAPI.post(other_user, %{
3203 "status" => "hi @#{user.nickname}",
3204 "visibility" => "public"
3208 CommonAPI.post(other_user, %{
3209 "status" => "hi @#{user.nickname}",
3210 "visibility" => "public"
3213 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3214 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3218 |> assign(:user, user)
3219 |> get("/api/v1/notifications", %{media_only: true})
3221 assert [link_header] = get_resp_header(conn, "link")
3222 assert link_header =~ ~r/media_only=true/
3223 assert link_header =~ ~r/min_id=#{notification2.id}/
3224 assert link_header =~ ~r/max_id=#{notification1.id}/
3228 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3229 # Need to set an old-style integer ID to reproduce the problem
3230 # (these are no longer assigned to new accounts but were preserved
3231 # for existing accounts during the migration to flakeIDs)
3232 user_one = insert(:user, %{id: 1212})
3233 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3237 |> get("/api/v1/accounts/#{user_one.id}")
3241 |> get("/api/v1/accounts/#{user_two.nickname}")
3245 |> get("/api/v1/accounts/#{user_two.id}")
3247 acc_one = json_response(resp_one, 200)
3248 acc_two = json_response(resp_two, 200)
3249 acc_three = json_response(resp_three, 200)
3250 refute acc_one == acc_two
3251 assert acc_two == acc_three
3254 describe "custom emoji" do
3255 test "with tags", %{conn: conn} do
3258 |> get("/api/v1/custom_emojis")
3259 |> json_response(200)
3261 assert Map.has_key?(emoji, "shortcode")
3262 assert Map.has_key?(emoji, "static_url")
3263 assert Map.has_key?(emoji, "tags")
3264 assert is_list(emoji["tags"])
3265 assert Map.has_key?(emoji, "category")
3266 assert Map.has_key?(emoji, "url")
3267 assert Map.has_key?(emoji, "visible_in_picker")
3271 describe "index/2 redirections" do
3272 setup %{conn: conn} do
3276 signing_salt: "cooldude"
3281 |> Plug.Session.call(Plug.Session.init(session_opts))
3284 test_path = "/web/statuses/test"
3285 %{conn: conn, path: test_path}
3288 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3289 conn = get(conn, path)
3291 assert conn.status == 302
3292 assert redirected_to(conn) == "/web/login"
3295 test "redirects not logged-in users to the login page on private instances", %{
3299 Config.put([:instance, :public], false)
3301 conn = get(conn, path)
3303 assert conn.status == 302
3304 assert redirected_to(conn) == "/web/login"
3307 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3308 token = insert(:oauth_token)
3312 |> assign(:user, token.user)
3313 |> put_session(:oauth_token, token.token)
3316 assert conn.status == 200
3319 test "saves referer path to session", %{conn: conn, path: path} do
3320 conn = get(conn, path)
3321 return_to = Plug.Conn.get_session(conn, :return_to)
3323 assert return_to == path
3326 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3327 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3328 auth = insert(:oauth_authorization, app: app)
3332 |> put_session(:return_to, path)
3333 |> get("/web/login", %{code: auth.token})
3335 assert conn.status == 302
3336 assert redirected_to(conn) == path
3339 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3340 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3341 auth = insert(:oauth_authorization, app: app)
3343 conn = get(conn, "/web/login", %{code: auth.token})
3345 assert conn.status == 302
3346 assert redirected_to(conn) == "/web/getting-started"
3350 describe "scheduled activities" do
3351 test "creates a scheduled activity", %{conn: conn} do
3352 user = insert(:user)
3353 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3357 |> assign(:user, user)
3358 |> post("/api/v1/statuses", %{
3359 "status" => "scheduled",
3360 "scheduled_at" => scheduled_at
3363 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3364 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3365 assert [] == Repo.all(Activity)
3368 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3369 user = insert(:user)
3370 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3372 file = %Plug.Upload{
3373 content_type: "image/jpg",
3374 path: Path.absname("test/fixtures/image.jpg"),
3375 filename: "an_image.jpg"
3378 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3382 |> assign(:user, user)
3383 |> post("/api/v1/statuses", %{
3384 "media_ids" => [to_string(upload.id)],
3385 "status" => "scheduled",
3386 "scheduled_at" => scheduled_at
3389 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3390 assert %{"type" => "image"} = media_attachment
3393 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3395 user = insert(:user)
3398 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3402 |> assign(:user, user)
3403 |> post("/api/v1/statuses", %{
3404 "status" => "not scheduled",
3405 "scheduled_at" => scheduled_at
3408 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3409 assert [] == Repo.all(ScheduledActivity)
3412 test "returns error when daily user limit is exceeded", %{conn: conn} do
3413 user = insert(:user)
3416 NaiveDateTime.utc_now()
3417 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3418 |> NaiveDateTime.to_iso8601()
3420 attrs = %{params: %{}, scheduled_at: today}
3421 {:ok, _} = ScheduledActivity.create(user, attrs)
3422 {:ok, _} = ScheduledActivity.create(user, attrs)
3426 |> assign(:user, user)
3427 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3429 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3432 test "returns error when total user limit is exceeded", %{conn: conn} do
3433 user = insert(:user)
3436 NaiveDateTime.utc_now()
3437 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3438 |> NaiveDateTime.to_iso8601()
3441 NaiveDateTime.utc_now()
3442 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3443 |> NaiveDateTime.to_iso8601()
3445 attrs = %{params: %{}, scheduled_at: today}
3446 {:ok, _} = ScheduledActivity.create(user, attrs)
3447 {:ok, _} = ScheduledActivity.create(user, attrs)
3448 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3452 |> assign(:user, user)
3453 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3455 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3458 test "shows scheduled activities", %{conn: conn} do
3459 user = insert(:user)
3460 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3461 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3462 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3463 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3467 |> assign(:user, user)
3472 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3474 result = json_response(conn_res, 200)
3475 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3480 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3482 result = json_response(conn_res, 200)
3483 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3488 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3490 result = json_response(conn_res, 200)
3491 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3494 test "shows a scheduled activity", %{conn: conn} do
3495 user = insert(:user)
3496 scheduled_activity = insert(:scheduled_activity, user: user)
3500 |> assign(:user, user)
3501 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3503 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3504 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3508 |> assign(:user, user)
3509 |> get("/api/v1/scheduled_statuses/404")
3511 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3514 test "updates a scheduled activity", %{conn: conn} do
3515 user = insert(:user)
3516 scheduled_activity = insert(:scheduled_activity, user: user)
3519 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3523 |> assign(:user, user)
3524 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3525 scheduled_at: new_scheduled_at
3528 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3529 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3533 |> assign(:user, user)
3534 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3536 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3539 test "deletes a scheduled activity", %{conn: conn} do
3540 user = insert(:user)
3541 scheduled_activity = insert(:scheduled_activity, user: user)
3545 |> assign(:user, user)
3546 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3548 assert %{} = json_response(res_conn, 200)
3549 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3553 |> assign(:user, user)
3554 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3556 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3560 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3561 user1 = insert(:user)
3562 user2 = insert(:user)
3563 user3 = insert(:user)
3565 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3567 # Reply to status from another user
3570 |> assign(:user, user2)
3571 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3573 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3575 activity = Activity.get_by_id_with_object(id)
3577 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3578 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3580 # Reblog from the third user
3583 |> assign(:user, user3)
3584 |> post("/api/v1/statuses/#{activity.id}/reblog")
3586 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3587 json_response(conn2, 200)
3589 assert to_string(activity.id) == id
3591 # Getting third user status
3594 |> assign(:user, user3)
3595 |> get("api/v1/timelines/home")
3597 [reblogged_activity] = json_response(conn3, 200)
3599 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3601 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3602 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3605 describe "create account by app" do
3606 test "Account registration via Application", %{conn: conn} do
3609 |> post("/api/v1/apps", %{
3610 client_name: "client_name",
3611 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3612 scopes: "read, write, follow"
3616 "client_id" => client_id,
3617 "client_secret" => client_secret,
3619 "name" => "client_name",
3620 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3623 } = json_response(conn, 200)
3627 |> post("/oauth/token", %{
3628 grant_type: "client_credentials",
3629 client_id: client_id,
3630 client_secret: client_secret
3633 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3634 json_response(conn, 200)
3637 token_from_db = Repo.get_by(Token, token: token)
3638 assert token_from_db
3640 assert scope == "read write follow"
3644 |> put_req_header("authorization", "Bearer " <> token)
3645 |> post("/api/v1/accounts", %{
3647 email: "lain@example.org",
3648 password: "PlzDontHackLain",
3653 "access_token" => token,
3654 "created_at" => _created_at,
3656 "token_type" => "Bearer"
3657 } = json_response(conn, 200)
3659 token_from_db = Repo.get_by(Token, token: token)
3660 assert token_from_db
3661 token_from_db = Repo.preload(token_from_db, :user)
3662 assert token_from_db.user
3664 assert token_from_db.user.info.confirmation_pending
3667 test "rate limit", %{conn: conn} do
3668 app_token = insert(:oauth_token, user: nil)
3671 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3672 |> Map.put(:remote_ip, {15, 15, 15, 15})
3677 |> post("/api/v1/accounts", %{
3678 username: "#{i}lain",
3679 email: "#{i}lain@example.org",
3680 password: "PlzDontHackLain",
3685 "access_token" => token,
3686 "created_at" => _created_at,
3688 "token_type" => "Bearer"
3689 } = json_response(conn, 200)
3691 token_from_db = Repo.get_by(Token, token: token)
3692 assert token_from_db
3693 token_from_db = Repo.preload(token_from_db, :user)
3694 assert token_from_db.user
3696 assert token_from_db.user.info.confirmation_pending
3701 |> post("/api/v1/accounts", %{
3703 email: "6lain@example.org",
3704 password: "PlzDontHackLain",
3708 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3712 describe "GET /api/v1/polls/:id" do
3713 test "returns poll entity for object id", %{conn: conn} do
3714 user = insert(:user)
3717 CommonAPI.post(user, %{
3718 "status" => "Pleroma does",
3719 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3722 object = Object.normalize(activity)
3726 |> assign(:user, user)
3727 |> get("/api/v1/polls/#{object.id}")
3729 response = json_response(conn, 200)
3730 id = to_string(object.id)
3731 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3734 test "does not expose polls for private statuses", %{conn: conn} do
3735 user = insert(:user)
3736 other_user = insert(:user)
3739 CommonAPI.post(user, %{
3740 "status" => "Pleroma does",
3741 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3742 "visibility" => "private"
3745 object = Object.normalize(activity)
3749 |> assign(:user, other_user)
3750 |> get("/api/v1/polls/#{object.id}")
3752 assert json_response(conn, 404)
3756 describe "POST /api/v1/polls/:id/votes" do
3757 test "votes are added to the poll", %{conn: conn} do
3758 user = insert(:user)
3759 other_user = insert(:user)
3762 CommonAPI.post(user, %{
3763 "status" => "A very delicious sandwich",
3765 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3771 object = Object.normalize(activity)
3775 |> assign(:user, other_user)
3776 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3778 assert json_response(conn, 200)
3779 object = Object.get_by_id(object.id)
3781 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3786 test "author can't vote", %{conn: conn} do
3787 user = insert(:user)
3790 CommonAPI.post(user, %{
3791 "status" => "Am I cute?",
3792 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3795 object = Object.normalize(activity)
3798 |> assign(:user, user)
3799 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3800 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3802 object = Object.get_by_id(object.id)
3804 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3807 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3808 user = insert(:user)
3809 other_user = insert(:user)
3812 CommonAPI.post(user, %{
3813 "status" => "The glass is",
3814 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3817 object = Object.normalize(activity)
3820 |> assign(:user, other_user)
3821 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3822 |> json_response(422) == %{"error" => "Too many choices"}
3824 object = Object.get_by_id(object.id)
3826 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3831 test "does not allow choice index to be greater than options count", %{conn: conn} do
3832 user = insert(:user)
3833 other_user = insert(:user)
3836 CommonAPI.post(user, %{
3837 "status" => "Am I cute?",
3838 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3841 object = Object.normalize(activity)
3845 |> assign(:user, other_user)
3846 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3848 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3851 test "returns 404 error when object is not exist", %{conn: conn} do
3852 user = insert(:user)
3856 |> assign(:user, user)
3857 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3859 assert json_response(conn, 404) == %{"error" => "Record not found"}
3862 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3863 user = insert(:user)
3864 other_user = insert(:user)
3867 CommonAPI.post(user, %{
3868 "status" => "Am I cute?",
3869 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3870 "visibility" => "private"
3873 object = Object.normalize(activity)
3877 |> assign(:user, other_user)
3878 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3880 assert json_response(conn, 404) == %{"error" => "Record not found"}
3884 describe "GET /api/v1/statuses/:id/favourited_by" do
3886 user = insert(:user)
3887 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3891 |> assign(:user, user)
3893 [conn: conn, activity: activity, user: user]
3896 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3897 other_user = insert(:user)
3898 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3902 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3903 |> json_response(:ok)
3905 [%{"id" => id}] = response
3907 assert id == other_user.id
3910 test "returns empty array when status has not been favorited yet", %{
3916 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3917 |> json_response(:ok)
3919 assert Enum.empty?(response)
3922 test "does not return users who have favorited the status but are blocked", %{
3923 conn: %{assigns: %{user: user}} = conn,
3926 other_user = insert(:user)
3927 {:ok, user} = User.block(user, other_user)
3929 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3933 |> assign(:user, user)
3934 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3935 |> json_response(:ok)
3937 assert Enum.empty?(response)
3940 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3941 other_user = insert(:user)
3942 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3946 |> assign(:user, nil)
3947 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3948 |> json_response(:ok)
3950 [%{"id" => id}] = response
3951 assert id == other_user.id
3954 test "requires authentification for private posts", %{conn: conn, user: user} do
3955 other_user = insert(:user)
3958 CommonAPI.post(user, %{
3959 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3960 "visibility" => "direct"
3963 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3966 |> assign(:user, nil)
3967 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3968 |> json_response(404)
3972 |> assign(:user, other_user)
3973 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3974 |> json_response(200)
3976 [%{"id" => id}] = response
3977 assert id == other_user.id
3981 describe "GET /api/v1/statuses/:id/reblogged_by" do
3983 user = insert(:user)
3984 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3988 |> assign(:user, user)
3990 [conn: conn, activity: activity, user: user]
3993 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3994 other_user = insert(:user)
3995 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3999 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4000 |> json_response(:ok)
4002 [%{"id" => id}] = response
4004 assert id == other_user.id
4007 test "returns empty array when status has not been reblogged yet", %{
4013 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4014 |> json_response(:ok)
4016 assert Enum.empty?(response)
4019 test "does not return users who have reblogged the status but are blocked", %{
4020 conn: %{assigns: %{user: user}} = conn,
4023 other_user = insert(:user)
4024 {:ok, user} = User.block(user, other_user)
4026 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
4030 |> assign(:user, user)
4031 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4032 |> json_response(:ok)
4034 assert Enum.empty?(response)
4037 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
4038 other_user = insert(:user)
4039 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
4043 |> assign(:user, nil)
4044 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4045 |> json_response(:ok)
4047 [%{"id" => id}] = response
4048 assert id == other_user.id
4051 test "requires authentification for private posts", %{conn: conn, user: user} do
4052 other_user = insert(:user)
4055 CommonAPI.post(user, %{
4056 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
4057 "visibility" => "direct"
4061 |> assign(:user, nil)
4062 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4063 |> json_response(404)
4067 |> assign(:user, other_user)
4068 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4069 |> json_response(200)
4071 assert [] == response
4075 describe "POST /auth/password, with valid parameters" do
4076 setup %{conn: conn} do
4077 user = insert(:user)
4078 conn = post(conn, "/auth/password?email=#{user.email}")
4079 %{conn: conn, user: user}
4082 test "it returns 204", %{conn: conn} do
4083 assert json_response(conn, :no_content)
4086 test "it creates a PasswordResetToken record for user", %{user: user} do
4087 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
4091 test "it sends an email to user", %{user: user} do
4092 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
4094 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
4095 notify_email = Config.get([:instance, :notify_email])
4096 instance_name = Config.get([:instance, :name])
4099 from: {instance_name, notify_email},
4100 to: {user.name, user.email},
4101 html_body: email.html_body
4106 describe "POST /auth/password, with invalid parameters" do
4108 user = insert(:user)
4112 test "it returns 404 when user is not found", %{conn: conn, user: user} do
4113 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
4114 assert conn.status == 404
4115 assert conn.resp_body == ""
4118 test "it returns 400 when user is not local", %{conn: conn, user: user} do
4119 {:ok, user} = Repo.update(Changeset.change(user, local: false))
4120 conn = post(conn, "/auth/password?email=#{user.email}")
4121 assert conn.status == 400
4122 assert conn.resp_body == ""
4126 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
4128 user = insert(:user)
4129 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
4133 |> Changeset.change()
4134 |> Changeset.put_embed(:info, info_change)
4137 assert user.info.confirmation_pending
4142 clear_config([:instance, :account_activation_required]) do
4143 Config.put([:instance, :account_activation_required], true)
4146 test "resend account confirmation email", %{conn: conn, user: user} do
4148 |> assign(:user, user)
4149 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
4150 |> json_response(:no_content)
4152 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
4153 notify_email = Config.get([:instance, :notify_email])
4154 instance_name = Config.get([:instance, :name])
4157 from: {instance_name, notify_email},
4158 to: {user.name, user.email},
4159 html_body: email.html_body
4164 describe "GET /api/v1/suggestions" do
4166 user = insert(:user)
4167 other_user = insert(:user)
4168 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
4169 url500 = "http://test500?#{host}&#{user.nickname}"
4170 url200 = "http://test200?#{host}&#{user.nickname}"
4173 %{method: :get, url: ^url500} ->
4174 %Tesla.Env{status: 500, body: "bad request"}
4176 %{method: :get, url: ^url200} ->
4180 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4182 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4186 [user: user, other_user: other_user]
4189 clear_config(:suggestions)
4191 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4192 Config.put([:suggestions, :enabled], false)
4196 |> assign(:user, user)
4197 |> get("/api/v1/suggestions")
4198 |> json_response(200)
4203 test "returns error", %{conn: conn, user: user} do
4204 Config.put([:suggestions, :enabled], true)
4205 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4207 assert capture_log(fn ->
4210 |> assign(:user, user)
4211 |> get("/api/v1/suggestions")
4212 |> json_response(500)
4214 assert res == "Something went wrong"
4215 end) =~ "Could not retrieve suggestions"
4218 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4219 Config.put([:suggestions, :enabled], true)
4220 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4224 |> assign(:user, user)
4225 |> get("/api/v1/suggestions")
4226 |> json_response(200)
4231 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4232 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4236 "acct" => other_user.ap_id,
4237 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4238 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4239 "id" => other_user.id