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 "blocking / unblocking a domain", %{conn: conn} do
1180 user = insert(:user)
1181 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1185 |> assign(:user, user)
1186 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1188 assert %{} = json_response(conn, 200)
1189 user = User.get_cached_by_ap_id(user.ap_id)
1190 assert User.blocks?(user, other_user)
1194 |> assign(:user, user)
1195 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1197 assert %{} = json_response(conn, 200)
1198 user = User.get_cached_by_ap_id(user.ap_id)
1199 refute User.blocks?(user, other_user)
1202 test "getting a list of domain blocks", %{conn: conn} do
1203 user = insert(:user)
1205 {:ok, user} = User.block_domain(user, "bad.site")
1206 {:ok, user} = User.block_domain(user, "even.worse.site")
1210 |> assign(:user, user)
1211 |> get("/api/v1/domain_blocks")
1213 domain_blocks = json_response(conn, 200)
1215 assert "bad.site" in domain_blocks
1216 assert "even.worse.site" in domain_blocks
1219 test "unimplemented follow_requests, blocks, domain blocks" do
1220 user = insert(:user)
1222 ["blocks", "domain_blocks", "follow_requests"]
1223 |> Enum.each(fn endpoint ->
1226 |> assign(:user, user)
1227 |> get("/api/v1/#{endpoint}")
1229 assert [] = json_response(conn, 200)
1233 test "returns the favorites of a user", %{conn: conn} do
1234 user = insert(:user)
1235 other_user = insert(:user)
1237 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1238 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1240 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1244 |> assign(:user, user)
1245 |> get("/api/v1/favourites")
1247 assert [status] = json_response(first_conn, 200)
1248 assert status["id"] == to_string(activity.id)
1250 assert [{"link", _link_header}] =
1251 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1253 # Honours query params
1254 {:ok, second_activity} =
1255 CommonAPI.post(other_user, %{
1257 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1260 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1262 last_like = status["id"]
1266 |> assign(:user, user)
1267 |> get("/api/v1/favourites?since_id=#{last_like}")
1269 assert [second_status] = json_response(second_conn, 200)
1270 assert second_status["id"] == to_string(second_activity.id)
1274 |> assign(:user, user)
1275 |> get("/api/v1/favourites?limit=0")
1277 assert [] = json_response(third_conn, 200)
1280 describe "getting favorites timeline of specified user" do
1282 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
1283 [current_user: current_user, user: user]
1286 test "returns list of statuses favorited by specified user", %{
1288 current_user: current_user,
1291 [activity | _] = insert_pair(:note_activity)
1292 CommonAPI.favorite(activity.id, user)
1296 |> assign(:user, current_user)
1297 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1298 |> json_response(:ok)
1302 assert length(response) == 1
1303 assert like["id"] == activity.id
1306 test "returns favorites for specified user_id when user is not logged in", %{
1310 activity = insert(:note_activity)
1311 CommonAPI.favorite(activity.id, user)
1315 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1316 |> json_response(:ok)
1318 assert length(response) == 1
1321 test "returns favorited DM only when user is logged in and he is one of recipients", %{
1323 current_user: current_user,
1327 CommonAPI.post(current_user, %{
1328 "status" => "Hi @#{user.nickname}!",
1329 "visibility" => "direct"
1332 CommonAPI.favorite(direct.id, user)
1336 |> assign(:user, current_user)
1337 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1338 |> json_response(:ok)
1340 assert length(response) == 1
1342 anonymous_response =
1344 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1345 |> json_response(:ok)
1347 assert Enum.empty?(anonymous_response)
1350 test "does not return others' favorited DM when user is not one of recipients", %{
1352 current_user: current_user,
1355 user_two = insert(:user)
1358 CommonAPI.post(user_two, %{
1359 "status" => "Hi @#{user.nickname}!",
1360 "visibility" => "direct"
1363 CommonAPI.favorite(direct.id, user)
1367 |> assign(:user, current_user)
1368 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1369 |> json_response(:ok)
1371 assert Enum.empty?(response)
1374 test "paginates favorites using since_id and max_id", %{
1376 current_user: current_user,
1379 activities = insert_list(10, :note_activity)
1381 Enum.each(activities, fn activity ->
1382 CommonAPI.favorite(activity.id, user)
1385 third_activity = Enum.at(activities, 2)
1386 seventh_activity = Enum.at(activities, 6)
1390 |> assign(:user, current_user)
1391 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
1392 since_id: third_activity.id,
1393 max_id: seventh_activity.id
1395 |> json_response(:ok)
1397 assert length(response) == 3
1398 refute third_activity in response
1399 refute seventh_activity in response
1402 test "limits favorites using limit parameter", %{
1404 current_user: current_user,
1408 |> insert_list(:note_activity)
1409 |> Enum.each(fn activity ->
1410 CommonAPI.favorite(activity.id, user)
1415 |> assign(:user, current_user)
1416 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
1417 |> json_response(:ok)
1419 assert length(response) == 3
1422 test "returns empty response when user does not have any favorited statuses", %{
1424 current_user: current_user,
1429 |> assign(:user, current_user)
1430 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1431 |> json_response(:ok)
1433 assert Enum.empty?(response)
1436 test "returns 404 error when specified user is not exist", %{conn: conn} do
1437 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
1439 assert json_response(conn, 404) == %{"error" => "Record not found"}
1442 test "returns 403 error when user has hidden own favorites", %{
1444 current_user: current_user
1446 user = insert(:user, %{info: %{hide_favorites: true}})
1447 activity = insert(:note_activity)
1448 CommonAPI.favorite(activity.id, user)
1452 |> assign(:user, current_user)
1453 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1455 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1458 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
1459 user = insert(:user)
1460 activity = insert(:note_activity)
1461 CommonAPI.favorite(activity.id, user)
1465 |> assign(:user, current_user)
1466 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1468 assert user.info.hide_favorites
1469 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1473 test "get instance information", %{conn: conn} do
1474 conn = get(conn, "/api/v1/instance")
1475 assert result = json_response(conn, 200)
1477 email = Config.get([:instance, :email])
1478 # Note: not checking for "max_toot_chars" since it's optional
1484 "email" => from_config_email,
1486 "streaming_api" => _
1491 "registrations" => _,
1495 assert email == from_config_email
1498 test "get instance stats", %{conn: conn} do
1499 user = insert(:user, %{local: true})
1501 user2 = insert(:user, %{local: true})
1502 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1504 insert(:user, %{local: false, nickname: "u@peer1.com"})
1505 insert(:user, %{local: false, nickname: "u@peer2.com"})
1507 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
1509 # Stats should count users with missing or nil `info.deactivated` value
1513 |> User.get_cached_by_id()
1514 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
1516 Pleroma.Stats.force_update()
1518 conn = get(conn, "/api/v1/instance")
1520 assert result = json_response(conn, 200)
1522 stats = result["stats"]
1525 assert stats["user_count"] == 1
1526 assert stats["status_count"] == 1
1527 assert stats["domain_count"] == 2
1530 test "get peers", %{conn: conn} do
1531 insert(:user, %{local: false, nickname: "u@peer1.com"})
1532 insert(:user, %{local: false, nickname: "u@peer2.com"})
1534 Pleroma.Stats.force_update()
1536 conn = get(conn, "/api/v1/instance/peers")
1538 assert result = json_response(conn, 200)
1540 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
1543 test "put settings", %{conn: conn} do
1544 user = insert(:user)
1548 |> assign(:user, user)
1549 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
1551 assert _result = json_response(conn, 200)
1553 user = User.get_cached_by_ap_id(user.ap_id)
1554 assert user.info.settings == %{"programming" => "socks"}
1557 describe "pinned statuses" do
1559 user = insert(:user)
1560 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
1562 [user: user, activity: activity]
1565 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
1566 {:ok, _} = CommonAPI.pin(activity.id, user)
1570 |> assign(:user, user)
1571 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1572 |> json_response(200)
1574 id_str = to_string(activity.id)
1576 assert [%{"id" => ^id_str, "pinned" => true}] = result
1580 describe "reports" do
1582 reporter = insert(:user)
1583 target_user = insert(:user)
1585 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
1587 [reporter: reporter, target_user: target_user, activity: activity]
1590 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
1591 assert %{"action_taken" => false, "id" => _} =
1593 |> assign(:user, reporter)
1594 |> post("/api/v1/reports", %{"account_id" => target_user.id})
1595 |> json_response(200)
1598 test "submit a report with statuses and comment", %{
1601 target_user: target_user,
1604 assert %{"action_taken" => false, "id" => _} =
1606 |> assign(:user, reporter)
1607 |> post("/api/v1/reports", %{
1608 "account_id" => target_user.id,
1609 "status_ids" => [activity.id],
1610 "comment" => "bad status!",
1611 "forward" => "false"
1613 |> json_response(200)
1616 test "account_id is required", %{
1621 assert %{"error" => "Valid `account_id` required"} =
1623 |> assign(:user, reporter)
1624 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
1625 |> json_response(400)
1628 test "comment must be up to the size specified in the config", %{
1631 target_user: target_user
1633 max_size = Config.get([:instance, :max_report_comment_size], 1000)
1634 comment = String.pad_trailing("a", max_size + 1, "a")
1636 error = %{"error" => "Comment must be up to #{max_size} characters"}
1640 |> assign(:user, reporter)
1641 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
1642 |> json_response(400)
1645 test "returns error when account is not exist", %{
1652 |> assign(:user, reporter)
1653 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
1655 assert json_response(conn, 400) == %{"error" => "Account not found"}
1659 describe "link headers" do
1660 test "preserves parameters in link headers", %{conn: conn} do
1661 user = insert(:user)
1662 other_user = insert(:user)
1665 CommonAPI.post(other_user, %{
1666 "status" => "hi @#{user.nickname}",
1667 "visibility" => "public"
1671 CommonAPI.post(other_user, %{
1672 "status" => "hi @#{user.nickname}",
1673 "visibility" => "public"
1676 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
1677 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
1681 |> assign(:user, user)
1682 |> get("/api/v1/notifications", %{media_only: true})
1684 assert [link_header] = get_resp_header(conn, "link")
1685 assert link_header =~ ~r/media_only=true/
1686 assert link_header =~ ~r/min_id=#{notification2.id}/
1687 assert link_header =~ ~r/max_id=#{notification1.id}/
1691 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
1692 # Need to set an old-style integer ID to reproduce the problem
1693 # (these are no longer assigned to new accounts but were preserved
1694 # for existing accounts during the migration to flakeIDs)
1695 user_one = insert(:user, %{id: 1212})
1696 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
1700 |> get("/api/v1/accounts/#{user_one.id}")
1704 |> get("/api/v1/accounts/#{user_two.nickname}")
1708 |> get("/api/v1/accounts/#{user_two.id}")
1710 acc_one = json_response(resp_one, 200)
1711 acc_two = json_response(resp_two, 200)
1712 acc_three = json_response(resp_three, 200)
1713 refute acc_one == acc_two
1714 assert acc_two == acc_three
1717 describe "custom emoji" do
1718 test "with tags", %{conn: conn} do
1721 |> get("/api/v1/custom_emojis")
1722 |> json_response(200)
1724 assert Map.has_key?(emoji, "shortcode")
1725 assert Map.has_key?(emoji, "static_url")
1726 assert Map.has_key?(emoji, "tags")
1727 assert is_list(emoji["tags"])
1728 assert Map.has_key?(emoji, "category")
1729 assert Map.has_key?(emoji, "url")
1730 assert Map.has_key?(emoji, "visible_in_picker")
1734 describe "index/2 redirections" do
1735 setup %{conn: conn} do
1739 signing_salt: "cooldude"
1744 |> Plug.Session.call(Plug.Session.init(session_opts))
1747 test_path = "/web/statuses/test"
1748 %{conn: conn, path: test_path}
1751 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
1752 conn = get(conn, path)
1754 assert conn.status == 302
1755 assert redirected_to(conn) == "/web/login"
1758 test "redirects not logged-in users to the login page on private instances", %{
1762 Config.put([:instance, :public], false)
1764 conn = get(conn, path)
1766 assert conn.status == 302
1767 assert redirected_to(conn) == "/web/login"
1770 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
1771 token = insert(:oauth_token)
1775 |> assign(:user, token.user)
1776 |> put_session(:oauth_token, token.token)
1779 assert conn.status == 200
1782 test "saves referer path to session", %{conn: conn, path: path} do
1783 conn = get(conn, path)
1784 return_to = Plug.Conn.get_session(conn, :return_to)
1786 assert return_to == path
1789 test "redirects to the saved path after log in", %{conn: conn, path: path} do
1790 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1791 auth = insert(:oauth_authorization, app: app)
1795 |> put_session(:return_to, path)
1796 |> get("/web/login", %{code: auth.token})
1798 assert conn.status == 302
1799 assert redirected_to(conn) == path
1802 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
1803 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1804 auth = insert(:oauth_authorization, app: app)
1806 conn = get(conn, "/web/login", %{code: auth.token})
1808 assert conn.status == 302
1809 assert redirected_to(conn) == "/web/getting-started"
1813 describe "scheduled activities" do
1814 test "creates a scheduled activity", %{conn: conn} do
1815 user = insert(:user)
1816 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1820 |> assign(:user, user)
1821 |> post("/api/v1/statuses", %{
1822 "status" => "scheduled",
1823 "scheduled_at" => scheduled_at
1826 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
1827 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
1828 assert [] == Repo.all(Activity)
1831 test "creates a scheduled activity with a media attachment", %{conn: conn} do
1832 user = insert(:user)
1833 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1835 file = %Plug.Upload{
1836 content_type: "image/jpg",
1837 path: Path.absname("test/fixtures/image.jpg"),
1838 filename: "an_image.jpg"
1841 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
1845 |> assign(:user, user)
1846 |> post("/api/v1/statuses", %{
1847 "media_ids" => [to_string(upload.id)],
1848 "status" => "scheduled",
1849 "scheduled_at" => scheduled_at
1852 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
1853 assert %{"type" => "image"} = media_attachment
1856 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
1858 user = insert(:user)
1861 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
1865 |> assign(:user, user)
1866 |> post("/api/v1/statuses", %{
1867 "status" => "not scheduled",
1868 "scheduled_at" => scheduled_at
1871 assert %{"content" => "not scheduled"} = json_response(conn, 200)
1872 assert [] == Repo.all(ScheduledActivity)
1875 test "returns error when daily user limit is exceeded", %{conn: conn} do
1876 user = insert(:user)
1879 NaiveDateTime.utc_now()
1880 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
1881 |> NaiveDateTime.to_iso8601()
1883 attrs = %{params: %{}, scheduled_at: today}
1884 {:ok, _} = ScheduledActivity.create(user, attrs)
1885 {:ok, _} = ScheduledActivity.create(user, attrs)
1889 |> assign(:user, user)
1890 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
1892 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
1895 test "returns error when total user limit is exceeded", %{conn: conn} do
1896 user = insert(:user)
1899 NaiveDateTime.utc_now()
1900 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
1901 |> NaiveDateTime.to_iso8601()
1904 NaiveDateTime.utc_now()
1905 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
1906 |> NaiveDateTime.to_iso8601()
1908 attrs = %{params: %{}, scheduled_at: today}
1909 {:ok, _} = ScheduledActivity.create(user, attrs)
1910 {:ok, _} = ScheduledActivity.create(user, attrs)
1911 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
1915 |> assign(:user, user)
1916 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
1918 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
1921 test "shows scheduled activities", %{conn: conn} do
1922 user = insert(:user)
1923 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
1924 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
1925 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
1926 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
1930 |> assign(:user, user)
1935 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
1937 result = json_response(conn_res, 200)
1938 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
1943 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
1945 result = json_response(conn_res, 200)
1946 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
1951 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
1953 result = json_response(conn_res, 200)
1954 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
1957 test "shows a scheduled activity", %{conn: conn} do
1958 user = insert(:user)
1959 scheduled_activity = insert(:scheduled_activity, user: user)
1963 |> assign(:user, user)
1964 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1966 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
1967 assert scheduled_activity_id == scheduled_activity.id |> to_string()
1971 |> assign(:user, user)
1972 |> get("/api/v1/scheduled_statuses/404")
1974 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1977 test "updates a scheduled activity", %{conn: conn} do
1978 user = insert(:user)
1979 scheduled_activity = insert(:scheduled_activity, user: user)
1982 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1986 |> assign(:user, user)
1987 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
1988 scheduled_at: new_scheduled_at
1991 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
1992 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
1996 |> assign(:user, user)
1997 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
1999 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2002 test "deletes a scheduled activity", %{conn: conn} do
2003 user = insert(:user)
2004 scheduled_activity = insert(:scheduled_activity, user: user)
2008 |> assign(:user, user)
2009 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2011 assert %{} = json_response(res_conn, 200)
2012 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
2016 |> assign(:user, user)
2017 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2019 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2023 describe "create account by app" do
2024 test "Account registration via Application", %{conn: conn} do
2027 |> post("/api/v1/apps", %{
2028 client_name: "client_name",
2029 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
2030 scopes: "read, write, follow"
2034 "client_id" => client_id,
2035 "client_secret" => client_secret,
2037 "name" => "client_name",
2038 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
2041 } = json_response(conn, 200)
2045 |> post("/oauth/token", %{
2046 grant_type: "client_credentials",
2047 client_id: client_id,
2048 client_secret: client_secret
2051 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
2052 json_response(conn, 200)
2055 token_from_db = Repo.get_by(Token, token: token)
2056 assert token_from_db
2058 assert scope == "read write follow"
2062 |> put_req_header("authorization", "Bearer " <> token)
2063 |> post("/api/v1/accounts", %{
2065 email: "lain@example.org",
2066 password: "PlzDontHackLain",
2071 "access_token" => token,
2072 "created_at" => _created_at,
2074 "token_type" => "Bearer"
2075 } = json_response(conn, 200)
2077 token_from_db = Repo.get_by(Token, token: token)
2078 assert token_from_db
2079 token_from_db = Repo.preload(token_from_db, :user)
2080 assert token_from_db.user
2082 assert token_from_db.user.info.confirmation_pending
2085 test "rate limit", %{conn: conn} do
2086 app_token = insert(:oauth_token, user: nil)
2089 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
2090 |> Map.put(:remote_ip, {15, 15, 15, 15})
2095 |> post("/api/v1/accounts", %{
2096 username: "#{i}lain",
2097 email: "#{i}lain@example.org",
2098 password: "PlzDontHackLain",
2103 "access_token" => token,
2104 "created_at" => _created_at,
2106 "token_type" => "Bearer"
2107 } = json_response(conn, 200)
2109 token_from_db = Repo.get_by(Token, token: token)
2110 assert token_from_db
2111 token_from_db = Repo.preload(token_from_db, :user)
2112 assert token_from_db.user
2114 assert token_from_db.user.info.confirmation_pending
2119 |> post("/api/v1/accounts", %{
2121 email: "6lain@example.org",
2122 password: "PlzDontHackLain",
2126 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
2130 describe "GET /api/v1/polls/:id" do
2131 test "returns poll entity for object id", %{conn: conn} do
2132 user = insert(:user)
2135 CommonAPI.post(user, %{
2136 "status" => "Pleroma does",
2137 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
2140 object = Object.normalize(activity)
2144 |> assign(:user, user)
2145 |> get("/api/v1/polls/#{object.id}")
2147 response = json_response(conn, 200)
2148 id = to_string(object.id)
2149 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
2152 test "does not expose polls for private statuses", %{conn: conn} do
2153 user = insert(:user)
2154 other_user = insert(:user)
2157 CommonAPI.post(user, %{
2158 "status" => "Pleroma does",
2159 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
2160 "visibility" => "private"
2163 object = Object.normalize(activity)
2167 |> assign(:user, other_user)
2168 |> get("/api/v1/polls/#{object.id}")
2170 assert json_response(conn, 404)
2174 describe "POST /api/v1/polls/:id/votes" do
2175 test "votes are added to the poll", %{conn: conn} do
2176 user = insert(:user)
2177 other_user = insert(:user)
2180 CommonAPI.post(user, %{
2181 "status" => "A very delicious sandwich",
2183 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
2189 object = Object.normalize(activity)
2193 |> assign(:user, other_user)
2194 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
2196 assert json_response(conn, 200)
2197 object = Object.get_by_id(object.id)
2199 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
2204 test "author can't vote", %{conn: conn} do
2205 user = insert(:user)
2208 CommonAPI.post(user, %{
2209 "status" => "Am I cute?",
2210 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
2213 object = Object.normalize(activity)
2216 |> assign(:user, user)
2217 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
2218 |> json_response(422) == %{"error" => "Poll's author can't vote"}
2220 object = Object.get_by_id(object.id)
2222 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
2225 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
2226 user = insert(:user)
2227 other_user = insert(:user)
2230 CommonAPI.post(user, %{
2231 "status" => "The glass is",
2232 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
2235 object = Object.normalize(activity)
2238 |> assign(:user, other_user)
2239 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
2240 |> json_response(422) == %{"error" => "Too many choices"}
2242 object = Object.get_by_id(object.id)
2244 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
2249 test "does not allow choice index to be greater than options count", %{conn: conn} do
2250 user = insert(:user)
2251 other_user = insert(:user)
2254 CommonAPI.post(user, %{
2255 "status" => "Am I cute?",
2256 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
2259 object = Object.normalize(activity)
2263 |> assign(:user, other_user)
2264 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
2266 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
2269 test "returns 404 error when object is not exist", %{conn: conn} do
2270 user = insert(:user)
2274 |> assign(:user, user)
2275 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
2277 assert json_response(conn, 404) == %{"error" => "Record not found"}
2280 test "returns 404 when poll is private and not available for user", %{conn: conn} do
2281 user = insert(:user)
2282 other_user = insert(:user)
2285 CommonAPI.post(user, %{
2286 "status" => "Am I cute?",
2287 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
2288 "visibility" => "private"
2291 object = Object.normalize(activity)
2295 |> assign(:user, other_user)
2296 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
2298 assert json_response(conn, 404) == %{"error" => "Record not found"}
2302 describe "POST /auth/password, with valid parameters" do
2303 setup %{conn: conn} do
2304 user = insert(:user)
2305 conn = post(conn, "/auth/password?email=#{user.email}")
2306 %{conn: conn, user: user}
2309 test "it returns 204", %{conn: conn} do
2310 assert json_response(conn, :no_content)
2313 test "it creates a PasswordResetToken record for user", %{user: user} do
2314 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
2318 test "it sends an email to user", %{user: user} do
2319 ObanHelpers.perform_all()
2320 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
2322 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
2323 notify_email = Config.get([:instance, :notify_email])
2324 instance_name = Config.get([:instance, :name])
2327 from: {instance_name, notify_email},
2328 to: {user.name, user.email},
2329 html_body: email.html_body
2334 describe "POST /auth/password, with invalid parameters" do
2336 user = insert(:user)
2340 test "it returns 404 when user is not found", %{conn: conn, user: user} do
2341 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
2342 assert conn.status == 404
2343 assert conn.resp_body == ""
2346 test "it returns 400 when user is not local", %{conn: conn, user: user} do
2347 {:ok, user} = Repo.update(Changeset.change(user, local: false))
2348 conn = post(conn, "/auth/password?email=#{user.email}")
2349 assert conn.status == 400
2350 assert conn.resp_body == ""
2354 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
2358 |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
2361 assert user.info.confirmation_pending
2366 clear_config([:instance, :account_activation_required]) do
2367 Config.put([:instance, :account_activation_required], true)
2370 test "resend account confirmation email", %{conn: conn, user: user} do
2372 |> assign(:user, user)
2373 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
2374 |> json_response(:no_content)
2376 ObanHelpers.perform_all()
2378 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
2379 notify_email = Config.get([:instance, :notify_email])
2380 instance_name = Config.get([:instance, :name])
2383 from: {instance_name, notify_email},
2384 to: {user.name, user.email},
2385 html_body: email.html_body
2390 describe "GET /api/v1/suggestions" do
2392 user = insert(:user)
2393 other_user = insert(:user)
2394 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
2395 url500 = "http://test500?#{host}&#{user.nickname}"
2396 url200 = "http://test200?#{host}&#{user.nickname}"
2399 %{method: :get, url: ^url500} ->
2400 %Tesla.Env{status: 500, body: "bad request"}
2402 %{method: :get, url: ^url200} ->
2406 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
2408 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
2412 [user: user, other_user: other_user]
2415 clear_config(:suggestions)
2417 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
2418 Config.put([:suggestions, :enabled], false)
2422 |> assign(:user, user)
2423 |> get("/api/v1/suggestions")
2424 |> json_response(200)
2429 test "returns error", %{conn: conn, user: user} do
2430 Config.put([:suggestions, :enabled], true)
2431 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
2433 assert capture_log(fn ->
2436 |> assign(:user, user)
2437 |> get("/api/v1/suggestions")
2438 |> json_response(500)
2440 assert res == "Something went wrong"
2441 end) =~ "Could not retrieve suggestions"
2444 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
2445 Config.put([:suggestions, :enabled], true)
2446 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
2450 |> assign(:user, user)
2451 |> get("/api/v1/suggestions")
2452 |> json_response(200)
2457 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
2458 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
2462 "acct" => other_user.ap_id,
2463 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
2464 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
2465 "id" => other_user.id