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.OAuth.App
20 alias Pleroma.Web.OAuth.Token
21 alias Pleroma.Web.Push
23 import ExUnit.CaptureLog
24 import Pleroma.Factory
25 import Swoosh.TestAssertions
28 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
31 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
35 clear_config([:instance, :public])
36 clear_config([:rich_media, :enabled])
38 test "Conversations", %{conn: conn} do
39 user_one = insert(:user)
40 user_two = insert(:user)
41 user_three = insert(:user)
43 {:ok, user_two} = User.follow(user_two, user_one)
46 CommonAPI.post(user_one, %{
47 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
48 "visibility" => "direct"
51 {:ok, _follower_only} =
52 CommonAPI.post(user_one, %{
53 "status" => "Hi @#{user_two.nickname}!",
54 "visibility" => "private"
59 |> assign(:user, user_one)
60 |> get("/api/v1/conversations")
62 assert response = json_response(res_conn, 200)
67 "accounts" => res_accounts,
68 "last_status" => res_last_status,
73 account_ids = Enum.map(res_accounts, & &1["id"])
74 assert length(res_accounts) == 2
75 assert user_two.id in account_ids
76 assert user_three.id in account_ids
77 assert is_binary(res_id)
79 assert res_last_status["id"] == direct.id
81 # Apparently undocumented API endpoint
84 |> assign(:user, user_one)
85 |> post("/api/v1/conversations/#{res_id}/read")
87 assert response = json_response(res_conn, 200)
88 assert length(response["accounts"]) == 2
89 assert response["last_status"]["id"] == direct.id
90 assert response["unread"] == false
92 # (vanilla) Mastodon frontend behaviour
95 |> assign(:user, user_one)
96 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
98 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
101 test "verify_credentials", %{conn: conn} do
106 |> assign(:user, user)
107 |> get("/api/v1/accounts/verify_credentials")
109 response = json_response(conn, 200)
111 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
112 assert response["pleroma"]["chat_token"]
113 assert id == to_string(user.id)
116 test "verify_credentials default scope unlisted", %{conn: conn} do
117 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
121 |> assign(:user, user)
122 |> get("/api/v1/accounts/verify_credentials")
124 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
125 assert id == to_string(user.id)
128 test "apps/verify_credentials", %{conn: conn} do
129 token = insert(:oauth_token)
133 |> assign(:user, token.user)
134 |> assign(:token, token)
135 |> get("/api/v1/apps/verify_credentials")
137 app = Repo.preload(token, :app).app
140 "name" => app.client_name,
141 "website" => app.website,
142 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
145 assert expected == json_response(conn, 200)
148 test "user avatar can be set", %{conn: conn} do
150 avatar_image = File.read!("test/fixtures/avatar_data_uri")
154 |> assign(:user, user)
155 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
157 user = refresh_record(user)
171 assert %{"url" => _} = json_response(conn, 200)
174 test "user avatar can be reset", %{conn: conn} do
179 |> assign(:user, user)
180 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
182 user = User.get_cached_by_id(user.id)
184 assert user.avatar == nil
186 assert %{"url" => nil} = json_response(conn, 200)
189 test "can set profile banner", %{conn: conn} do
194 |> assign(:user, user)
195 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
197 user = refresh_record(user)
198 assert user.info.banner["type"] == "Image"
200 assert %{"url" => _} = json_response(conn, 200)
203 test "can reset profile banner", %{conn: conn} do
208 |> assign(:user, user)
209 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
211 user = refresh_record(user)
212 assert user.info.banner == %{}
214 assert %{"url" => nil} = json_response(conn, 200)
217 test "background image can be set", %{conn: conn} do
222 |> assign(:user, user)
223 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
225 user = refresh_record(user)
226 assert user.info.background["type"] == "Image"
227 assert %{"url" => _} = json_response(conn, 200)
230 test "background image can be reset", %{conn: conn} do
235 |> assign(:user, user)
236 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
238 user = refresh_record(user)
239 assert user.info.background == %{}
240 assert %{"url" => nil} = json_response(conn, 200)
243 test "creates an oauth app", %{conn: conn} do
245 app_attrs = build(:oauth_app)
249 |> assign(:user, user)
250 |> post("/api/v1/apps", %{
251 client_name: app_attrs.client_name,
252 redirect_uris: app_attrs.redirect_uris
255 [app] = Repo.all(App)
258 "name" => app.client_name,
259 "website" => app.website,
260 "client_id" => app.client_id,
261 "client_secret" => app.client_secret,
262 "id" => app.id |> to_string(),
263 "redirect_uri" => app.redirect_uris,
264 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
267 assert expected == json_response(conn, 200)
270 describe "user timelines" do
271 test "gets a users statuses", %{conn: conn} do
272 user_one = insert(:user)
273 user_two = insert(:user)
274 user_three = insert(:user)
276 {:ok, user_three} = User.follow(user_three, user_one)
278 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
280 {:ok, direct_activity} =
281 CommonAPI.post(user_one, %{
282 "status" => "Hi, @#{user_two.nickname}.",
283 "visibility" => "direct"
286 {:ok, private_activity} =
287 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
291 |> get("/api/v1/accounts/#{user_one.id}/statuses")
293 assert [%{"id" => id}] = json_response(resp, 200)
294 assert id == to_string(activity.id)
298 |> assign(:user, user_two)
299 |> get("/api/v1/accounts/#{user_one.id}/statuses")
301 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
302 assert id_one == to_string(direct_activity.id)
303 assert id_two == to_string(activity.id)
307 |> assign(:user, user_three)
308 |> get("/api/v1/accounts/#{user_one.id}/statuses")
310 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
311 assert id_one == to_string(private_activity.id)
312 assert id_two == to_string(activity.id)
315 test "unimplemented pinned statuses feature", %{conn: conn} do
316 note = insert(:note_activity)
317 user = User.get_cached_by_ap_id(note.data["actor"])
321 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
323 assert json_response(conn, 200) == []
326 test "gets an users media", %{conn: conn} do
327 note = insert(:note_activity)
328 user = User.get_cached_by_ap_id(note.data["actor"])
331 content_type: "image/jpg",
332 path: Path.absname("test/fixtures/image.jpg"),
333 filename: "an_image.jpg"
336 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
338 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
342 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
344 assert [%{"id" => id}] = json_response(conn, 200)
345 assert id == to_string(image_post.id)
349 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
351 assert [%{"id" => id}] = json_response(conn, 200)
352 assert id == to_string(image_post.id)
355 test "gets a user's statuses without reblogs", %{conn: conn} do
357 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
358 {:ok, _, _} = CommonAPI.repeat(post.id, user)
362 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
364 assert [%{"id" => id}] = json_response(conn, 200)
365 assert id == to_string(post.id)
369 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
371 assert [%{"id" => id}] = json_response(conn, 200)
372 assert id == to_string(post.id)
375 test "filters user's statuses by a hashtag", %{conn: conn} do
377 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
378 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
382 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
384 assert [%{"id" => id}] = json_response(conn, 200)
385 assert id == to_string(post.id)
389 describe "user relationships" do
390 test "returns the relationships for the current user", %{conn: conn} do
392 other_user = insert(:user)
393 {:ok, user} = User.follow(user, other_user)
397 |> assign(:user, user)
398 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
400 assert [relationship] = json_response(conn, 200)
402 assert to_string(other_user.id) == relationship["id"]
406 describe "media upload" do
412 |> assign(:user, user)
414 image = %Plug.Upload{
415 content_type: "image/jpg",
416 path: Path.absname("test/fixtures/image.jpg"),
417 filename: "an_image.jpg"
420 [conn: conn, image: image]
423 clear_config([:media_proxy])
424 clear_config([Pleroma.Upload])
426 test "returns uploaded image", %{conn: conn, image: image} do
427 desc = "Description of the image"
431 |> post("/api/v1/media", %{"file" => image, "description" => desc})
432 |> json_response(:ok)
434 assert media["type"] == "image"
435 assert media["description"] == desc
438 object = Repo.get(Object, media["id"])
439 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
443 describe "locked accounts" do
444 test "/api/v1/follow_requests works" do
445 user = insert(:user, %{info: %User.Info{locked: true}})
446 other_user = insert(:user)
448 {:ok, _activity} = ActivityPub.follow(other_user, user)
450 user = User.get_cached_by_id(user.id)
451 other_user = User.get_cached_by_id(other_user.id)
453 assert User.following?(other_user, user) == false
457 |> assign(:user, user)
458 |> get("/api/v1/follow_requests")
460 assert [relationship] = json_response(conn, 200)
461 assert to_string(other_user.id) == relationship["id"]
464 test "/api/v1/follow_requests/:id/authorize works" do
465 user = insert(:user, %{info: %User.Info{locked: true}})
466 other_user = insert(:user)
468 {:ok, _activity} = ActivityPub.follow(other_user, user)
470 user = User.get_cached_by_id(user.id)
471 other_user = User.get_cached_by_id(other_user.id)
473 assert User.following?(other_user, user) == false
477 |> assign(:user, user)
478 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
480 assert relationship = json_response(conn, 200)
481 assert to_string(other_user.id) == relationship["id"]
483 user = User.get_cached_by_id(user.id)
484 other_user = User.get_cached_by_id(other_user.id)
486 assert User.following?(other_user, user) == true
489 test "verify_credentials", %{conn: conn} do
490 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
494 |> assign(:user, user)
495 |> get("/api/v1/accounts/verify_credentials")
497 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
498 assert id == to_string(user.id)
501 test "/api/v1/follow_requests/:id/reject works" do
502 user = insert(:user, %{info: %User.Info{locked: true}})
503 other_user = insert(:user)
505 {:ok, _activity} = ActivityPub.follow(other_user, user)
507 user = User.get_cached_by_id(user.id)
511 |> assign(:user, user)
512 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
514 assert relationship = json_response(conn, 200)
515 assert to_string(other_user.id) == relationship["id"]
517 user = User.get_cached_by_id(user.id)
518 other_user = User.get_cached_by_id(other_user.id)
520 assert User.following?(other_user, user) == false
524 describe "account fetching" do
525 test "works by id" do
530 |> get("/api/v1/accounts/#{user.id}")
532 assert %{"id" => id} = json_response(conn, 200)
533 assert id == to_string(user.id)
537 |> get("/api/v1/accounts/-1")
539 assert %{"error" => "Can't find user"} = json_response(conn, 404)
542 test "works by nickname" do
547 |> get("/api/v1/accounts/#{user.nickname}")
549 assert %{"id" => id} = json_response(conn, 200)
553 test "works by nickname for remote users" do
554 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
555 Pleroma.Config.put([:instance, :limit_to_local_content], false)
556 user = insert(:user, nickname: "user@example.com", local: false)
560 |> get("/api/v1/accounts/#{user.nickname}")
562 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
563 assert %{"id" => id} = json_response(conn, 200)
567 test "respects limit_to_local_content == :all for remote user nicknames" do
568 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
569 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
571 user = insert(:user, nickname: "user@example.com", local: false)
575 |> get("/api/v1/accounts/#{user.nickname}")
577 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
578 assert json_response(conn, 404)
581 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
582 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
583 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
585 user = insert(:user, nickname: "user@example.com", local: false)
586 reading_user = insert(:user)
590 |> get("/api/v1/accounts/#{user.nickname}")
592 assert json_response(conn, 404)
596 |> assign(:user, reading_user)
597 |> get("/api/v1/accounts/#{user.nickname}")
599 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
600 assert %{"id" => id} = json_response(conn, 200)
605 test "mascot upload", %{conn: conn} do
608 non_image_file = %Plug.Upload{
609 content_type: "audio/mpeg",
610 path: Path.absname("test/fixtures/sound.mp3"),
611 filename: "sound.mp3"
616 |> assign(:user, user)
617 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
619 assert json_response(conn, 415)
622 content_type: "image/jpg",
623 path: Path.absname("test/fixtures/image.jpg"),
624 filename: "an_image.jpg"
629 |> assign(:user, user)
630 |> put("/api/v1/pleroma/mascot", %{"file" => file})
632 assert %{"id" => _, "type" => image} = json_response(conn, 200)
635 test "mascot retrieving", %{conn: conn} do
637 # When user hasn't set a mascot, we should just get pleroma tan back
640 |> assign(:user, user)
641 |> get("/api/v1/pleroma/mascot")
643 assert %{"url" => url} = json_response(conn, 200)
644 assert url =~ "pleroma-fox-tan-smol"
646 # When a user sets their mascot, we should get that back
648 content_type: "image/jpg",
649 path: Path.absname("test/fixtures/image.jpg"),
650 filename: "an_image.jpg"
655 |> assign(:user, user)
656 |> put("/api/v1/pleroma/mascot", %{"file" => file})
658 assert json_response(conn, 200)
660 user = User.get_cached_by_id(user.id)
664 |> assign(:user, user)
665 |> get("/api/v1/pleroma/mascot")
667 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
668 assert url =~ "an_image"
671 test "getting followers", %{conn: conn} do
673 other_user = insert(:user)
674 {:ok, user} = User.follow(user, other_user)
678 |> get("/api/v1/accounts/#{other_user.id}/followers")
680 assert [%{"id" => id}] = json_response(conn, 200)
681 assert id == to_string(user.id)
684 test "getting followers, hide_followers", %{conn: conn} do
686 other_user = insert(:user, %{info: %{hide_followers: true}})
687 {:ok, _user} = User.follow(user, other_user)
691 |> get("/api/v1/accounts/#{other_user.id}/followers")
693 assert [] == json_response(conn, 200)
696 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
698 other_user = insert(:user, %{info: %{hide_followers: true}})
699 {:ok, _user} = User.follow(user, other_user)
703 |> assign(:user, other_user)
704 |> get("/api/v1/accounts/#{other_user.id}/followers")
706 refute [] == json_response(conn, 200)
709 test "getting followers, pagination", %{conn: conn} do
711 follower1 = insert(:user)
712 follower2 = insert(:user)
713 follower3 = insert(:user)
714 {:ok, _} = User.follow(follower1, user)
715 {:ok, _} = User.follow(follower2, user)
716 {:ok, _} = User.follow(follower3, user)
720 |> assign(:user, user)
724 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
726 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
727 assert id3 == follower3.id
728 assert id2 == follower2.id
732 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
734 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
735 assert id2 == follower2.id
736 assert id1 == follower1.id
740 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
742 assert [%{"id" => id2}] = json_response(res_conn, 200)
743 assert id2 == follower2.id
745 assert [link_header] = get_resp_header(res_conn, "link")
746 assert link_header =~ ~r/min_id=#{follower2.id}/
747 assert link_header =~ ~r/max_id=#{follower2.id}/
750 test "getting following", %{conn: conn} do
752 other_user = insert(:user)
753 {:ok, user} = User.follow(user, other_user)
757 |> get("/api/v1/accounts/#{user.id}/following")
759 assert [%{"id" => id}] = json_response(conn, 200)
760 assert id == to_string(other_user.id)
763 test "getting following, hide_follows", %{conn: conn} do
764 user = insert(:user, %{info: %{hide_follows: true}})
765 other_user = insert(:user)
766 {:ok, user} = User.follow(user, other_user)
770 |> get("/api/v1/accounts/#{user.id}/following")
772 assert [] == json_response(conn, 200)
775 test "getting following, hide_follows, same user requesting", %{conn: conn} do
776 user = insert(:user, %{info: %{hide_follows: true}})
777 other_user = insert(:user)
778 {:ok, user} = User.follow(user, other_user)
782 |> assign(:user, user)
783 |> get("/api/v1/accounts/#{user.id}/following")
785 refute [] == json_response(conn, 200)
788 test "getting following, pagination", %{conn: conn} do
790 following1 = insert(:user)
791 following2 = insert(:user)
792 following3 = insert(:user)
793 {:ok, _} = User.follow(user, following1)
794 {:ok, _} = User.follow(user, following2)
795 {:ok, _} = User.follow(user, following3)
799 |> assign(:user, user)
803 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
805 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
806 assert id3 == following3.id
807 assert id2 == following2.id
811 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
813 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
814 assert id2 == following2.id
815 assert id1 == following1.id
819 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
821 assert [%{"id" => id2}] = json_response(res_conn, 200)
822 assert id2 == following2.id
824 assert [link_header] = get_resp_header(res_conn, "link")
825 assert link_header =~ ~r/min_id=#{following2.id}/
826 assert link_header =~ ~r/max_id=#{following2.id}/
829 test "following / unfollowing a user", %{conn: conn} do
831 other_user = insert(:user)
835 |> assign(:user, user)
836 |> post("/api/v1/accounts/#{other_user.id}/follow")
838 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
840 user = User.get_cached_by_id(user.id)
844 |> assign(:user, user)
845 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
847 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
849 user = User.get_cached_by_id(user.id)
853 |> assign(:user, user)
854 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
856 assert %{"id" => id} = json_response(conn, 200)
857 assert id == to_string(other_user.id)
860 test "following without reblogs" do
861 follower = insert(:user)
862 followed = insert(:user)
863 other_user = insert(:user)
867 |> assign(:user, follower)
868 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
870 assert %{"showing_reblogs" => false} = json_response(conn, 200)
872 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
873 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
877 |> assign(:user, User.get_cached_by_id(follower.id))
878 |> get("/api/v1/timelines/home")
880 assert [] == json_response(conn, 200)
884 |> assign(:user, follower)
885 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
887 assert %{"showing_reblogs" => true} = json_response(conn, 200)
891 |> assign(:user, User.get_cached_by_id(follower.id))
892 |> get("/api/v1/timelines/home")
894 expected_activity_id = reblog.id
895 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
898 test "following / unfollowing errors" do
903 |> assign(:user, user)
906 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
907 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
910 user = User.get_cached_by_id(user.id)
911 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
912 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
914 # self follow via uri
915 user = User.get_cached_by_id(user.id)
916 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
917 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
919 # follow non existing user
920 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
921 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
923 # follow non existing user via uri
924 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
925 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
927 # unfollow non existing user
928 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
929 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
932 describe "mute/unmute" do
933 test "with notifications", %{conn: conn} do
935 other_user = insert(:user)
939 |> assign(:user, user)
940 |> post("/api/v1/accounts/#{other_user.id}/mute")
942 response = json_response(conn, 200)
944 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
945 user = User.get_cached_by_id(user.id)
949 |> assign(:user, user)
950 |> post("/api/v1/accounts/#{other_user.id}/unmute")
952 response = json_response(conn, 200)
953 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
956 test "without notifications", %{conn: conn} do
958 other_user = insert(:user)
962 |> assign(:user, user)
963 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
965 response = json_response(conn, 200)
967 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
968 user = User.get_cached_by_id(user.id)
972 |> assign(:user, user)
973 |> post("/api/v1/accounts/#{other_user.id}/unmute")
975 response = json_response(conn, 200)
976 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
980 test "subscribing / unsubscribing to a user", %{conn: conn} do
982 subscription_target = insert(:user)
986 |> assign(:user, user)
987 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
989 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
993 |> assign(:user, user)
994 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
996 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
999 test "getting a list of mutes", %{conn: conn} do
1000 user = insert(:user)
1001 other_user = insert(:user)
1003 {:ok, user} = User.mute(user, other_user)
1007 |> assign(:user, user)
1008 |> get("/api/v1/mutes")
1010 other_user_id = to_string(other_user.id)
1011 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1014 test "blocking / unblocking a user", %{conn: conn} do
1015 user = insert(:user)
1016 other_user = insert(:user)
1020 |> assign(:user, user)
1021 |> post("/api/v1/accounts/#{other_user.id}/block")
1023 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1025 user = User.get_cached_by_id(user.id)
1029 |> assign(:user, user)
1030 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1032 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1035 test "getting a list of blocks", %{conn: conn} do
1036 user = insert(:user)
1037 other_user = insert(:user)
1039 {:ok, user} = User.block(user, other_user)
1043 |> assign(:user, user)
1044 |> get("/api/v1/blocks")
1046 other_user_id = to_string(other_user.id)
1047 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1050 test "blocking / unblocking a domain", %{conn: conn} do
1051 user = insert(:user)
1052 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1056 |> assign(:user, user)
1057 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1059 assert %{} = json_response(conn, 200)
1060 user = User.get_cached_by_ap_id(user.ap_id)
1061 assert User.blocks?(user, other_user)
1065 |> assign(:user, user)
1066 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1068 assert %{} = json_response(conn, 200)
1069 user = User.get_cached_by_ap_id(user.ap_id)
1070 refute User.blocks?(user, other_user)
1073 test "getting a list of domain blocks", %{conn: conn} do
1074 user = insert(:user)
1076 {:ok, user} = User.block_domain(user, "bad.site")
1077 {:ok, user} = User.block_domain(user, "even.worse.site")
1081 |> assign(:user, user)
1082 |> get("/api/v1/domain_blocks")
1084 domain_blocks = json_response(conn, 200)
1086 assert "bad.site" in domain_blocks
1087 assert "even.worse.site" in domain_blocks
1090 test "unimplemented follow_requests, blocks, domain blocks" do
1091 user = insert(:user)
1093 ["blocks", "domain_blocks", "follow_requests"]
1094 |> Enum.each(fn endpoint ->
1097 |> assign(:user, user)
1098 |> get("/api/v1/#{endpoint}")
1100 assert [] = json_response(conn, 200)
1104 test "returns the favorites of a user", %{conn: conn} do
1105 user = insert(:user)
1106 other_user = insert(:user)
1108 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1109 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1111 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1115 |> assign(:user, user)
1116 |> get("/api/v1/favourites")
1118 assert [status] = json_response(first_conn, 200)
1119 assert status["id"] == to_string(activity.id)
1121 assert [{"link", _link_header}] =
1122 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1124 # Honours query params
1125 {:ok, second_activity} =
1126 CommonAPI.post(other_user, %{
1128 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1131 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1133 last_like = status["id"]
1137 |> assign(:user, user)
1138 |> get("/api/v1/favourites?since_id=#{last_like}")
1140 assert [second_status] = json_response(second_conn, 200)
1141 assert second_status["id"] == to_string(second_activity.id)
1145 |> assign(:user, user)
1146 |> get("/api/v1/favourites?limit=0")
1148 assert [] = json_response(third_conn, 200)
1151 describe "getting favorites timeline of specified user" do
1153 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
1154 [current_user: current_user, user: user]
1157 test "returns list of statuses favorited by specified user", %{
1159 current_user: current_user,
1162 [activity | _] = insert_pair(:note_activity)
1163 CommonAPI.favorite(activity.id, user)
1167 |> assign(:user, current_user)
1168 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1169 |> json_response(:ok)
1173 assert length(response) == 1
1174 assert like["id"] == activity.id
1177 test "returns favorites for specified user_id when user is not logged in", %{
1181 activity = insert(:note_activity)
1182 CommonAPI.favorite(activity.id, user)
1186 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1187 |> json_response(:ok)
1189 assert length(response) == 1
1192 test "returns favorited DM only when user is logged in and he is one of recipients", %{
1194 current_user: current_user,
1198 CommonAPI.post(current_user, %{
1199 "status" => "Hi @#{user.nickname}!",
1200 "visibility" => "direct"
1203 CommonAPI.favorite(direct.id, user)
1207 |> assign(:user, current_user)
1208 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1209 |> json_response(:ok)
1211 assert length(response) == 1
1213 anonymous_response =
1215 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1216 |> json_response(:ok)
1218 assert Enum.empty?(anonymous_response)
1221 test "does not return others' favorited DM when user is not one of recipients", %{
1223 current_user: current_user,
1226 user_two = insert(:user)
1229 CommonAPI.post(user_two, %{
1230 "status" => "Hi @#{user.nickname}!",
1231 "visibility" => "direct"
1234 CommonAPI.favorite(direct.id, user)
1238 |> assign(:user, current_user)
1239 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1240 |> json_response(:ok)
1242 assert Enum.empty?(response)
1245 test "paginates favorites using since_id and max_id", %{
1247 current_user: current_user,
1250 activities = insert_list(10, :note_activity)
1252 Enum.each(activities, fn activity ->
1253 CommonAPI.favorite(activity.id, user)
1256 third_activity = Enum.at(activities, 2)
1257 seventh_activity = Enum.at(activities, 6)
1261 |> assign(:user, current_user)
1262 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
1263 since_id: third_activity.id,
1264 max_id: seventh_activity.id
1266 |> json_response(:ok)
1268 assert length(response) == 3
1269 refute third_activity in response
1270 refute seventh_activity in response
1273 test "limits favorites using limit parameter", %{
1275 current_user: current_user,
1279 |> insert_list(:note_activity)
1280 |> Enum.each(fn activity ->
1281 CommonAPI.favorite(activity.id, user)
1286 |> assign(:user, current_user)
1287 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
1288 |> json_response(:ok)
1290 assert length(response) == 3
1293 test "returns empty response when user does not have any favorited statuses", %{
1295 current_user: current_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 "returns 404 error when specified user is not exist", %{conn: conn} do
1308 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
1310 assert json_response(conn, 404) == %{"error" => "Record not found"}
1313 test "returns 403 error when user has hidden own favorites", %{
1315 current_user: current_user
1317 user = insert(:user, %{info: %{hide_favorites: true}})
1318 activity = insert(:note_activity)
1319 CommonAPI.favorite(activity.id, user)
1323 |> assign(:user, current_user)
1324 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1326 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1329 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
1330 user = insert(:user)
1331 activity = insert(:note_activity)
1332 CommonAPI.favorite(activity.id, user)
1336 |> assign(:user, current_user)
1337 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1339 assert user.info.hide_favorites
1340 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1344 test "get instance information", %{conn: conn} do
1345 conn = get(conn, "/api/v1/instance")
1346 assert result = json_response(conn, 200)
1348 email = Config.get([:instance, :email])
1349 # Note: not checking for "max_toot_chars" since it's optional
1355 "email" => from_config_email,
1357 "streaming_api" => _
1362 "registrations" => _,
1366 assert email == from_config_email
1369 test "get instance stats", %{conn: conn} do
1370 user = insert(:user, %{local: true})
1372 user2 = insert(:user, %{local: true})
1373 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1375 insert(:user, %{local: false, nickname: "u@peer1.com"})
1376 insert(:user, %{local: false, nickname: "u@peer2.com"})
1378 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
1380 # Stats should count users with missing or nil `info.deactivated` value
1384 |> User.get_cached_by_id()
1385 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
1387 Pleroma.Stats.force_update()
1389 conn = get(conn, "/api/v1/instance")
1391 assert result = json_response(conn, 200)
1393 stats = result["stats"]
1396 assert stats["user_count"] == 1
1397 assert stats["status_count"] == 1
1398 assert stats["domain_count"] == 2
1401 test "get peers", %{conn: conn} do
1402 insert(:user, %{local: false, nickname: "u@peer1.com"})
1403 insert(:user, %{local: false, nickname: "u@peer2.com"})
1405 Pleroma.Stats.force_update()
1407 conn = get(conn, "/api/v1/instance/peers")
1409 assert result = json_response(conn, 200)
1411 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
1414 test "put settings", %{conn: conn} do
1415 user = insert(:user)
1419 |> assign(:user, user)
1420 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
1422 assert _result = json_response(conn, 200)
1424 user = User.get_cached_by_ap_id(user.ap_id)
1425 assert user.info.settings == %{"programming" => "socks"}
1428 describe "pinned statuses" do
1430 user = insert(:user)
1431 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
1433 [user: user, activity: activity]
1436 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
1437 {:ok, _} = CommonAPI.pin(activity.id, user)
1441 |> assign(:user, user)
1442 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1443 |> json_response(200)
1445 id_str = to_string(activity.id)
1447 assert [%{"id" => ^id_str, "pinned" => true}] = result
1451 describe "reports" do
1453 reporter = insert(:user)
1454 target_user = insert(:user)
1456 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
1458 [reporter: reporter, target_user: target_user, activity: activity]
1461 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
1462 assert %{"action_taken" => false, "id" => _} =
1464 |> assign(:user, reporter)
1465 |> post("/api/v1/reports", %{"account_id" => target_user.id})
1466 |> json_response(200)
1469 test "submit a report with statuses and comment", %{
1472 target_user: target_user,
1475 assert %{"action_taken" => false, "id" => _} =
1477 |> assign(:user, reporter)
1478 |> post("/api/v1/reports", %{
1479 "account_id" => target_user.id,
1480 "status_ids" => [activity.id],
1481 "comment" => "bad status!",
1482 "forward" => "false"
1484 |> json_response(200)
1487 test "account_id is required", %{
1492 assert %{"error" => "Valid `account_id` required"} =
1494 |> assign(:user, reporter)
1495 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
1496 |> json_response(400)
1499 test "comment must be up to the size specified in the config", %{
1502 target_user: target_user
1504 max_size = Config.get([:instance, :max_report_comment_size], 1000)
1505 comment = String.pad_trailing("a", max_size + 1, "a")
1507 error = %{"error" => "Comment must be up to #{max_size} characters"}
1511 |> assign(:user, reporter)
1512 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
1513 |> json_response(400)
1516 test "returns error when account is not exist", %{
1523 |> assign(:user, reporter)
1524 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
1526 assert json_response(conn, 400) == %{"error" => "Account not found"}
1530 describe "link headers" do
1531 test "preserves parameters in link headers", %{conn: conn} do
1532 user = insert(:user)
1533 other_user = insert(:user)
1536 CommonAPI.post(other_user, %{
1537 "status" => "hi @#{user.nickname}",
1538 "visibility" => "public"
1542 CommonAPI.post(other_user, %{
1543 "status" => "hi @#{user.nickname}",
1544 "visibility" => "public"
1547 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
1548 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
1552 |> assign(:user, user)
1553 |> get("/api/v1/notifications", %{media_only: true})
1555 assert [link_header] = get_resp_header(conn, "link")
1556 assert link_header =~ ~r/media_only=true/
1557 assert link_header =~ ~r/min_id=#{notification2.id}/
1558 assert link_header =~ ~r/max_id=#{notification1.id}/
1562 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
1563 # Need to set an old-style integer ID to reproduce the problem
1564 # (these are no longer assigned to new accounts but were preserved
1565 # for existing accounts during the migration to flakeIDs)
1566 user_one = insert(:user, %{id: 1212})
1567 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
1571 |> get("/api/v1/accounts/#{user_one.id}")
1575 |> get("/api/v1/accounts/#{user_two.nickname}")
1579 |> get("/api/v1/accounts/#{user_two.id}")
1581 acc_one = json_response(resp_one, 200)
1582 acc_two = json_response(resp_two, 200)
1583 acc_three = json_response(resp_three, 200)
1584 refute acc_one == acc_two
1585 assert acc_two == acc_three
1588 describe "custom emoji" do
1589 test "with tags", %{conn: conn} do
1592 |> get("/api/v1/custom_emojis")
1593 |> json_response(200)
1595 assert Map.has_key?(emoji, "shortcode")
1596 assert Map.has_key?(emoji, "static_url")
1597 assert Map.has_key?(emoji, "tags")
1598 assert is_list(emoji["tags"])
1599 assert Map.has_key?(emoji, "category")
1600 assert Map.has_key?(emoji, "url")
1601 assert Map.has_key?(emoji, "visible_in_picker")
1605 describe "index/2 redirections" do
1606 setup %{conn: conn} do
1610 signing_salt: "cooldude"
1615 |> Plug.Session.call(Plug.Session.init(session_opts))
1618 test_path = "/web/statuses/test"
1619 %{conn: conn, path: test_path}
1622 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
1623 conn = get(conn, path)
1625 assert conn.status == 302
1626 assert redirected_to(conn) == "/web/login"
1629 test "redirects not logged-in users to the login page on private instances", %{
1633 Config.put([:instance, :public], false)
1635 conn = get(conn, path)
1637 assert conn.status == 302
1638 assert redirected_to(conn) == "/web/login"
1641 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
1642 token = insert(:oauth_token)
1646 |> assign(:user, token.user)
1647 |> put_session(:oauth_token, token.token)
1650 assert conn.status == 200
1653 test "saves referer path to session", %{conn: conn, path: path} do
1654 conn = get(conn, path)
1655 return_to = Plug.Conn.get_session(conn, :return_to)
1657 assert return_to == path
1660 test "redirects to the saved path after log in", %{conn: conn, path: path} do
1661 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1662 auth = insert(:oauth_authorization, app: app)
1666 |> put_session(:return_to, path)
1667 |> get("/web/login", %{code: auth.token})
1669 assert conn.status == 302
1670 assert redirected_to(conn) == path
1673 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
1674 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1675 auth = insert(:oauth_authorization, app: app)
1677 conn = get(conn, "/web/login", %{code: auth.token})
1679 assert conn.status == 302
1680 assert redirected_to(conn) == "/web/getting-started"
1684 describe "scheduled activities" do
1685 test "creates a scheduled activity", %{conn: conn} do
1686 user = insert(:user)
1687 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1691 |> assign(:user, user)
1692 |> post("/api/v1/statuses", %{
1693 "status" => "scheduled",
1694 "scheduled_at" => scheduled_at
1697 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
1698 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
1699 assert [] == Repo.all(Activity)
1702 test "creates a scheduled activity with a media attachment", %{conn: conn} do
1703 user = insert(:user)
1704 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1706 file = %Plug.Upload{
1707 content_type: "image/jpg",
1708 path: Path.absname("test/fixtures/image.jpg"),
1709 filename: "an_image.jpg"
1712 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
1716 |> assign(:user, user)
1717 |> post("/api/v1/statuses", %{
1718 "media_ids" => [to_string(upload.id)],
1719 "status" => "scheduled",
1720 "scheduled_at" => scheduled_at
1723 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
1724 assert %{"type" => "image"} = media_attachment
1727 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
1729 user = insert(:user)
1732 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
1736 |> assign(:user, user)
1737 |> post("/api/v1/statuses", %{
1738 "status" => "not scheduled",
1739 "scheduled_at" => scheduled_at
1742 assert %{"content" => "not scheduled"} = json_response(conn, 200)
1743 assert [] == Repo.all(ScheduledActivity)
1746 test "returns error when daily user limit is exceeded", %{conn: conn} do
1747 user = insert(:user)
1750 NaiveDateTime.utc_now()
1751 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
1752 |> NaiveDateTime.to_iso8601()
1754 attrs = %{params: %{}, scheduled_at: today}
1755 {:ok, _} = ScheduledActivity.create(user, attrs)
1756 {:ok, _} = ScheduledActivity.create(user, attrs)
1760 |> assign(:user, user)
1761 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
1763 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
1766 test "returns error when total user limit is exceeded", %{conn: conn} do
1767 user = insert(:user)
1770 NaiveDateTime.utc_now()
1771 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
1772 |> NaiveDateTime.to_iso8601()
1775 NaiveDateTime.utc_now()
1776 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
1777 |> NaiveDateTime.to_iso8601()
1779 attrs = %{params: %{}, scheduled_at: today}
1780 {:ok, _} = ScheduledActivity.create(user, attrs)
1781 {:ok, _} = ScheduledActivity.create(user, attrs)
1782 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
1786 |> assign(:user, user)
1787 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
1789 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
1792 test "shows scheduled activities", %{conn: conn} do
1793 user = insert(:user)
1794 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
1795 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
1796 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
1797 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
1801 |> assign(:user, user)
1806 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
1808 result = json_response(conn_res, 200)
1809 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
1814 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
1816 result = json_response(conn_res, 200)
1817 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
1822 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
1824 result = json_response(conn_res, 200)
1825 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
1828 test "shows a scheduled activity", %{conn: conn} do
1829 user = insert(:user)
1830 scheduled_activity = insert(:scheduled_activity, user: user)
1834 |> assign(:user, user)
1835 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1837 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
1838 assert scheduled_activity_id == scheduled_activity.id |> to_string()
1842 |> assign(:user, user)
1843 |> get("/api/v1/scheduled_statuses/404")
1845 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1848 test "updates a scheduled activity", %{conn: conn} do
1849 user = insert(:user)
1850 scheduled_activity = insert(:scheduled_activity, user: user)
1853 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
1857 |> assign(:user, user)
1858 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
1859 scheduled_at: new_scheduled_at
1862 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
1863 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
1867 |> assign(:user, user)
1868 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
1870 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1873 test "deletes a scheduled activity", %{conn: conn} do
1874 user = insert(:user)
1875 scheduled_activity = insert(:scheduled_activity, user: user)
1879 |> assign(:user, user)
1880 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1882 assert %{} = json_response(res_conn, 200)
1883 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
1887 |> assign(:user, user)
1888 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
1890 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
1894 describe "create account by app" do
1895 test "Account registration via Application", %{conn: conn} do
1898 |> post("/api/v1/apps", %{
1899 client_name: "client_name",
1900 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1901 scopes: "read, write, follow"
1905 "client_id" => client_id,
1906 "client_secret" => client_secret,
1908 "name" => "client_name",
1909 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1912 } = json_response(conn, 200)
1916 |> post("/oauth/token", %{
1917 grant_type: "client_credentials",
1918 client_id: client_id,
1919 client_secret: client_secret
1922 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1923 json_response(conn, 200)
1926 token_from_db = Repo.get_by(Token, token: token)
1927 assert token_from_db
1929 assert scope == "read write follow"
1933 |> put_req_header("authorization", "Bearer " <> token)
1934 |> post("/api/v1/accounts", %{
1936 email: "lain@example.org",
1937 password: "PlzDontHackLain",
1942 "access_token" => token,
1943 "created_at" => _created_at,
1945 "token_type" => "Bearer"
1946 } = json_response(conn, 200)
1948 token_from_db = Repo.get_by(Token, token: token)
1949 assert token_from_db
1950 token_from_db = Repo.preload(token_from_db, :user)
1951 assert token_from_db.user
1953 assert token_from_db.user.info.confirmation_pending
1956 test "rate limit", %{conn: conn} do
1957 app_token = insert(:oauth_token, user: nil)
1960 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1961 |> Map.put(:remote_ip, {15, 15, 15, 15})
1966 |> post("/api/v1/accounts", %{
1967 username: "#{i}lain",
1968 email: "#{i}lain@example.org",
1969 password: "PlzDontHackLain",
1974 "access_token" => token,
1975 "created_at" => _created_at,
1977 "token_type" => "Bearer"
1978 } = json_response(conn, 200)
1980 token_from_db = Repo.get_by(Token, token: token)
1981 assert token_from_db
1982 token_from_db = Repo.preload(token_from_db, :user)
1983 assert token_from_db.user
1985 assert token_from_db.user.info.confirmation_pending
1990 |> post("/api/v1/accounts", %{
1992 email: "6lain@example.org",
1993 password: "PlzDontHackLain",
1997 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
2001 describe "GET /api/v1/polls/:id" do
2002 test "returns poll entity for object id", %{conn: conn} do
2003 user = insert(:user)
2006 CommonAPI.post(user, %{
2007 "status" => "Pleroma does",
2008 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
2011 object = Object.normalize(activity)
2015 |> assign(:user, user)
2016 |> get("/api/v1/polls/#{object.id}")
2018 response = json_response(conn, 200)
2019 id = to_string(object.id)
2020 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
2023 test "does not expose polls for private statuses", %{conn: conn} do
2024 user = insert(:user)
2025 other_user = insert(:user)
2028 CommonAPI.post(user, %{
2029 "status" => "Pleroma does",
2030 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
2031 "visibility" => "private"
2034 object = Object.normalize(activity)
2038 |> assign(:user, other_user)
2039 |> get("/api/v1/polls/#{object.id}")
2041 assert json_response(conn, 404)
2045 describe "POST /api/v1/polls/:id/votes" do
2046 test "votes are added to the poll", %{conn: conn} do
2047 user = insert(:user)
2048 other_user = insert(:user)
2051 CommonAPI.post(user, %{
2052 "status" => "A very delicious sandwich",
2054 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
2060 object = Object.normalize(activity)
2064 |> assign(:user, other_user)
2065 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
2067 assert json_response(conn, 200)
2068 object = Object.get_by_id(object.id)
2070 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
2075 test "author can't vote", %{conn: conn} do
2076 user = insert(:user)
2079 CommonAPI.post(user, %{
2080 "status" => "Am I cute?",
2081 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
2084 object = Object.normalize(activity)
2087 |> assign(:user, user)
2088 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
2089 |> json_response(422) == %{"error" => "Poll's author can't vote"}
2091 object = Object.get_by_id(object.id)
2093 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
2096 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
2097 user = insert(:user)
2098 other_user = insert(:user)
2101 CommonAPI.post(user, %{
2102 "status" => "The glass is",
2103 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
2106 object = Object.normalize(activity)
2109 |> assign(:user, other_user)
2110 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
2111 |> json_response(422) == %{"error" => "Too many choices"}
2113 object = Object.get_by_id(object.id)
2115 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
2120 test "does not allow choice index to be greater than options count", %{conn: conn} do
2121 user = insert(:user)
2122 other_user = insert(:user)
2125 CommonAPI.post(user, %{
2126 "status" => "Am I cute?",
2127 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
2130 object = Object.normalize(activity)
2134 |> assign(:user, other_user)
2135 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
2137 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
2140 test "returns 404 error when object is not exist", %{conn: conn} do
2141 user = insert(:user)
2145 |> assign(:user, user)
2146 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
2148 assert json_response(conn, 404) == %{"error" => "Record not found"}
2151 test "returns 404 when poll is private and not available for user", %{conn: conn} do
2152 user = insert(:user)
2153 other_user = insert(:user)
2156 CommonAPI.post(user, %{
2157 "status" => "Am I cute?",
2158 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
2159 "visibility" => "private"
2162 object = Object.normalize(activity)
2166 |> assign(:user, other_user)
2167 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
2169 assert json_response(conn, 404) == %{"error" => "Record not found"}
2173 describe "POST /auth/password, with valid parameters" do
2174 setup %{conn: conn} do
2175 user = insert(:user)
2176 conn = post(conn, "/auth/password?email=#{user.email}")
2177 %{conn: conn, user: user}
2180 test "it returns 204", %{conn: conn} do
2181 assert json_response(conn, :no_content)
2184 test "it creates a PasswordResetToken record for user", %{user: user} do
2185 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
2189 test "it sends an email to user", %{user: user} do
2190 ObanHelpers.perform_all()
2191 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
2193 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
2194 notify_email = Config.get([:instance, :notify_email])
2195 instance_name = Config.get([:instance, :name])
2198 from: {instance_name, notify_email},
2199 to: {user.name, user.email},
2200 html_body: email.html_body
2205 describe "POST /auth/password, with invalid parameters" do
2207 user = insert(:user)
2211 test "it returns 404 when user is not found", %{conn: conn, user: user} do
2212 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
2213 assert conn.status == 404
2214 assert conn.resp_body == ""
2217 test "it returns 400 when user is not local", %{conn: conn, user: user} do
2218 {:ok, user} = Repo.update(Changeset.change(user, local: false))
2219 conn = post(conn, "/auth/password?email=#{user.email}")
2220 assert conn.status == 400
2221 assert conn.resp_body == ""
2225 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
2229 |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
2232 assert user.info.confirmation_pending
2237 clear_config([:instance, :account_activation_required]) do
2238 Config.put([:instance, :account_activation_required], true)
2241 test "resend account confirmation email", %{conn: conn, user: user} do
2243 |> assign(:user, user)
2244 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
2245 |> json_response(:no_content)
2247 ObanHelpers.perform_all()
2249 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
2250 notify_email = Config.get([:instance, :notify_email])
2251 instance_name = Config.get([:instance, :name])
2254 from: {instance_name, notify_email},
2255 to: {user.name, user.email},
2256 html_body: email.html_body
2261 describe "GET /api/v1/suggestions" do
2263 user = insert(:user)
2264 other_user = insert(:user)
2265 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
2266 url500 = "http://test500?#{host}&#{user.nickname}"
2267 url200 = "http://test200?#{host}&#{user.nickname}"
2270 %{method: :get, url: ^url500} ->
2271 %Tesla.Env{status: 500, body: "bad request"}
2273 %{method: :get, url: ^url200} ->
2277 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
2279 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
2283 [user: user, other_user: other_user]
2286 clear_config(:suggestions)
2288 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
2289 Config.put([:suggestions, :enabled], false)
2293 |> assign(:user, user)
2294 |> get("/api/v1/suggestions")
2295 |> json_response(200)
2300 test "returns error", %{conn: conn, user: user} do
2301 Config.put([:suggestions, :enabled], true)
2302 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
2304 assert capture_log(fn ->
2307 |> assign(:user, user)
2308 |> get("/api/v1/suggestions")
2309 |> json_response(500)
2311 assert res == "Something went wrong"
2312 end) =~ "Could not retrieve suggestions"
2315 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
2316 Config.put([:suggestions, :enabled], true)
2317 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
2321 |> assign(:user, user)
2322 |> get("/api/v1/suggestions")
2323 |> json_response(200)
2328 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
2329 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
2333 "acct" => other_user.ap_id,
2334 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
2335 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
2336 "id" => other_user.id