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 "verify_credentials", %{conn: conn} do
574 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
578 |> assign(:user, user)
579 |> get("/api/v1/accounts/verify_credentials")
581 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
582 assert id == to_string(user.id)
586 describe "account fetching" do
587 test "works by id" do
592 |> get("/api/v1/accounts/#{user.id}")
594 assert %{"id" => id} = json_response(conn, 200)
595 assert id == to_string(user.id)
599 |> get("/api/v1/accounts/-1")
601 assert %{"error" => "Can't find user"} = json_response(conn, 404)
604 test "works by nickname" do
609 |> get("/api/v1/accounts/#{user.nickname}")
611 assert %{"id" => id} = json_response(conn, 200)
615 test "works by nickname for remote users" do
616 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
617 Pleroma.Config.put([:instance, :limit_to_local_content], false)
618 user = insert(:user, nickname: "user@example.com", local: false)
622 |> get("/api/v1/accounts/#{user.nickname}")
624 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
625 assert %{"id" => id} = json_response(conn, 200)
629 test "respects limit_to_local_content == :all for remote user nicknames" do
630 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
631 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
633 user = insert(:user, nickname: "user@example.com", local: false)
637 |> get("/api/v1/accounts/#{user.nickname}")
639 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
640 assert json_response(conn, 404)
643 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
644 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
645 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
647 user = insert(:user, nickname: "user@example.com", local: false)
648 reading_user = insert(:user)
652 |> get("/api/v1/accounts/#{user.nickname}")
654 assert json_response(conn, 404)
658 |> assign(:user, reading_user)
659 |> get("/api/v1/accounts/#{user.nickname}")
661 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
662 assert %{"id" => id} = json_response(conn, 200)
667 test "mascot upload", %{conn: conn} do
670 non_image_file = %Plug.Upload{
671 content_type: "audio/mpeg",
672 path: Path.absname("test/fixtures/sound.mp3"),
673 filename: "sound.mp3"
678 |> assign(:user, user)
679 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
681 assert json_response(conn, 415)
684 content_type: "image/jpg",
685 path: Path.absname("test/fixtures/image.jpg"),
686 filename: "an_image.jpg"
691 |> assign(:user, user)
692 |> put("/api/v1/pleroma/mascot", %{"file" => file})
694 assert %{"id" => _, "type" => image} = json_response(conn, 200)
697 test "mascot retrieving", %{conn: conn} do
699 # When user hasn't set a mascot, we should just get pleroma tan back
702 |> assign(:user, user)
703 |> get("/api/v1/pleroma/mascot")
705 assert %{"url" => url} = json_response(conn, 200)
706 assert url =~ "pleroma-fox-tan-smol"
708 # When a user sets their mascot, we should get that back
710 content_type: "image/jpg",
711 path: Path.absname("test/fixtures/image.jpg"),
712 filename: "an_image.jpg"
717 |> assign(:user, user)
718 |> put("/api/v1/pleroma/mascot", %{"file" => file})
720 assert json_response(conn, 200)
722 user = User.get_cached_by_id(user.id)
726 |> assign(:user, user)
727 |> get("/api/v1/pleroma/mascot")
729 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
730 assert url =~ "an_image"
733 test "getting followers", %{conn: conn} do
735 other_user = insert(:user)
736 {:ok, user} = User.follow(user, other_user)
740 |> get("/api/v1/accounts/#{other_user.id}/followers")
742 assert [%{"id" => id}] = json_response(conn, 200)
743 assert id == to_string(user.id)
746 test "getting followers, hide_followers", %{conn: conn} do
748 other_user = insert(:user, %{info: %{hide_followers: true}})
749 {:ok, _user} = User.follow(user, other_user)
753 |> get("/api/v1/accounts/#{other_user.id}/followers")
755 assert [] == json_response(conn, 200)
758 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
760 other_user = insert(:user, %{info: %{hide_followers: true}})
761 {:ok, _user} = User.follow(user, other_user)
765 |> assign(:user, other_user)
766 |> get("/api/v1/accounts/#{other_user.id}/followers")
768 refute [] == json_response(conn, 200)
771 test "getting followers, pagination", %{conn: conn} do
773 follower1 = insert(:user)
774 follower2 = insert(:user)
775 follower3 = insert(:user)
776 {:ok, _} = User.follow(follower1, user)
777 {:ok, _} = User.follow(follower2, user)
778 {:ok, _} = User.follow(follower3, user)
782 |> assign(:user, user)
786 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
788 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
789 assert id3 == follower3.id
790 assert id2 == follower2.id
794 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
796 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
797 assert id2 == follower2.id
798 assert id1 == follower1.id
802 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
804 assert [%{"id" => id2}] = json_response(res_conn, 200)
805 assert id2 == follower2.id
807 assert [link_header] = get_resp_header(res_conn, "link")
808 assert link_header =~ ~r/min_id=#{follower2.id}/
809 assert link_header =~ ~r/max_id=#{follower2.id}/
812 test "getting following", %{conn: conn} do
814 other_user = insert(:user)
815 {:ok, user} = User.follow(user, other_user)
819 |> get("/api/v1/accounts/#{user.id}/following")
821 assert [%{"id" => id}] = json_response(conn, 200)
822 assert id == to_string(other_user.id)
825 test "getting following, hide_follows", %{conn: conn} do
826 user = insert(:user, %{info: %{hide_follows: true}})
827 other_user = insert(:user)
828 {:ok, user} = User.follow(user, other_user)
832 |> get("/api/v1/accounts/#{user.id}/following")
834 assert [] == json_response(conn, 200)
837 test "getting following, hide_follows, same user requesting", %{conn: conn} do
838 user = insert(:user, %{info: %{hide_follows: true}})
839 other_user = insert(:user)
840 {:ok, user} = User.follow(user, other_user)
844 |> assign(:user, user)
845 |> get("/api/v1/accounts/#{user.id}/following")
847 refute [] == json_response(conn, 200)
850 test "getting following, pagination", %{conn: conn} do
852 following1 = insert(:user)
853 following2 = insert(:user)
854 following3 = insert(:user)
855 {:ok, _} = User.follow(user, following1)
856 {:ok, _} = User.follow(user, following2)
857 {:ok, _} = User.follow(user, following3)
861 |> assign(:user, user)
865 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
867 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
868 assert id3 == following3.id
869 assert id2 == following2.id
873 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
875 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
876 assert id2 == following2.id
877 assert id1 == following1.id
881 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
883 assert [%{"id" => id2}] = json_response(res_conn, 200)
884 assert id2 == following2.id
886 assert [link_header] = get_resp_header(res_conn, "link")
887 assert link_header =~ ~r/min_id=#{following2.id}/
888 assert link_header =~ ~r/max_id=#{following2.id}/
891 test "following / unfollowing a user", %{conn: conn} do
893 other_user = insert(:user)
897 |> assign(:user, user)
898 |> post("/api/v1/accounts/#{other_user.id}/follow")
900 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
902 user = User.get_cached_by_id(user.id)
906 |> assign(:user, user)
907 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
909 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
911 user = User.get_cached_by_id(user.id)
915 |> assign(:user, user)
916 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
918 assert %{"id" => id} = json_response(conn, 200)
919 assert id == to_string(other_user.id)
922 test "following without reblogs" do
923 follower = insert(:user)
924 followed = insert(:user)
925 other_user = insert(:user)
929 |> assign(:user, follower)
930 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
932 assert %{"showing_reblogs" => false} = json_response(conn, 200)
934 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
935 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
939 |> assign(:user, User.get_cached_by_id(follower.id))
940 |> get("/api/v1/timelines/home")
942 assert [] == json_response(conn, 200)
946 |> assign(:user, follower)
947 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
949 assert %{"showing_reblogs" => true} = json_response(conn, 200)
953 |> assign(:user, User.get_cached_by_id(follower.id))
954 |> get("/api/v1/timelines/home")
956 expected_activity_id = reblog.id
957 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
960 test "following / unfollowing errors" do
965 |> assign(:user, user)
968 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
969 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
972 user = User.get_cached_by_id(user.id)
973 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
974 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
976 # self follow via uri
977 user = User.get_cached_by_id(user.id)
978 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
979 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
981 # follow non existing user
982 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
983 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
985 # follow non existing user via uri
986 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
987 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
989 # unfollow non existing user
990 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
991 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
994 describe "mute/unmute" do
995 test "with notifications", %{conn: conn} do
997 other_user = insert(:user)
1001 |> assign(:user, user)
1002 |> post("/api/v1/accounts/#{other_user.id}/mute")
1004 response = json_response(conn, 200)
1006 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
1007 user = User.get_cached_by_id(user.id)
1011 |> assign(:user, user)
1012 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1014 response = json_response(conn, 200)
1015 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
1018 test "without notifications", %{conn: conn} do
1019 user = insert(:user)
1020 other_user = insert(:user)
1024 |> assign(:user, user)
1025 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
1027 response = json_response(conn, 200)
1029 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
1030 user = User.get_cached_by_id(user.id)
1034 |> assign(:user, user)
1035 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1037 response = json_response(conn, 200)
1038 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
1042 test "subscribing / unsubscribing to a user", %{conn: conn} do
1043 user = insert(:user)
1044 subscription_target = insert(:user)
1048 |> assign(:user, user)
1049 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1051 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1055 |> assign(:user, user)
1056 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1058 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1061 test "getting a list of mutes", %{conn: conn} do
1062 user = insert(:user)
1063 other_user = insert(:user)
1065 {:ok, user} = User.mute(user, other_user)
1069 |> assign(:user, user)
1070 |> get("/api/v1/mutes")
1072 other_user_id = to_string(other_user.id)
1073 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1076 test "blocking / unblocking a user", %{conn: conn} do
1077 user = insert(:user)
1078 other_user = insert(:user)
1082 |> assign(:user, user)
1083 |> post("/api/v1/accounts/#{other_user.id}/block")
1085 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1087 user = User.get_cached_by_id(user.id)
1091 |> assign(:user, user)
1092 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1094 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1097 test "getting a list of blocks", %{conn: conn} do
1098 user = insert(:user)
1099 other_user = insert(:user)
1101 {:ok, user} = User.block(user, other_user)
1105 |> assign(:user, user)
1106 |> get("/api/v1/blocks")
1108 other_user_id = to_string(other_user.id)
1109 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1112 test "blocking / unblocking a domain", %{conn: conn} do
1113 user = insert(:user)
1114 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1118 |> assign(:user, user)
1119 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1121 assert %{} = json_response(conn, 200)
1122 user = User.get_cached_by_ap_id(user.ap_id)
1123 assert User.blocks?(user, other_user)
1127 |> assign(:user, user)
1128 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1130 assert %{} = json_response(conn, 200)
1131 user = User.get_cached_by_ap_id(user.ap_id)
1132 refute User.blocks?(user, other_user)
1135 test "getting a list of domain blocks", %{conn: conn} do
1136 user = insert(:user)
1138 {:ok, user} = User.block_domain(user, "bad.site")
1139 {:ok, user} = User.block_domain(user, "even.worse.site")
1143 |> assign(:user, user)
1144 |> get("/api/v1/domain_blocks")
1146 domain_blocks = json_response(conn, 200)
1148 assert "bad.site" in domain_blocks
1149 assert "even.worse.site" in domain_blocks
1152 test "unimplemented follow_requests, blocks, domain blocks" do
1153 user = insert(:user)
1155 ["blocks", "domain_blocks", "follow_requests"]
1156 |> Enum.each(fn endpoint ->
1159 |> assign(:user, user)
1160 |> get("/api/v1/#{endpoint}")
1162 assert [] = json_response(conn, 200)
1166 test "returns the favorites of a user", %{conn: conn} do
1167 user = insert(:user)
1168 other_user = insert(:user)
1170 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1171 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1173 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1177 |> assign(:user, user)
1178 |> get("/api/v1/favourites")
1180 assert [status] = json_response(first_conn, 200)
1181 assert status["id"] == to_string(activity.id)
1183 assert [{"link", _link_header}] =
1184 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1186 # Honours query params
1187 {:ok, second_activity} =
1188 CommonAPI.post(other_user, %{
1190 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1193 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1195 last_like = status["id"]
1199 |> assign(:user, user)
1200 |> get("/api/v1/favourites?since_id=#{last_like}")
1202 assert [second_status] = json_response(second_conn, 200)
1203 assert second_status["id"] == to_string(second_activity.id)
1207 |> assign(:user, user)
1208 |> get("/api/v1/favourites?limit=0")
1210 assert [] = json_response(third_conn, 200)
1213 describe "getting favorites timeline of specified user" do
1215 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
1216 [current_user: current_user, user: user]
1219 test "returns list of statuses favorited by specified user", %{
1221 current_user: current_user,
1224 [activity | _] = insert_pair(:note_activity)
1225 CommonAPI.favorite(activity.id, user)
1229 |> assign(:user, current_user)
1230 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1231 |> json_response(:ok)
1235 assert length(response) == 1
1236 assert like["id"] == activity.id
1239 test "returns favorites for specified user_id when user is not logged in", %{
1243 activity = insert(:note_activity)
1244 CommonAPI.favorite(activity.id, user)
1248 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1249 |> json_response(:ok)
1251 assert length(response) == 1
1254 test "returns favorited DM only when user is logged in and he is one of recipients", %{
1256 current_user: current_user,
1260 CommonAPI.post(current_user, %{
1261 "status" => "Hi @#{user.nickname}!",
1262 "visibility" => "direct"
1265 CommonAPI.favorite(direct.id, user)
1269 |> assign(:user, current_user)
1270 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1271 |> json_response(:ok)
1273 assert length(response) == 1
1275 anonymous_response =
1277 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1278 |> json_response(:ok)
1280 assert Enum.empty?(anonymous_response)
1283 test "does not return others' favorited DM when user is not one of recipients", %{
1285 current_user: current_user,
1288 user_two = insert(:user)
1291 CommonAPI.post(user_two, %{
1292 "status" => "Hi @#{user.nickname}!",
1293 "visibility" => "direct"
1296 CommonAPI.favorite(direct.id, user)
1300 |> assign(:user, current_user)
1301 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1302 |> json_response(:ok)
1304 assert Enum.empty?(response)
1307 test "paginates favorites using since_id and max_id", %{
1309 current_user: current_user,
1312 activities = insert_list(10, :note_activity)
1314 Enum.each(activities, fn activity ->
1315 CommonAPI.favorite(activity.id, user)
1318 third_activity = Enum.at(activities, 2)
1319 seventh_activity = Enum.at(activities, 6)
1323 |> assign(:user, current_user)
1324 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
1325 since_id: third_activity.id,
1326 max_id: seventh_activity.id
1328 |> json_response(:ok)
1330 assert length(response) == 3
1331 refute third_activity in response
1332 refute seventh_activity in response
1335 test "limits favorites using limit parameter", %{
1337 current_user: current_user,
1341 |> insert_list(:note_activity)
1342 |> Enum.each(fn activity ->
1343 CommonAPI.favorite(activity.id, user)
1348 |> assign(:user, current_user)
1349 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
1350 |> json_response(:ok)
1352 assert length(response) == 3
1355 test "returns empty response when user does not have any favorited statuses", %{
1357 current_user: current_user,
1362 |> assign(:user, current_user)
1363 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1364 |> json_response(:ok)
1366 assert Enum.empty?(response)
1369 test "returns 404 error when specified user is not exist", %{conn: conn} do
1370 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
1372 assert json_response(conn, 404) == %{"error" => "Record not found"}
1375 test "returns 403 error when user has hidden own favorites", %{
1377 current_user: current_user
1379 user = insert(:user, %{info: %{hide_favorites: true}})
1380 activity = insert(:note_activity)
1381 CommonAPI.favorite(activity.id, user)
1385 |> assign(:user, current_user)
1386 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1388 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1391 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
1392 user = insert(:user)
1393 activity = insert(:note_activity)
1394 CommonAPI.favorite(activity.id, user)
1398 |> assign(:user, current_user)
1399 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1401 assert user.info.hide_favorites
1402 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1406 test "get instance information", %{conn: conn} do
1407 conn = get(conn, "/api/v1/instance")
1408 assert result = json_response(conn, 200)
1410 email = Config.get([:instance, :email])
1411 # Note: not checking for "max_toot_chars" since it's optional
1417 "email" => from_config_email,
1419 "streaming_api" => _
1424 "registrations" => _,
1428 assert email == from_config_email
1431 test "get instance stats", %{conn: conn} do
1432 user = insert(:user, %{local: true})
1434 user2 = insert(:user, %{local: true})
1435 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1437 insert(:user, %{local: false, nickname: "u@peer1.com"})
1438 insert(:user, %{local: false, nickname: "u@peer2.com"})
1440 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
1442 # Stats should count users with missing or nil `info.deactivated` value
1446 |> User.get_cached_by_id()
1447 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
1449 Pleroma.Stats.force_update()
1451 conn = get(conn, "/api/v1/instance")
1453 assert result = json_response(conn, 200)
1455 stats = result["stats"]
1458 assert stats["user_count"] == 1
1459 assert stats["status_count"] == 1
1460 assert stats["domain_count"] == 2
1463 test "get peers", %{conn: conn} do
1464 insert(:user, %{local: false, nickname: "u@peer1.com"})
1465 insert(:user, %{local: false, nickname: "u@peer2.com"})
1467 Pleroma.Stats.force_update()
1469 conn = get(conn, "/api/v1/instance/peers")
1471 assert result = json_response(conn, 200)
1473 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
1476 test "put settings", %{conn: conn} do
1477 user = insert(:user)
1481 |> assign(:user, user)
1482 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
1484 assert _result = json_response(conn, 200)
1486 user = User.get_cached_by_ap_id(user.ap_id)
1487 assert user.info.settings == %{"programming" => "socks"}
1490 describe "pinned statuses" do
1492 user = insert(:user)
1493 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
1495 [user: user, activity: activity]
1498 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
1499 {:ok, _} = CommonAPI.pin(activity.id, user)
1503 |> assign(:user, user)
1504 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1505 |> json_response(200)
1507 id_str = to_string(activity.id)
1509 assert [%{"id" => ^id_str, "pinned" => true}] = result
1513 describe "reports" do
1515 reporter = insert(:user)
1516 target_user = insert(:user)
1518 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
1520 [reporter: reporter, target_user: target_user, activity: activity]
1523 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
1524 assert %{"action_taken" => false, "id" => _} =
1526 |> assign(:user, reporter)
1527 |> post("/api/v1/reports", %{"account_id" => target_user.id})
1528 |> json_response(200)
1531 test "submit a report with statuses and comment", %{
1534 target_user: target_user,
1537 assert %{"action_taken" => false, "id" => _} =
1539 |> assign(:user, reporter)
1540 |> post("/api/v1/reports", %{
1541 "account_id" => target_user.id,
1542 "status_ids" => [activity.id],
1543 "comment" => "bad status!",
1544 "forward" => "false"
1546 |> json_response(200)
1549 test "account_id is required", %{
1554 assert %{"error" => "Valid `account_id` required"} =
1556 |> assign(:user, reporter)
1557 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
1558 |> json_response(400)
1561 test "comment must be up to the size specified in the config", %{
1564 target_user: target_user
1566 max_size = Config.get([:instance, :max_report_comment_size], 1000)
1567 comment = String.pad_trailing("a", max_size + 1, "a")
1569 error = %{"error" => "Comment must be up to #{max_size} characters"}
1573 |> assign(:user, reporter)
1574 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
1575 |> json_response(400)
1578 test "returns error when account is not exist", %{
1585 |> assign(:user, reporter)
1586 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
1588 assert json_response(conn, 400) == %{"error" => "Account not found"}
1592 describe "link headers" do
1593 test "preserves parameters in link headers", %{conn: conn} do
1594 user = insert(:user)
1595 other_user = insert(:user)
1598 CommonAPI.post(other_user, %{
1599 "status" => "hi @#{user.nickname}",
1600 "visibility" => "public"
1604 CommonAPI.post(other_user, %{
1605 "status" => "hi @#{user.nickname}",
1606 "visibility" => "public"
1609 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
1610 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
1614 |> assign(:user, user)
1615 |> get("/api/v1/notifications", %{media_only: true})
1617 assert [link_header] = get_resp_header(conn, "link")
1618 assert link_header =~ ~r/media_only=true/
1619 assert link_header =~ ~r/min_id=#{notification2.id}/
1620 assert link_header =~ ~r/max_id=#{notification1.id}/
1624 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
1625 # Need to set an old-style integer ID to reproduce the problem
1626 # (these are no longer assigned to new accounts but were preserved
1627 # for existing accounts during the migration to flakeIDs)
1628 user_one = insert(:user, %{id: 1212})
1629 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
1633 |> get("/api/v1/accounts/#{user_one.id}")
1637 |> get("/api/v1/accounts/#{user_two.nickname}")
1641 |> get("/api/v1/accounts/#{user_two.id}")
1643 acc_one = json_response(resp_one, 200)
1644 acc_two = json_response(resp_two, 200)
1645 acc_three = json_response(resp_three, 200)
1646 refute acc_one == acc_two
1647 assert acc_two == acc_three
1650 describe "custom emoji" do
1651 test "with tags", %{conn: conn} do
1654 |> get("/api/v1/custom_emojis")
1655 |> json_response(200)
1657 assert Map.has_key?(emoji, "shortcode")
1658 assert Map.has_key?(emoji, "static_url")
1659 assert Map.has_key?(emoji, "tags")
1660 assert is_list(emoji["tags"])
1661 assert Map.has_key?(emoji, "category")
1662 assert Map.has_key?(emoji, "url")
1663 assert Map.has_key?(emoji, "visible_in_picker")
1667 describe "index/2 redirections" do
1668 setup %{conn: conn} do
1672 signing_salt: "cooldude"
1677 |> Plug.Session.call(Plug.Session.init(session_opts))
1680 test_path = "/web/statuses/test"
1681 %{conn: conn, path: test_path}
1684 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
1685 conn = get(conn, path)
1687 assert conn.status == 302
1688 assert redirected_to(conn) == "/web/login"
1691 test "redirects not logged-in users to the login page on private instances", %{
1695 Config.put([:instance, :public], false)
1697 conn = get(conn, path)
1699 assert conn.status == 302
1700 assert redirected_to(conn) == "/web/login"
1703 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
1704 token = insert(:oauth_token)
1708 |> assign(:user, token.user)
1709 |> put_session(:oauth_token, token.token)
1712 assert conn.status == 200
1715 test "saves referer path to session", %{conn: conn, path: path} do
1716 conn = get(conn, path)
1717 return_to = Plug.Conn.get_session(conn, :return_to)
1719 assert return_to == path
1722 test "redirects to the saved path after log in", %{conn: conn, path: path} do
1723 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1724 auth = insert(:oauth_authorization, app: app)
1728 |> put_session(:return_to, path)
1729 |> get("/web/login", %{code: auth.token})
1731 assert conn.status == 302
1732 assert redirected_to(conn) == path
1735 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
1736 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1737 auth = insert(:oauth_authorization, app: app)
1739 conn = get(conn, "/web/login", %{code: auth.token})
1741 assert conn.status == 302
1742 assert redirected_to(conn) == "/web/getting-started"
1746 describe "scheduled activities" do
1747 test "creates a scheduled activity", %{conn: conn} do
1748 user = insert(:user)
1749 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1753 |> assign(:user, user)
1754 |> post("/api/v1/statuses", %{
1755 "status" => "scheduled",
1756 "scheduled_at" => scheduled_at
1759 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
1760 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
1761 assert [] == Repo.all(Activity)
1764 test "creates a scheduled activity with a media attachment", %{conn: conn} do
1765 user = insert(:user)
1766 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1768 file = %Plug.Upload{
1769 content_type: "image/jpg",
1770 path: Path.absname("test/fixtures/image.jpg"),
1771 filename: "an_image.jpg"
1774 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
1778 |> assign(:user, user)
1779 |> post("/api/v1/statuses", %{
1780 "media_ids" => [to_string(upload.id)],
1781 "status" => "scheduled",
1782 "scheduled_at" => scheduled_at
1785 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
1786 assert %{"type" => "image"} = media_attachment
1789 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
1791 user = insert(:user)
1794 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
1798 |> assign(:user, user)
1799 |> post("/api/v1/statuses", %{
1800 "status" => "not scheduled",
1801 "scheduled_at" => scheduled_at
1804 assert %{"content" => "not scheduled"} = json_response(conn, 200)
1805 assert [] == Repo.all(ScheduledActivity)
1808 test "returns error when daily user limit is exceeded", %{conn: conn} do
1809 user = insert(:user)
1812 NaiveDateTime.utc_now()
1813 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
1814 |> NaiveDateTime.to_iso8601()
1816 attrs = %{params: %{}, scheduled_at: today}
1817 {:ok, _} = ScheduledActivity.create(user, attrs)
1818 {:ok, _} = ScheduledActivity.create(user, attrs)
1822 |> assign(:user, user)
1823 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
1825 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
1828 test "returns error when total user limit is exceeded", %{conn: conn} do
1829 user = insert(:user)
1832 NaiveDateTime.utc_now()
1833 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
1834 |> NaiveDateTime.to_iso8601()
1837 NaiveDateTime.utc_now()
1838 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
1839 |> NaiveDateTime.to_iso8601()
1841 attrs = %{params: %{}, scheduled_at: today}
1842 {:ok, _} = ScheduledActivity.create(user, attrs)
1843 {:ok, _} = ScheduledActivity.create(user, attrs)
1844 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
1848 |> assign(:user, user)
1849 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
1851 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
1854 test "shows scheduled activities", %{conn: conn} do
1855 user = insert(:user)
1856 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
1857 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
1858 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
1859 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
1863 |> assign(:user, user)
1868 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
1870 result = json_response(conn_res, 200)
1871 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
1876 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
1878 result = json_response(conn_res, 200)
1879 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
1884 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
1886 result = json_response(conn_res, 200)
1887 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
1890 test "shows a scheduled activity", %{conn: conn} do
1891 user = insert(:user)
1892 scheduled_activity = insert(:scheduled_activity, user: user)
1896 |> assign(:user, user)
1897 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1899 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
1900 assert scheduled_activity_id == scheduled_activity.id |> to_string()
1904 |> assign(:user, user)
1905 |> get("/api/v1/scheduled_statuses/404")
1907 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1910 test "updates a scheduled activity", %{conn: conn} do
1911 user = insert(:user)
1912 scheduled_activity = insert(:scheduled_activity, user: user)
1915 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1919 |> assign(:user, user)
1920 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
1921 scheduled_at: new_scheduled_at
1924 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
1925 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
1929 |> assign(:user, user)
1930 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
1932 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1935 test "deletes a scheduled activity", %{conn: conn} do
1936 user = insert(:user)
1937 scheduled_activity = insert(:scheduled_activity, user: user)
1941 |> assign(:user, user)
1942 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1944 assert %{} = json_response(res_conn, 200)
1945 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
1949 |> assign(:user, user)
1950 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1952 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1956 describe "create account by app" do
1957 test "Account registration via Application", %{conn: conn} do
1960 |> post("/api/v1/apps", %{
1961 client_name: "client_name",
1962 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1963 scopes: "read, write, follow"
1967 "client_id" => client_id,
1968 "client_secret" => client_secret,
1970 "name" => "client_name",
1971 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1974 } = json_response(conn, 200)
1978 |> post("/oauth/token", %{
1979 grant_type: "client_credentials",
1980 client_id: client_id,
1981 client_secret: client_secret
1984 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1985 json_response(conn, 200)
1988 token_from_db = Repo.get_by(Token, token: token)
1989 assert token_from_db
1991 assert scope == "read write follow"
1995 |> put_req_header("authorization", "Bearer " <> token)
1996 |> post("/api/v1/accounts", %{
1998 email: "lain@example.org",
1999 password: "PlzDontHackLain",
2004 "access_token" => token,
2005 "created_at" => _created_at,
2007 "token_type" => "Bearer"
2008 } = json_response(conn, 200)
2010 token_from_db = Repo.get_by(Token, token: token)
2011 assert token_from_db
2012 token_from_db = Repo.preload(token_from_db, :user)
2013 assert token_from_db.user
2015 assert token_from_db.user.info.confirmation_pending
2018 test "rate limit", %{conn: conn} do
2019 app_token = insert(:oauth_token, user: nil)
2022 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
2023 |> Map.put(:remote_ip, {15, 15, 15, 15})
2028 |> post("/api/v1/accounts", %{
2029 username: "#{i}lain",
2030 email: "#{i}lain@example.org",
2031 password: "PlzDontHackLain",
2036 "access_token" => token,
2037 "created_at" => _created_at,
2039 "token_type" => "Bearer"
2040 } = json_response(conn, 200)
2042 token_from_db = Repo.get_by(Token, token: token)
2043 assert token_from_db
2044 token_from_db = Repo.preload(token_from_db, :user)
2045 assert token_from_db.user
2047 assert token_from_db.user.info.confirmation_pending
2052 |> post("/api/v1/accounts", %{
2054 email: "6lain@example.org",
2055 password: "PlzDontHackLain",
2059 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
2063 describe "GET /api/v1/polls/:id" do
2064 test "returns poll entity for object id", %{conn: conn} do
2065 user = insert(:user)
2068 CommonAPI.post(user, %{
2069 "status" => "Pleroma does",
2070 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
2073 object = Object.normalize(activity)
2077 |> assign(:user, user)
2078 |> get("/api/v1/polls/#{object.id}")
2080 response = json_response(conn, 200)
2081 id = to_string(object.id)
2082 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
2085 test "does not expose polls for private statuses", %{conn: conn} do
2086 user = insert(:user)
2087 other_user = insert(:user)
2090 CommonAPI.post(user, %{
2091 "status" => "Pleroma does",
2092 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
2093 "visibility" => "private"
2096 object = Object.normalize(activity)
2100 |> assign(:user, other_user)
2101 |> get("/api/v1/polls/#{object.id}")
2103 assert json_response(conn, 404)
2107 describe "POST /api/v1/polls/:id/votes" do
2108 test "votes are added to the poll", %{conn: conn} do
2109 user = insert(:user)
2110 other_user = insert(:user)
2113 CommonAPI.post(user, %{
2114 "status" => "A very delicious sandwich",
2116 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
2122 object = Object.normalize(activity)
2126 |> assign(:user, other_user)
2127 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
2129 assert json_response(conn, 200)
2130 object = Object.get_by_id(object.id)
2132 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
2137 test "author can't vote", %{conn: conn} do
2138 user = insert(:user)
2141 CommonAPI.post(user, %{
2142 "status" => "Am I cute?",
2143 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
2146 object = Object.normalize(activity)
2149 |> assign(:user, user)
2150 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
2151 |> json_response(422) == %{"error" => "Poll's author can't vote"}
2153 object = Object.get_by_id(object.id)
2155 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
2158 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
2159 user = insert(:user)
2160 other_user = insert(:user)
2163 CommonAPI.post(user, %{
2164 "status" => "The glass is",
2165 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
2168 object = Object.normalize(activity)
2171 |> assign(:user, other_user)
2172 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
2173 |> json_response(422) == %{"error" => "Too many choices"}
2175 object = Object.get_by_id(object.id)
2177 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
2182 test "does not allow choice index to be greater than options count", %{conn: conn} do
2183 user = insert(:user)
2184 other_user = insert(:user)
2187 CommonAPI.post(user, %{
2188 "status" => "Am I cute?",
2189 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
2192 object = Object.normalize(activity)
2196 |> assign(:user, other_user)
2197 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
2199 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
2202 test "returns 404 error when object is not exist", %{conn: conn} do
2203 user = insert(:user)
2207 |> assign(:user, user)
2208 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
2210 assert json_response(conn, 404) == %{"error" => "Record not found"}
2213 test "returns 404 when poll is private and not available for user", %{conn: conn} do
2214 user = insert(:user)
2215 other_user = insert(:user)
2218 CommonAPI.post(user, %{
2219 "status" => "Am I cute?",
2220 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
2221 "visibility" => "private"
2224 object = Object.normalize(activity)
2228 |> assign(:user, other_user)
2229 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
2231 assert json_response(conn, 404) == %{"error" => "Record not found"}
2235 describe "POST /auth/password, with valid parameters" do
2236 setup %{conn: conn} do
2237 user = insert(:user)
2238 conn = post(conn, "/auth/password?email=#{user.email}")
2239 %{conn: conn, user: user}
2242 test "it returns 204", %{conn: conn} do
2243 assert json_response(conn, :no_content)
2246 test "it creates a PasswordResetToken record for user", %{user: user} do
2247 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
2251 test "it sends an email to user", %{user: user} do
2252 ObanHelpers.perform_all()
2253 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
2255 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
2256 notify_email = Config.get([:instance, :notify_email])
2257 instance_name = Config.get([:instance, :name])
2260 from: {instance_name, notify_email},
2261 to: {user.name, user.email},
2262 html_body: email.html_body
2267 describe "POST /auth/password, with invalid parameters" do
2269 user = insert(:user)
2273 test "it returns 404 when user is not found", %{conn: conn, user: user} do
2274 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
2275 assert conn.status == 404
2276 assert conn.resp_body == ""
2279 test "it returns 400 when user is not local", %{conn: conn, user: user} do
2280 {:ok, user} = Repo.update(Changeset.change(user, local: false))
2281 conn = post(conn, "/auth/password?email=#{user.email}")
2282 assert conn.status == 400
2283 assert conn.resp_body == ""
2287 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
2291 |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
2294 assert user.info.confirmation_pending
2299 clear_config([:instance, :account_activation_required]) do
2300 Config.put([:instance, :account_activation_required], true)
2303 test "resend account confirmation email", %{conn: conn, user: user} do
2305 |> assign(:user, user)
2306 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
2307 |> json_response(:no_content)
2309 ObanHelpers.perform_all()
2311 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
2312 notify_email = Config.get([:instance, :notify_email])
2313 instance_name = Config.get([:instance, :name])
2316 from: {instance_name, notify_email},
2317 to: {user.name, user.email},
2318 html_body: email.html_body
2323 describe "GET /api/v1/suggestions" do
2325 user = insert(:user)
2326 other_user = insert(:user)
2327 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
2328 url500 = "http://test500?#{host}&#{user.nickname}"
2329 url200 = "http://test200?#{host}&#{user.nickname}"
2332 %{method: :get, url: ^url500} ->
2333 %Tesla.Env{status: 500, body: "bad request"}
2335 %{method: :get, url: ^url200} ->
2339 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
2341 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
2345 [user: user, other_user: other_user]
2348 clear_config(:suggestions)
2350 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
2351 Config.put([:suggestions, :enabled], false)
2355 |> assign(:user, user)
2356 |> get("/api/v1/suggestions")
2357 |> json_response(200)
2362 test "returns error", %{conn: conn, user: user} do
2363 Config.put([:suggestions, :enabled], true)
2364 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
2366 assert capture_log(fn ->
2369 |> assign(:user, user)
2370 |> get("/api/v1/suggestions")
2371 |> json_response(500)
2373 assert res == "Something went wrong"
2374 end) =~ "Could not retrieve suggestions"
2377 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
2378 Config.put([:suggestions, :enabled], true)
2379 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
2383 |> assign(:user, user)
2384 |> get("/api/v1/suggestions")
2385 |> json_response(200)
2390 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
2391 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
2395 "acct" => other_user.ap_id,
2396 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
2397 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
2398 "id" => other_user.id