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
11 alias Pleroma.Notification
14 alias Pleroma.ScheduledActivity
15 alias Pleroma.Tests.ObanHelpers
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.MastodonAPI.FilterView
20 alias Pleroma.Web.OAuth.App
21 alias Pleroma.Web.OAuth.Token
22 alias Pleroma.Web.Push
24 import ExUnit.CaptureLog
25 import Pleroma.Factory
26 import Swoosh.TestAssertions
29 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
32 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
36 clear_config([:instance, :public])
37 clear_config([:rich_media, :enabled])
39 test "Conversations", %{conn: conn} do
40 user_one = insert(:user)
41 user_two = insert(:user)
42 user_three = insert(:user)
44 {:ok, user_two} = User.follow(user_two, user_one)
47 CommonAPI.post(user_one, %{
48 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
49 "visibility" => "direct"
52 {:ok, _follower_only} =
53 CommonAPI.post(user_one, %{
54 "status" => "Hi @#{user_two.nickname}!",
55 "visibility" => "private"
60 |> assign(:user, user_one)
61 |> get("/api/v1/conversations")
63 assert response = json_response(res_conn, 200)
68 "accounts" => res_accounts,
69 "last_status" => res_last_status,
74 account_ids = Enum.map(res_accounts, & &1["id"])
75 assert length(res_accounts) == 2
76 assert user_two.id in account_ids
77 assert user_three.id in account_ids
78 assert is_binary(res_id)
80 assert res_last_status["id"] == direct.id
82 # Apparently undocumented API endpoint
85 |> assign(:user, user_one)
86 |> post("/api/v1/conversations/#{res_id}/read")
88 assert response = json_response(res_conn, 200)
89 assert length(response["accounts"]) == 2
90 assert response["last_status"]["id"] == direct.id
91 assert response["unread"] == false
93 # (vanilla) Mastodon frontend behaviour
96 |> assign(:user, user_one)
97 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
99 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
102 test "verify_credentials", %{conn: conn} do
107 |> assign(:user, user)
108 |> get("/api/v1/accounts/verify_credentials")
110 response = json_response(conn, 200)
112 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
113 assert response["pleroma"]["chat_token"]
114 assert id == to_string(user.id)
117 test "verify_credentials default scope unlisted", %{conn: conn} do
118 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
122 |> assign(:user, user)
123 |> get("/api/v1/accounts/verify_credentials")
125 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
126 assert id == to_string(user.id)
129 test "apps/verify_credentials", %{conn: conn} do
130 token = insert(:oauth_token)
134 |> assign(:user, token.user)
135 |> assign(:token, token)
136 |> get("/api/v1/apps/verify_credentials")
138 app = Repo.preload(token, :app).app
141 "name" => app.client_name,
142 "website" => app.website,
143 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
146 assert expected == json_response(conn, 200)
149 test "user avatar can be set", %{conn: conn} do
151 avatar_image = File.read!("test/fixtures/avatar_data_uri")
155 |> assign(:user, user)
156 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
158 user = refresh_record(user)
172 assert %{"url" => _} = json_response(conn, 200)
175 test "user avatar can be reset", %{conn: conn} do
180 |> assign(:user, user)
181 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
183 user = User.get_cached_by_id(user.id)
185 assert user.avatar == nil
187 assert %{"url" => nil} = json_response(conn, 200)
190 test "can set profile banner", %{conn: conn} do
195 |> assign(:user, user)
196 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
198 user = refresh_record(user)
199 assert user.info.banner["type"] == "Image"
201 assert %{"url" => _} = json_response(conn, 200)
204 test "can reset profile banner", %{conn: conn} do
209 |> assign(:user, user)
210 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
212 user = refresh_record(user)
213 assert user.info.banner == %{}
215 assert %{"url" => nil} = json_response(conn, 200)
218 test "background image can be set", %{conn: conn} do
223 |> assign(:user, user)
224 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
226 user = refresh_record(user)
227 assert user.info.background["type"] == "Image"
228 assert %{"url" => _} = json_response(conn, 200)
231 test "background image can be reset", %{conn: conn} do
236 |> assign(:user, user)
237 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
239 user = refresh_record(user)
240 assert user.info.background == %{}
241 assert %{"url" => nil} = json_response(conn, 200)
244 test "creates an oauth app", %{conn: conn} do
246 app_attrs = build(:oauth_app)
250 |> assign(:user, user)
251 |> post("/api/v1/apps", %{
252 client_name: app_attrs.client_name,
253 redirect_uris: app_attrs.redirect_uris
256 [app] = Repo.all(App)
259 "name" => app.client_name,
260 "website" => app.website,
261 "client_id" => app.client_id,
262 "client_secret" => app.client_secret,
263 "id" => app.id |> to_string(),
264 "redirect_uri" => app.redirect_uris,
265 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
268 assert expected == json_response(conn, 200)
271 describe "filters" do
272 test "creating a filter", %{conn: conn} do
275 filter = %Pleroma.Filter{
282 |> assign(:user, user)
283 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
285 assert response = json_response(conn, 200)
286 assert response["phrase"] == filter.phrase
287 assert response["context"] == filter.context
288 assert response["irreversible"] == false
289 assert response["id"] != nil
290 assert response["id"] != ""
293 test "fetching a list of filters", %{conn: conn} do
296 query_one = %Pleroma.Filter{
303 query_two = %Pleroma.Filter{
310 {:ok, filter_one} = Pleroma.Filter.create(query_one)
311 {:ok, filter_two} = Pleroma.Filter.create(query_two)
315 |> assign(:user, user)
316 |> get("/api/v1/filters")
317 |> json_response(200)
323 filters: [filter_two, filter_one]
327 test "get a filter", %{conn: conn} do
330 query = %Pleroma.Filter{
337 {:ok, filter} = Pleroma.Filter.create(query)
341 |> assign(:user, user)
342 |> get("/api/v1/filters/#{filter.filter_id}")
344 assert _response = json_response(conn, 200)
347 test "update a filter", %{conn: conn} do
350 query = %Pleroma.Filter{
357 {:ok, _filter} = Pleroma.Filter.create(query)
359 new = %Pleroma.Filter{
366 |> assign(:user, user)
367 |> put("/api/v1/filters/#{query.filter_id}", %{
372 assert response = json_response(conn, 200)
373 assert response["phrase"] == new.phrase
374 assert response["context"] == new.context
377 test "delete a filter", %{conn: conn} do
380 query = %Pleroma.Filter{
387 {:ok, filter} = Pleroma.Filter.create(query)
391 |> assign(:user, user)
392 |> delete("/api/v1/filters/#{filter.filter_id}")
394 assert response = json_response(conn, 200)
395 assert response == %{}
399 describe "user timelines" do
400 test "gets a users statuses", %{conn: conn} do
401 user_one = insert(:user)
402 user_two = insert(:user)
403 user_three = insert(:user)
405 {:ok, user_three} = User.follow(user_three, user_one)
407 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
409 {:ok, direct_activity} =
410 CommonAPI.post(user_one, %{
411 "status" => "Hi, @#{user_two.nickname}.",
412 "visibility" => "direct"
415 {:ok, private_activity} =
416 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
420 |> get("/api/v1/accounts/#{user_one.id}/statuses")
422 assert [%{"id" => id}] = json_response(resp, 200)
423 assert id == to_string(activity.id)
427 |> assign(:user, user_two)
428 |> get("/api/v1/accounts/#{user_one.id}/statuses")
430 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
431 assert id_one == to_string(direct_activity.id)
432 assert id_two == to_string(activity.id)
436 |> assign(:user, user_three)
437 |> get("/api/v1/accounts/#{user_one.id}/statuses")
439 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
440 assert id_one == to_string(private_activity.id)
441 assert id_two == to_string(activity.id)
444 test "unimplemented pinned statuses feature", %{conn: conn} do
445 note = insert(:note_activity)
446 user = User.get_cached_by_ap_id(note.data["actor"])
450 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
452 assert json_response(conn, 200) == []
455 test "gets an users media", %{conn: conn} do
456 note = insert(:note_activity)
457 user = User.get_cached_by_ap_id(note.data["actor"])
460 content_type: "image/jpg",
461 path: Path.absname("test/fixtures/image.jpg"),
462 filename: "an_image.jpg"
465 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
467 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
471 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
473 assert [%{"id" => id}] = json_response(conn, 200)
474 assert id == to_string(image_post.id)
478 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
480 assert [%{"id" => id}] = json_response(conn, 200)
481 assert id == to_string(image_post.id)
484 test "gets a user's statuses without reblogs", %{conn: conn} do
486 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
487 {:ok, _, _} = CommonAPI.repeat(post.id, user)
491 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
493 assert [%{"id" => id}] = json_response(conn, 200)
494 assert id == to_string(post.id)
498 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
500 assert [%{"id" => id}] = json_response(conn, 200)
501 assert id == to_string(post.id)
504 test "filters user's statuses by a hashtag", %{conn: conn} do
506 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
507 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
511 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
513 assert [%{"id" => id}] = json_response(conn, 200)
514 assert id == to_string(post.id)
518 describe "user relationships" do
519 test "returns the relationships for the current user", %{conn: conn} do
521 other_user = insert(:user)
522 {:ok, user} = User.follow(user, other_user)
526 |> assign(:user, user)
527 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
529 assert [relationship] = json_response(conn, 200)
531 assert to_string(other_user.id) == relationship["id"]
535 describe "media upload" do
541 |> assign(:user, user)
543 image = %Plug.Upload{
544 content_type: "image/jpg",
545 path: Path.absname("test/fixtures/image.jpg"),
546 filename: "an_image.jpg"
549 [conn: conn, image: image]
552 clear_config([:media_proxy])
553 clear_config([Pleroma.Upload])
555 test "returns uploaded image", %{conn: conn, image: image} do
556 desc = "Description of the image"
560 |> post("/api/v1/media", %{"file" => image, "description" => desc})
561 |> json_response(:ok)
563 assert media["type"] == "image"
564 assert media["description"] == desc
567 object = Repo.get(Object, media["id"])
568 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
572 describe "locked accounts" do
573 test "/api/v1/follow_requests works" do
574 user = insert(:user, %{info: %User.Info{locked: true}})
575 other_user = insert(:user)
577 {:ok, _activity} = ActivityPub.follow(other_user, user)
579 user = User.get_cached_by_id(user.id)
580 other_user = User.get_cached_by_id(other_user.id)
582 assert User.following?(other_user, user) == false
586 |> assign(:user, user)
587 |> get("/api/v1/follow_requests")
589 assert [relationship] = json_response(conn, 200)
590 assert to_string(other_user.id) == relationship["id"]
593 test "/api/v1/follow_requests/:id/authorize works" do
594 user = insert(:user, %{info: %User.Info{locked: true}})
595 other_user = insert(:user)
597 {:ok, _activity} = ActivityPub.follow(other_user, user)
599 user = User.get_cached_by_id(user.id)
600 other_user = User.get_cached_by_id(other_user.id)
602 assert User.following?(other_user, user) == false
606 |> assign(:user, user)
607 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
609 assert relationship = json_response(conn, 200)
610 assert to_string(other_user.id) == relationship["id"]
612 user = User.get_cached_by_id(user.id)
613 other_user = User.get_cached_by_id(other_user.id)
615 assert User.following?(other_user, user) == true
618 test "verify_credentials", %{conn: conn} do
619 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
623 |> assign(:user, user)
624 |> get("/api/v1/accounts/verify_credentials")
626 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
627 assert id == to_string(user.id)
630 test "/api/v1/follow_requests/:id/reject works" do
631 user = insert(:user, %{info: %User.Info{locked: true}})
632 other_user = insert(:user)
634 {:ok, _activity} = ActivityPub.follow(other_user, user)
636 user = User.get_cached_by_id(user.id)
640 |> assign(:user, user)
641 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
643 assert relationship = json_response(conn, 200)
644 assert to_string(other_user.id) == relationship["id"]
646 user = User.get_cached_by_id(user.id)
647 other_user = User.get_cached_by_id(other_user.id)
649 assert User.following?(other_user, user) == false
653 describe "account fetching" do
654 test "works by id" do
659 |> get("/api/v1/accounts/#{user.id}")
661 assert %{"id" => id} = json_response(conn, 200)
662 assert id == to_string(user.id)
666 |> get("/api/v1/accounts/-1")
668 assert %{"error" => "Can't find user"} = json_response(conn, 404)
671 test "works by nickname" do
676 |> get("/api/v1/accounts/#{user.nickname}")
678 assert %{"id" => id} = json_response(conn, 200)
682 test "works by nickname for remote users" do
683 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
684 Pleroma.Config.put([:instance, :limit_to_local_content], false)
685 user = insert(:user, nickname: "user@example.com", local: false)
689 |> get("/api/v1/accounts/#{user.nickname}")
691 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
692 assert %{"id" => id} = json_response(conn, 200)
696 test "respects limit_to_local_content == :all for remote user nicknames" do
697 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
698 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
700 user = insert(:user, nickname: "user@example.com", local: false)
704 |> get("/api/v1/accounts/#{user.nickname}")
706 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
707 assert json_response(conn, 404)
710 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
711 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
712 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
714 user = insert(:user, nickname: "user@example.com", local: false)
715 reading_user = insert(:user)
719 |> get("/api/v1/accounts/#{user.nickname}")
721 assert json_response(conn, 404)
725 |> assign(:user, reading_user)
726 |> get("/api/v1/accounts/#{user.nickname}")
728 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
729 assert %{"id" => id} = json_response(conn, 200)
734 test "mascot upload", %{conn: conn} do
737 non_image_file = %Plug.Upload{
738 content_type: "audio/mpeg",
739 path: Path.absname("test/fixtures/sound.mp3"),
740 filename: "sound.mp3"
745 |> assign(:user, user)
746 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
748 assert json_response(conn, 415)
751 content_type: "image/jpg",
752 path: Path.absname("test/fixtures/image.jpg"),
753 filename: "an_image.jpg"
758 |> assign(:user, user)
759 |> put("/api/v1/pleroma/mascot", %{"file" => file})
761 assert %{"id" => _, "type" => image} = json_response(conn, 200)
764 test "mascot retrieving", %{conn: conn} do
766 # When user hasn't set a mascot, we should just get pleroma tan back
769 |> assign(:user, user)
770 |> get("/api/v1/pleroma/mascot")
772 assert %{"url" => url} = json_response(conn, 200)
773 assert url =~ "pleroma-fox-tan-smol"
775 # When a user sets their mascot, we should get that back
777 content_type: "image/jpg",
778 path: Path.absname("test/fixtures/image.jpg"),
779 filename: "an_image.jpg"
784 |> assign(:user, user)
785 |> put("/api/v1/pleroma/mascot", %{"file" => file})
787 assert json_response(conn, 200)
789 user = User.get_cached_by_id(user.id)
793 |> assign(:user, user)
794 |> get("/api/v1/pleroma/mascot")
796 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
797 assert url =~ "an_image"
800 test "getting followers", %{conn: conn} do
802 other_user = insert(:user)
803 {:ok, user} = User.follow(user, other_user)
807 |> get("/api/v1/accounts/#{other_user.id}/followers")
809 assert [%{"id" => id}] = json_response(conn, 200)
810 assert id == to_string(user.id)
813 test "getting followers, hide_followers", %{conn: conn} do
815 other_user = insert(:user, %{info: %{hide_followers: true}})
816 {:ok, _user} = User.follow(user, other_user)
820 |> get("/api/v1/accounts/#{other_user.id}/followers")
822 assert [] == json_response(conn, 200)
825 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
827 other_user = insert(:user, %{info: %{hide_followers: true}})
828 {:ok, _user} = User.follow(user, other_user)
832 |> assign(:user, other_user)
833 |> get("/api/v1/accounts/#{other_user.id}/followers")
835 refute [] == json_response(conn, 200)
838 test "getting followers, pagination", %{conn: conn} do
840 follower1 = insert(:user)
841 follower2 = insert(:user)
842 follower3 = insert(:user)
843 {:ok, _} = User.follow(follower1, user)
844 {:ok, _} = User.follow(follower2, user)
845 {:ok, _} = User.follow(follower3, user)
849 |> assign(:user, user)
853 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
855 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
856 assert id3 == follower3.id
857 assert id2 == follower2.id
861 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
863 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
864 assert id2 == follower2.id
865 assert id1 == follower1.id
869 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
871 assert [%{"id" => id2}] = json_response(res_conn, 200)
872 assert id2 == follower2.id
874 assert [link_header] = get_resp_header(res_conn, "link")
875 assert link_header =~ ~r/min_id=#{follower2.id}/
876 assert link_header =~ ~r/max_id=#{follower2.id}/
879 test "getting following", %{conn: conn} do
881 other_user = insert(:user)
882 {:ok, user} = User.follow(user, other_user)
886 |> get("/api/v1/accounts/#{user.id}/following")
888 assert [%{"id" => id}] = json_response(conn, 200)
889 assert id == to_string(other_user.id)
892 test "getting following, hide_follows", %{conn: conn} do
893 user = insert(:user, %{info: %{hide_follows: true}})
894 other_user = insert(:user)
895 {:ok, user} = User.follow(user, other_user)
899 |> get("/api/v1/accounts/#{user.id}/following")
901 assert [] == json_response(conn, 200)
904 test "getting following, hide_follows, same user requesting", %{conn: conn} do
905 user = insert(:user, %{info: %{hide_follows: true}})
906 other_user = insert(:user)
907 {:ok, user} = User.follow(user, other_user)
911 |> assign(:user, user)
912 |> get("/api/v1/accounts/#{user.id}/following")
914 refute [] == json_response(conn, 200)
917 test "getting following, pagination", %{conn: conn} do
919 following1 = insert(:user)
920 following2 = insert(:user)
921 following3 = insert(:user)
922 {:ok, _} = User.follow(user, following1)
923 {:ok, _} = User.follow(user, following2)
924 {:ok, _} = User.follow(user, following3)
928 |> assign(:user, user)
932 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
934 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
935 assert id3 == following3.id
936 assert id2 == following2.id
940 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
942 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
943 assert id2 == following2.id
944 assert id1 == following1.id
948 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
950 assert [%{"id" => id2}] = json_response(res_conn, 200)
951 assert id2 == following2.id
953 assert [link_header] = get_resp_header(res_conn, "link")
954 assert link_header =~ ~r/min_id=#{following2.id}/
955 assert link_header =~ ~r/max_id=#{following2.id}/
958 test "following / unfollowing a user", %{conn: conn} do
960 other_user = insert(:user)
964 |> assign(:user, user)
965 |> post("/api/v1/accounts/#{other_user.id}/follow")
967 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
969 user = User.get_cached_by_id(user.id)
973 |> assign(:user, user)
974 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
976 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
978 user = User.get_cached_by_id(user.id)
982 |> assign(:user, user)
983 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
985 assert %{"id" => id} = json_response(conn, 200)
986 assert id == to_string(other_user.id)
989 test "following without reblogs" do
990 follower = insert(:user)
991 followed = insert(:user)
992 other_user = insert(:user)
996 |> assign(:user, follower)
997 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
999 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1001 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1002 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1006 |> assign(:user, User.get_cached_by_id(follower.id))
1007 |> get("/api/v1/timelines/home")
1009 assert [] == json_response(conn, 200)
1013 |> assign(:user, follower)
1014 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1016 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1020 |> assign(:user, User.get_cached_by_id(follower.id))
1021 |> get("/api/v1/timelines/home")
1023 expected_activity_id = reblog.id
1024 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1027 test "following / unfollowing errors" do
1028 user = insert(:user)
1032 |> assign(:user, user)
1035 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1036 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1039 user = User.get_cached_by_id(user.id)
1040 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1041 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1043 # self follow via uri
1044 user = User.get_cached_by_id(user.id)
1045 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1046 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1048 # follow non existing user
1049 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1050 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1052 # follow non existing user via uri
1053 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1054 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1056 # unfollow non existing user
1057 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1058 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1061 describe "mute/unmute" do
1062 test "with notifications", %{conn: conn} do
1063 user = insert(:user)
1064 other_user = insert(:user)
1068 |> assign(:user, user)
1069 |> post("/api/v1/accounts/#{other_user.id}/mute")
1071 response = json_response(conn, 200)
1073 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
1074 user = User.get_cached_by_id(user.id)
1078 |> assign(:user, user)
1079 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1081 response = json_response(conn, 200)
1082 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
1085 test "without notifications", %{conn: conn} do
1086 user = insert(:user)
1087 other_user = insert(:user)
1091 |> assign(:user, user)
1092 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
1094 response = json_response(conn, 200)
1096 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
1097 user = User.get_cached_by_id(user.id)
1101 |> assign(:user, user)
1102 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1104 response = json_response(conn, 200)
1105 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
1109 test "subscribing / unsubscribing to a user", %{conn: conn} do
1110 user = insert(:user)
1111 subscription_target = insert(:user)
1115 |> assign(:user, user)
1116 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1118 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1122 |> assign(:user, user)
1123 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1125 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1128 test "getting a list of mutes", %{conn: conn} do
1129 user = insert(:user)
1130 other_user = insert(:user)
1132 {:ok, user} = User.mute(user, other_user)
1136 |> assign(:user, user)
1137 |> get("/api/v1/mutes")
1139 other_user_id = to_string(other_user.id)
1140 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1143 test "blocking / unblocking a user", %{conn: conn} do
1144 user = insert(:user)
1145 other_user = insert(:user)
1149 |> assign(:user, user)
1150 |> post("/api/v1/accounts/#{other_user.id}/block")
1152 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1154 user = User.get_cached_by_id(user.id)
1158 |> assign(:user, user)
1159 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1161 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1164 test "getting a list of blocks", %{conn: conn} do
1165 user = insert(:user)
1166 other_user = insert(:user)
1168 {:ok, user} = User.block(user, other_user)
1172 |> assign(:user, user)
1173 |> get("/api/v1/blocks")
1175 other_user_id = to_string(other_user.id)
1176 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1179 test "unimplemented follow_requests, blocks, domain blocks" do
1180 user = insert(:user)
1182 ["blocks", "domain_blocks", "follow_requests"]
1183 |> Enum.each(fn endpoint ->
1186 |> assign(:user, user)
1187 |> get("/api/v1/#{endpoint}")
1189 assert [] = json_response(conn, 200)
1193 test "returns the favorites of a user", %{conn: conn} do
1194 user = insert(:user)
1195 other_user = insert(:user)
1197 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1198 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1200 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1204 |> assign(:user, user)
1205 |> get("/api/v1/favourites")
1207 assert [status] = json_response(first_conn, 200)
1208 assert status["id"] == to_string(activity.id)
1210 assert [{"link", _link_header}] =
1211 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1213 # Honours query params
1214 {:ok, second_activity} =
1215 CommonAPI.post(other_user, %{
1217 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1220 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1222 last_like = status["id"]
1226 |> assign(:user, user)
1227 |> get("/api/v1/favourites?since_id=#{last_like}")
1229 assert [second_status] = json_response(second_conn, 200)
1230 assert second_status["id"] == to_string(second_activity.id)
1234 |> assign(:user, user)
1235 |> get("/api/v1/favourites?limit=0")
1237 assert [] = json_response(third_conn, 200)
1240 describe "getting favorites timeline of specified user" do
1242 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
1243 [current_user: current_user, user: user]
1246 test "returns list of statuses favorited by specified user", %{
1248 current_user: current_user,
1251 [activity | _] = insert_pair(:note_activity)
1252 CommonAPI.favorite(activity.id, user)
1256 |> assign(:user, current_user)
1257 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1258 |> json_response(:ok)
1262 assert length(response) == 1
1263 assert like["id"] == activity.id
1266 test "returns favorites for specified user_id when user is not logged in", %{
1270 activity = insert(:note_activity)
1271 CommonAPI.favorite(activity.id, user)
1275 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1276 |> json_response(:ok)
1278 assert length(response) == 1
1281 test "returns favorited DM only when user is logged in and he is one of recipients", %{
1283 current_user: current_user,
1287 CommonAPI.post(current_user, %{
1288 "status" => "Hi @#{user.nickname}!",
1289 "visibility" => "direct"
1292 CommonAPI.favorite(direct.id, user)
1296 |> assign(:user, current_user)
1297 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1298 |> json_response(:ok)
1300 assert length(response) == 1
1302 anonymous_response =
1304 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1305 |> json_response(:ok)
1307 assert Enum.empty?(anonymous_response)
1310 test "does not return others' favorited DM when user is not one of recipients", %{
1312 current_user: current_user,
1315 user_two = insert(:user)
1318 CommonAPI.post(user_two, %{
1319 "status" => "Hi @#{user.nickname}!",
1320 "visibility" => "direct"
1323 CommonAPI.favorite(direct.id, user)
1327 |> assign(:user, current_user)
1328 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1329 |> json_response(:ok)
1331 assert Enum.empty?(response)
1334 test "paginates favorites using since_id and max_id", %{
1336 current_user: current_user,
1339 activities = insert_list(10, :note_activity)
1341 Enum.each(activities, fn activity ->
1342 CommonAPI.favorite(activity.id, user)
1345 third_activity = Enum.at(activities, 2)
1346 seventh_activity = Enum.at(activities, 6)
1350 |> assign(:user, current_user)
1351 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
1352 since_id: third_activity.id,
1353 max_id: seventh_activity.id
1355 |> json_response(:ok)
1357 assert length(response) == 3
1358 refute third_activity in response
1359 refute seventh_activity in response
1362 test "limits favorites using limit parameter", %{
1364 current_user: current_user,
1368 |> insert_list(:note_activity)
1369 |> Enum.each(fn activity ->
1370 CommonAPI.favorite(activity.id, user)
1375 |> assign(:user, current_user)
1376 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
1377 |> json_response(:ok)
1379 assert length(response) == 3
1382 test "returns empty response when user does not have any favorited statuses", %{
1384 current_user: current_user,
1389 |> assign(:user, current_user)
1390 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1391 |> json_response(:ok)
1393 assert Enum.empty?(response)
1396 test "returns 404 error when specified user is not exist", %{conn: conn} do
1397 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
1399 assert json_response(conn, 404) == %{"error" => "Record not found"}
1402 test "returns 403 error when user has hidden own favorites", %{
1404 current_user: current_user
1406 user = insert(:user, %{info: %{hide_favorites: true}})
1407 activity = insert(:note_activity)
1408 CommonAPI.favorite(activity.id, user)
1412 |> assign(:user, current_user)
1413 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1415 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1418 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
1419 user = insert(:user)
1420 activity = insert(:note_activity)
1421 CommonAPI.favorite(activity.id, user)
1425 |> assign(:user, current_user)
1426 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1428 assert user.info.hide_favorites
1429 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1433 test "get instance information", %{conn: conn} do
1434 conn = get(conn, "/api/v1/instance")
1435 assert result = json_response(conn, 200)
1437 email = Config.get([:instance, :email])
1438 # Note: not checking for "max_toot_chars" since it's optional
1444 "email" => from_config_email,
1446 "streaming_api" => _
1451 "registrations" => _,
1455 assert email == from_config_email
1458 test "get instance stats", %{conn: conn} do
1459 user = insert(:user, %{local: true})
1461 user2 = insert(:user, %{local: true})
1462 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1464 insert(:user, %{local: false, nickname: "u@peer1.com"})
1465 insert(:user, %{local: false, nickname: "u@peer2.com"})
1467 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
1469 # Stats should count users with missing or nil `info.deactivated` value
1473 |> User.get_cached_by_id()
1474 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
1476 Pleroma.Stats.force_update()
1478 conn = get(conn, "/api/v1/instance")
1480 assert result = json_response(conn, 200)
1482 stats = result["stats"]
1485 assert stats["user_count"] == 1
1486 assert stats["status_count"] == 1
1487 assert stats["domain_count"] == 2
1490 test "get peers", %{conn: conn} do
1491 insert(:user, %{local: false, nickname: "u@peer1.com"})
1492 insert(:user, %{local: false, nickname: "u@peer2.com"})
1494 Pleroma.Stats.force_update()
1496 conn = get(conn, "/api/v1/instance/peers")
1498 assert result = json_response(conn, 200)
1500 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
1503 test "put settings", %{conn: conn} do
1504 user = insert(:user)
1508 |> assign(:user, user)
1509 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
1511 assert _result = json_response(conn, 200)
1513 user = User.get_cached_by_ap_id(user.ap_id)
1514 assert user.info.settings == %{"programming" => "socks"}
1517 describe "pinned statuses" do
1519 user = insert(:user)
1520 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
1522 [user: user, activity: activity]
1525 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
1526 {:ok, _} = CommonAPI.pin(activity.id, user)
1530 |> assign(:user, user)
1531 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1532 |> json_response(200)
1534 id_str = to_string(activity.id)
1536 assert [%{"id" => ^id_str, "pinned" => true}] = result
1540 describe "reports" do
1542 reporter = insert(:user)
1543 target_user = insert(:user)
1545 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
1547 [reporter: reporter, target_user: target_user, activity: activity]
1550 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
1551 assert %{"action_taken" => false, "id" => _} =
1553 |> assign(:user, reporter)
1554 |> post("/api/v1/reports", %{"account_id" => target_user.id})
1555 |> json_response(200)
1558 test "submit a report with statuses and comment", %{
1561 target_user: target_user,
1564 assert %{"action_taken" => false, "id" => _} =
1566 |> assign(:user, reporter)
1567 |> post("/api/v1/reports", %{
1568 "account_id" => target_user.id,
1569 "status_ids" => [activity.id],
1570 "comment" => "bad status!",
1571 "forward" => "false"
1573 |> json_response(200)
1576 test "account_id is required", %{
1581 assert %{"error" => "Valid `account_id` required"} =
1583 |> assign(:user, reporter)
1584 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
1585 |> json_response(400)
1588 test "comment must be up to the size specified in the config", %{
1591 target_user: target_user
1593 max_size = Config.get([:instance, :max_report_comment_size], 1000)
1594 comment = String.pad_trailing("a", max_size + 1, "a")
1596 error = %{"error" => "Comment must be up to #{max_size} characters"}
1600 |> assign(:user, reporter)
1601 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
1602 |> json_response(400)
1605 test "returns error when account is not exist", %{
1612 |> assign(:user, reporter)
1613 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
1615 assert json_response(conn, 400) == %{"error" => "Account not found"}
1619 describe "link headers" do
1620 test "preserves parameters in link headers", %{conn: conn} do
1621 user = insert(:user)
1622 other_user = insert(:user)
1625 CommonAPI.post(other_user, %{
1626 "status" => "hi @#{user.nickname}",
1627 "visibility" => "public"
1631 CommonAPI.post(other_user, %{
1632 "status" => "hi @#{user.nickname}",
1633 "visibility" => "public"
1636 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
1637 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
1641 |> assign(:user, user)
1642 |> get("/api/v1/notifications", %{media_only: true})
1644 assert [link_header] = get_resp_header(conn, "link")
1645 assert link_header =~ ~r/media_only=true/
1646 assert link_header =~ ~r/min_id=#{notification2.id}/
1647 assert link_header =~ ~r/max_id=#{notification1.id}/
1651 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
1652 # Need to set an old-style integer ID to reproduce the problem
1653 # (these are no longer assigned to new accounts but were preserved
1654 # for existing accounts during the migration to flakeIDs)
1655 user_one = insert(:user, %{id: 1212})
1656 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
1660 |> get("/api/v1/accounts/#{user_one.id}")
1664 |> get("/api/v1/accounts/#{user_two.nickname}")
1668 |> get("/api/v1/accounts/#{user_two.id}")
1670 acc_one = json_response(resp_one, 200)
1671 acc_two = json_response(resp_two, 200)
1672 acc_three = json_response(resp_three, 200)
1673 refute acc_one == acc_two
1674 assert acc_two == acc_three
1677 describe "custom emoji" do
1678 test "with tags", %{conn: conn} do
1681 |> get("/api/v1/custom_emojis")
1682 |> json_response(200)
1684 assert Map.has_key?(emoji, "shortcode")
1685 assert Map.has_key?(emoji, "static_url")
1686 assert Map.has_key?(emoji, "tags")
1687 assert is_list(emoji["tags"])
1688 assert Map.has_key?(emoji, "category")
1689 assert Map.has_key?(emoji, "url")
1690 assert Map.has_key?(emoji, "visible_in_picker")
1694 describe "index/2 redirections" do
1695 setup %{conn: conn} do
1699 signing_salt: "cooldude"
1704 |> Plug.Session.call(Plug.Session.init(session_opts))
1707 test_path = "/web/statuses/test"
1708 %{conn: conn, path: test_path}
1711 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
1712 conn = get(conn, path)
1714 assert conn.status == 302
1715 assert redirected_to(conn) == "/web/login"
1718 test "redirects not logged-in users to the login page on private instances", %{
1722 Config.put([:instance, :public], false)
1724 conn = get(conn, path)
1726 assert conn.status == 302
1727 assert redirected_to(conn) == "/web/login"
1730 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
1731 token = insert(:oauth_token)
1735 |> assign(:user, token.user)
1736 |> put_session(:oauth_token, token.token)
1739 assert conn.status == 200
1742 test "saves referer path to session", %{conn: conn, path: path} do
1743 conn = get(conn, path)
1744 return_to = Plug.Conn.get_session(conn, :return_to)
1746 assert return_to == path
1749 test "redirects to the saved path after log in", %{conn: conn, path: path} do
1750 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1751 auth = insert(:oauth_authorization, app: app)
1755 |> put_session(:return_to, path)
1756 |> get("/web/login", %{code: auth.token})
1758 assert conn.status == 302
1759 assert redirected_to(conn) == path
1762 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
1763 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1764 auth = insert(:oauth_authorization, app: app)
1766 conn = get(conn, "/web/login", %{code: auth.token})
1768 assert conn.status == 302
1769 assert redirected_to(conn) == "/web/getting-started"
1773 describe "scheduled activities" do
1774 test "creates a scheduled activity", %{conn: conn} do
1775 user = insert(:user)
1776 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1780 |> assign(:user, user)
1781 |> post("/api/v1/statuses", %{
1782 "status" => "scheduled",
1783 "scheduled_at" => scheduled_at
1786 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
1787 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
1788 assert [] == Repo.all(Activity)
1791 test "creates a scheduled activity with a media attachment", %{conn: conn} do
1792 user = insert(:user)
1793 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1795 file = %Plug.Upload{
1796 content_type: "image/jpg",
1797 path: Path.absname("test/fixtures/image.jpg"),
1798 filename: "an_image.jpg"
1801 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
1805 |> assign(:user, user)
1806 |> post("/api/v1/statuses", %{
1807 "media_ids" => [to_string(upload.id)],
1808 "status" => "scheduled",
1809 "scheduled_at" => scheduled_at
1812 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
1813 assert %{"type" => "image"} = media_attachment
1816 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
1818 user = insert(:user)
1821 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
1825 |> assign(:user, user)
1826 |> post("/api/v1/statuses", %{
1827 "status" => "not scheduled",
1828 "scheduled_at" => scheduled_at
1831 assert %{"content" => "not scheduled"} = json_response(conn, 200)
1832 assert [] == Repo.all(ScheduledActivity)
1835 test "returns error when daily user limit is exceeded", %{conn: conn} do
1836 user = insert(:user)
1839 NaiveDateTime.utc_now()
1840 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
1841 |> NaiveDateTime.to_iso8601()
1843 attrs = %{params: %{}, scheduled_at: today}
1844 {:ok, _} = ScheduledActivity.create(user, attrs)
1845 {:ok, _} = ScheduledActivity.create(user, attrs)
1849 |> assign(:user, user)
1850 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
1852 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
1855 test "returns error when total user limit is exceeded", %{conn: conn} do
1856 user = insert(:user)
1859 NaiveDateTime.utc_now()
1860 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
1861 |> NaiveDateTime.to_iso8601()
1864 NaiveDateTime.utc_now()
1865 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
1866 |> NaiveDateTime.to_iso8601()
1868 attrs = %{params: %{}, scheduled_at: today}
1869 {:ok, _} = ScheduledActivity.create(user, attrs)
1870 {:ok, _} = ScheduledActivity.create(user, attrs)
1871 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
1875 |> assign(:user, user)
1876 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
1878 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
1881 test "shows scheduled activities", %{conn: conn} do
1882 user = insert(:user)
1883 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
1884 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
1885 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
1886 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
1890 |> assign(:user, user)
1895 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
1897 result = json_response(conn_res, 200)
1898 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
1903 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
1905 result = json_response(conn_res, 200)
1906 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
1911 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
1913 result = json_response(conn_res, 200)
1914 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
1917 test "shows a scheduled activity", %{conn: conn} do
1918 user = insert(:user)
1919 scheduled_activity = insert(:scheduled_activity, user: user)
1923 |> assign(:user, user)
1924 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1926 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
1927 assert scheduled_activity_id == scheduled_activity.id |> to_string()
1931 |> assign(:user, user)
1932 |> get("/api/v1/scheduled_statuses/404")
1934 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1937 test "updates a scheduled activity", %{conn: conn} do
1938 user = insert(:user)
1939 scheduled_activity = insert(:scheduled_activity, user: user)
1942 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1946 |> assign(:user, user)
1947 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
1948 scheduled_at: new_scheduled_at
1951 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
1952 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
1956 |> assign(:user, user)
1957 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
1959 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1962 test "deletes a scheduled activity", %{conn: conn} do
1963 user = insert(:user)
1964 scheduled_activity = insert(:scheduled_activity, user: user)
1968 |> assign(:user, user)
1969 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1971 assert %{} = json_response(res_conn, 200)
1972 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
1976 |> assign(:user, user)
1977 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1979 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1983 describe "create account by app" do
1984 test "Account registration via Application", %{conn: conn} do
1987 |> post("/api/v1/apps", %{
1988 client_name: "client_name",
1989 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1990 scopes: "read, write, follow"
1994 "client_id" => client_id,
1995 "client_secret" => client_secret,
1997 "name" => "client_name",
1998 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
2001 } = json_response(conn, 200)
2005 |> post("/oauth/token", %{
2006 grant_type: "client_credentials",
2007 client_id: client_id,
2008 client_secret: client_secret
2011 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
2012 json_response(conn, 200)
2015 token_from_db = Repo.get_by(Token, token: token)
2016 assert token_from_db
2018 assert scope == "read write follow"
2022 |> put_req_header("authorization", "Bearer " <> token)
2023 |> post("/api/v1/accounts", %{
2025 email: "lain@example.org",
2026 password: "PlzDontHackLain",
2031 "access_token" => token,
2032 "created_at" => _created_at,
2034 "token_type" => "Bearer"
2035 } = json_response(conn, 200)
2037 token_from_db = Repo.get_by(Token, token: token)
2038 assert token_from_db
2039 token_from_db = Repo.preload(token_from_db, :user)
2040 assert token_from_db.user
2042 assert token_from_db.user.info.confirmation_pending
2045 test "rate limit", %{conn: conn} do
2046 app_token = insert(:oauth_token, user: nil)
2049 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
2050 |> Map.put(:remote_ip, {15, 15, 15, 15})
2055 |> post("/api/v1/accounts", %{
2056 username: "#{i}lain",
2057 email: "#{i}lain@example.org",
2058 password: "PlzDontHackLain",
2063 "access_token" => token,
2064 "created_at" => _created_at,
2066 "token_type" => "Bearer"
2067 } = json_response(conn, 200)
2069 token_from_db = Repo.get_by(Token, token: token)
2070 assert token_from_db
2071 token_from_db = Repo.preload(token_from_db, :user)
2072 assert token_from_db.user
2074 assert token_from_db.user.info.confirmation_pending
2079 |> post("/api/v1/accounts", %{
2081 email: "6lain@example.org",
2082 password: "PlzDontHackLain",
2086 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
2090 describe "GET /api/v1/polls/:id" do
2091 test "returns poll entity for object id", %{conn: conn} do
2092 user = insert(:user)
2095 CommonAPI.post(user, %{
2096 "status" => "Pleroma does",
2097 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
2100 object = Object.normalize(activity)
2104 |> assign(:user, user)
2105 |> get("/api/v1/polls/#{object.id}")
2107 response = json_response(conn, 200)
2108 id = to_string(object.id)
2109 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
2112 test "does not expose polls for private statuses", %{conn: conn} do
2113 user = insert(:user)
2114 other_user = insert(:user)
2117 CommonAPI.post(user, %{
2118 "status" => "Pleroma does",
2119 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
2120 "visibility" => "private"
2123 object = Object.normalize(activity)
2127 |> assign(:user, other_user)
2128 |> get("/api/v1/polls/#{object.id}")
2130 assert json_response(conn, 404)
2134 describe "POST /api/v1/polls/:id/votes" do
2135 test "votes are added to the poll", %{conn: conn} do
2136 user = insert(:user)
2137 other_user = insert(:user)
2140 CommonAPI.post(user, %{
2141 "status" => "A very delicious sandwich",
2143 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
2149 object = Object.normalize(activity)
2153 |> assign(:user, other_user)
2154 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
2156 assert json_response(conn, 200)
2157 object = Object.get_by_id(object.id)
2159 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
2164 test "author can't vote", %{conn: conn} do
2165 user = insert(:user)
2168 CommonAPI.post(user, %{
2169 "status" => "Am I cute?",
2170 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
2173 object = Object.normalize(activity)
2176 |> assign(:user, user)
2177 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
2178 |> json_response(422) == %{"error" => "Poll's author can't vote"}
2180 object = Object.get_by_id(object.id)
2182 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
2185 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
2186 user = insert(:user)
2187 other_user = insert(:user)
2190 CommonAPI.post(user, %{
2191 "status" => "The glass is",
2192 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
2195 object = Object.normalize(activity)
2198 |> assign(:user, other_user)
2199 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
2200 |> json_response(422) == %{"error" => "Too many choices"}
2202 object = Object.get_by_id(object.id)
2204 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
2209 test "does not allow choice index to be greater than options count", %{conn: conn} do
2210 user = insert(:user)
2211 other_user = insert(:user)
2214 CommonAPI.post(user, %{
2215 "status" => "Am I cute?",
2216 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
2219 object = Object.normalize(activity)
2223 |> assign(:user, other_user)
2224 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
2226 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
2229 test "returns 404 error when object is not exist", %{conn: conn} do
2230 user = insert(:user)
2234 |> assign(:user, user)
2235 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
2237 assert json_response(conn, 404) == %{"error" => "Record not found"}
2240 test "returns 404 when poll is private and not available for user", %{conn: conn} do
2241 user = insert(:user)
2242 other_user = insert(:user)
2245 CommonAPI.post(user, %{
2246 "status" => "Am I cute?",
2247 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
2248 "visibility" => "private"
2251 object = Object.normalize(activity)
2255 |> assign(:user, other_user)
2256 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
2258 assert json_response(conn, 404) == %{"error" => "Record not found"}
2262 describe "POST /auth/password, with valid parameters" do
2263 setup %{conn: conn} do
2264 user = insert(:user)
2265 conn = post(conn, "/auth/password?email=#{user.email}")
2266 %{conn: conn, user: user}
2269 test "it returns 204", %{conn: conn} do
2270 assert json_response(conn, :no_content)
2273 test "it creates a PasswordResetToken record for user", %{user: user} do
2274 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
2278 test "it sends an email to user", %{user: user} do
2279 ObanHelpers.perform_all()
2280 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
2282 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
2283 notify_email = Config.get([:instance, :notify_email])
2284 instance_name = Config.get([:instance, :name])
2287 from: {instance_name, notify_email},
2288 to: {user.name, user.email},
2289 html_body: email.html_body
2294 describe "POST /auth/password, with invalid parameters" do
2296 user = insert(:user)
2300 test "it returns 404 when user is not found", %{conn: conn, user: user} do
2301 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
2302 assert conn.status == 404
2303 assert conn.resp_body == ""
2306 test "it returns 400 when user is not local", %{conn: conn, user: user} do
2307 {:ok, user} = Repo.update(Changeset.change(user, local: false))
2308 conn = post(conn, "/auth/password?email=#{user.email}")
2309 assert conn.status == 400
2310 assert conn.resp_body == ""
2314 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
2318 |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
2321 assert user.info.confirmation_pending
2326 clear_config([:instance, :account_activation_required]) do
2327 Config.put([:instance, :account_activation_required], true)
2330 test "resend account confirmation email", %{conn: conn, user: user} do
2332 |> assign(:user, user)
2333 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
2334 |> json_response(:no_content)
2336 ObanHelpers.perform_all()
2338 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
2339 notify_email = Config.get([:instance, :notify_email])
2340 instance_name = Config.get([:instance, :name])
2343 from: {instance_name, notify_email},
2344 to: {user.name, user.email},
2345 html_body: email.html_body
2350 describe "GET /api/v1/suggestions" do
2352 user = insert(:user)
2353 other_user = insert(:user)
2354 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
2355 url500 = "http://test500?#{host}&#{user.nickname}"
2356 url200 = "http://test200?#{host}&#{user.nickname}"
2359 %{method: :get, url: ^url500} ->
2360 %Tesla.Env{status: 500, body: "bad request"}
2362 %{method: :get, url: ^url200} ->
2366 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
2368 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
2372 [user: user, other_user: other_user]
2375 clear_config(:suggestions)
2377 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
2378 Config.put([:suggestions, :enabled], false)
2382 |> assign(:user, user)
2383 |> get("/api/v1/suggestions")
2384 |> json_response(200)
2389 test "returns error", %{conn: conn, user: user} do
2390 Config.put([:suggestions, :enabled], true)
2391 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
2393 assert capture_log(fn ->
2396 |> assign(:user, user)
2397 |> get("/api/v1/suggestions")
2398 |> json_response(500)
2400 assert res == "Something went wrong"
2401 end) =~ "Could not retrieve suggestions"
2404 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
2405 Config.put([:suggestions, :enabled], true)
2406 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
2410 |> assign(:user, user)
2411 |> get("/api/v1/suggestions")
2412 |> json_response(200)
2417 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
2418 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
2422 "acct" => other_user.ap_id,
2423 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
2424 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
2425 "id" => other_user.id