1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.ActivityExpiration
11 alias Pleroma.Notification
14 alias Pleroma.ScheduledActivity
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.FilterView
19 alias Pleroma.Web.OAuth.App
20 alias Pleroma.Web.OAuth.Token
21 alias Pleroma.Web.OStatus
22 alias Pleroma.Web.Push
23 alias Pleroma.Web.TwitterAPI.TwitterAPI
24 import Pleroma.Factory
25 import ExUnit.CaptureLog
27 import Swoosh.TestAssertions
29 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
32 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
36 test "the home timeline", %{conn: conn} do
38 following = insert(:user)
40 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
44 |> assign(:user, user)
45 |> get("/api/v1/timelines/home")
47 assert Enum.empty?(json_response(conn, 200))
49 {:ok, user} = User.follow(user, following)
53 |> assign(:user, user)
54 |> get("/api/v1/timelines/home")
56 assert [%{"content" => "test"}] = json_response(conn, 200)
59 test "the public timeline", %{conn: conn} do
60 following = insert(:user)
63 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
66 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
70 |> get("/api/v1/timelines/public", %{"local" => "False"})
72 assert length(json_response(conn, 200)) == 2
76 |> get("/api/v1/timelines/public", %{"local" => "True"})
78 assert [%{"content" => "test"}] = json_response(conn, 200)
82 |> get("/api/v1/timelines/public", %{"local" => "1"})
84 assert [%{"content" => "test"}] = json_response(conn, 200)
88 test "the public timeline when public is set to false", %{conn: conn} do
89 public = Pleroma.Config.get([:instance, :public])
90 Pleroma.Config.put([:instance, :public], false)
93 Pleroma.Config.put([:instance, :public], public)
97 |> get("/api/v1/timelines/public", %{"local" => "False"})
98 |> json_response(403) == %{"error" => "This resource requires authentication."}
101 describe "posting statuses" do
107 |> assign(:user, user)
112 test "posting a status", %{conn: conn} do
113 idempotency_key = "Pikachu rocks!"
117 |> put_req_header("idempotency-key", idempotency_key)
118 |> post("/api/v1/statuses", %{
120 "spoiler_text" => "2hu",
121 "sensitive" => "false"
124 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
126 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
128 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
129 json_response(conn_one, 200)
131 assert Activity.get_by_id(id)
135 |> put_req_header("idempotency-key", idempotency_key)
136 |> post("/api/v1/statuses", %{
138 "spoiler_text" => "2hu",
139 "sensitive" => "false"
142 assert %{"id" => second_id} = json_response(conn_two, 200)
143 assert id == second_id
147 |> post("/api/v1/statuses", %{
149 "spoiler_text" => "2hu",
150 "sensitive" => "false"
153 assert %{"id" => third_id} = json_response(conn_three, 200)
154 refute id == third_id
156 # An activity that will expire:
158 NaiveDateTime.utc_now()
159 |> NaiveDateTime.add(:timer.minutes(120), :millisecond)
160 |> NaiveDateTime.truncate(:second)
164 |> post("api/v1/statuses", %{
165 "status" => "oolong",
166 "expires_at" => expires_at
169 assert %{"id" => fourth_id} = json_response(conn_four, 200)
170 assert activity = Activity.get_by_id(fourth_id)
171 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
172 assert expiration.scheduled_at == expires_at
175 test "replying to a status", %{conn: conn} do
177 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
181 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
183 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
185 activity = Activity.get_by_id(id)
187 assert activity.data["context"] == replied_to.data["context"]
188 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
191 test "replying to a direct message with visibility other than direct", %{conn: conn} do
193 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
195 Enum.each(["public", "private", "unlisted"], fn visibility ->
198 |> post("/api/v1/statuses", %{
199 "status" => "@#{user.nickname} hey",
200 "in_reply_to_id" => replied_to.id,
201 "visibility" => visibility
204 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
208 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
211 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
213 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
214 assert Activity.get_by_id(id)
217 test "posting a sensitive status", %{conn: conn} do
220 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
222 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
223 assert Activity.get_by_id(id)
226 test "posting a fake status", %{conn: conn} do
229 |> post("/api/v1/statuses", %{
231 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
234 real_status = json_response(real_conn, 200)
237 assert Object.get_by_ap_id(real_status["uri"])
241 |> Map.put("id", nil)
242 |> Map.put("url", nil)
243 |> Map.put("uri", nil)
244 |> Map.put("created_at", nil)
245 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
249 |> post("/api/v1/statuses", %{
251 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
255 fake_status = json_response(fake_conn, 200)
258 refute Object.get_by_ap_id(fake_status["uri"])
262 |> Map.put("id", nil)
263 |> Map.put("url", nil)
264 |> Map.put("uri", nil)
265 |> Map.put("created_at", nil)
266 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
268 assert real_status == fake_status
271 test "posting a status with OGP link preview", %{conn: conn} do
272 Pleroma.Config.put([:rich_media, :enabled], true)
276 |> post("/api/v1/statuses", %{
277 "status" => "https://example.com/ogp"
280 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
281 assert Activity.get_by_id(id)
282 Pleroma.Config.put([:rich_media, :enabled], false)
285 test "posting a direct status", %{conn: conn} do
286 user2 = insert(:user)
287 content = "direct cofe @#{user2.nickname}"
291 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
293 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
294 assert activity = Activity.get_by_id(id)
295 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
296 assert activity.data["to"] == [user2.ap_id]
297 assert activity.data["cc"] == []
301 describe "posting polls" do
302 test "posting a poll", %{conn: conn} do
304 time = NaiveDateTime.utc_now()
308 |> assign(:user, user)
309 |> post("/api/v1/statuses", %{
310 "status" => "Who is the #bestgrill?",
311 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
314 response = json_response(conn, 200)
316 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
317 title in ["Rei", "Asuka", "Misato"]
320 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
321 refute response["poll"]["expred"]
324 test "option limit is enforced", %{conn: conn} do
326 limit = Pleroma.Config.get([:instance, :poll_limits, :max_options])
330 |> assign(:user, user)
331 |> post("/api/v1/statuses", %{
333 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
336 %{"error" => error} = json_response(conn, 422)
337 assert error == "Poll can't contain more than #{limit} options"
340 test "option character limit is enforced", %{conn: conn} do
342 limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars])
346 |> assign(:user, user)
347 |> post("/api/v1/statuses", %{
350 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
355 %{"error" => error} = json_response(conn, 422)
356 assert error == "Poll options cannot be longer than #{limit} characters each"
359 test "minimal date limit is enforced", %{conn: conn} do
361 limit = Pleroma.Config.get([:instance, :poll_limits, :min_expiration])
365 |> assign(:user, user)
366 |> post("/api/v1/statuses", %{
367 "status" => "imagine arbitrary limits",
369 "options" => ["this post was made by pleroma gang"],
370 "expires_in" => limit - 1
374 %{"error" => error} = json_response(conn, 422)
375 assert error == "Expiration date is too soon"
378 test "maximum date limit is enforced", %{conn: conn} do
380 limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration])
384 |> assign(:user, user)
385 |> post("/api/v1/statuses", %{
386 "status" => "imagine arbitrary limits",
388 "options" => ["this post was made by pleroma gang"],
389 "expires_in" => limit + 1
393 %{"error" => error} = json_response(conn, 422)
394 assert error == "Expiration date is too far in the future"
398 test "direct timeline", %{conn: conn} do
399 user_one = insert(:user)
400 user_two = insert(:user)
402 {:ok, user_two} = User.follow(user_two, user_one)
405 CommonAPI.post(user_one, %{
406 "status" => "Hi @#{user_two.nickname}!",
407 "visibility" => "direct"
410 {:ok, _follower_only} =
411 CommonAPI.post(user_one, %{
412 "status" => "Hi @#{user_two.nickname}!",
413 "visibility" => "private"
416 # Only direct should be visible here
419 |> assign(:user, user_two)
420 |> get("api/v1/timelines/direct")
422 [status] = json_response(res_conn, 200)
424 assert %{"visibility" => "direct"} = status
425 assert status["url"] != direct.data["id"]
427 # User should be able to see their own direct message
430 |> assign(:user, user_one)
431 |> get("api/v1/timelines/direct")
433 [status] = json_response(res_conn, 200)
435 assert %{"visibility" => "direct"} = status
437 # Both should be visible here
440 |> assign(:user, user_two)
441 |> get("api/v1/timelines/home")
443 [_s1, _s2] = json_response(res_conn, 200)
446 Enum.each(1..20, fn _ ->
448 CommonAPI.post(user_one, %{
449 "status" => "Hi @#{user_two.nickname}!",
450 "visibility" => "direct"
456 |> assign(:user, user_two)
457 |> get("api/v1/timelines/direct")
459 statuses = json_response(res_conn, 200)
460 assert length(statuses) == 20
464 |> assign(:user, user_two)
465 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
467 [status] = json_response(res_conn, 200)
469 assert status["url"] != direct.data["id"]
472 test "Conversations", %{conn: conn} do
473 user_one = insert(:user)
474 user_two = insert(:user)
475 user_three = insert(:user)
477 {:ok, user_two} = User.follow(user_two, user_one)
480 CommonAPI.post(user_one, %{
481 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
482 "visibility" => "direct"
485 {:ok, _follower_only} =
486 CommonAPI.post(user_one, %{
487 "status" => "Hi @#{user_two.nickname}!",
488 "visibility" => "private"
493 |> assign(:user, user_one)
494 |> get("/api/v1/conversations")
496 assert response = json_response(res_conn, 200)
501 "accounts" => res_accounts,
502 "last_status" => res_last_status,
507 account_ids = Enum.map(res_accounts, & &1["id"])
508 assert length(res_accounts) == 2
509 assert user_two.id in account_ids
510 assert user_three.id in account_ids
511 assert is_binary(res_id)
512 assert unread == true
513 assert res_last_status["id"] == direct.id
515 # Apparently undocumented API endpoint
518 |> assign(:user, user_one)
519 |> post("/api/v1/conversations/#{res_id}/read")
521 assert response = json_response(res_conn, 200)
522 assert length(response["accounts"]) == 2
523 assert response["last_status"]["id"] == direct.id
524 assert response["unread"] == false
526 # (vanilla) Mastodon frontend behaviour
529 |> assign(:user, user_one)
530 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
532 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
535 test "doesn't include DMs from blocked users", %{conn: conn} do
536 blocker = insert(:user)
537 blocked = insert(:user)
539 {:ok, blocker} = User.block(blocker, blocked)
541 {:ok, _blocked_direct} =
542 CommonAPI.post(blocked, %{
543 "status" => "Hi @#{blocker.nickname}!",
544 "visibility" => "direct"
548 CommonAPI.post(user, %{
549 "status" => "Hi @#{blocker.nickname}!",
550 "visibility" => "direct"
555 |> assign(:user, user)
556 |> get("api/v1/timelines/direct")
558 [status] = json_response(res_conn, 200)
559 assert status["id"] == direct.id
562 test "verify_credentials", %{conn: conn} do
567 |> assign(:user, user)
568 |> get("/api/v1/accounts/verify_credentials")
570 response = json_response(conn, 200)
572 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
573 assert response["pleroma"]["chat_token"]
574 assert id == to_string(user.id)
577 test "verify_credentials default scope unlisted", %{conn: conn} do
578 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
582 |> assign(:user, user)
583 |> get("/api/v1/accounts/verify_credentials")
585 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
586 assert id == to_string(user.id)
589 test "apps/verify_credentials", %{conn: conn} do
590 token = insert(:oauth_token)
594 |> assign(:user, token.user)
595 |> assign(:token, token)
596 |> get("/api/v1/apps/verify_credentials")
598 app = Repo.preload(token, :app).app
601 "name" => app.client_name,
602 "website" => app.website,
603 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
606 assert expected == json_response(conn, 200)
609 test "user avatar can be set", %{conn: conn} do
611 avatar_image = File.read!("test/fixtures/avatar_data_uri")
615 |> assign(:user, user)
616 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
618 user = refresh_record(user)
632 assert %{"url" => _} = json_response(conn, 200)
635 test "user avatar can be reset", %{conn: conn} do
640 |> assign(:user, user)
641 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
643 user = User.get_cached_by_id(user.id)
645 assert user.avatar == nil
647 assert %{"url" => nil} = json_response(conn, 200)
650 test "can set profile banner", %{conn: conn} do
655 |> assign(:user, user)
656 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
658 user = refresh_record(user)
659 assert user.info.banner["type"] == "Image"
661 assert %{"url" => _} = json_response(conn, 200)
664 test "can reset profile banner", %{conn: conn} do
669 |> assign(:user, user)
670 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
672 user = refresh_record(user)
673 assert user.info.banner == %{}
675 assert %{"url" => nil} = json_response(conn, 200)
678 test "background image can be set", %{conn: conn} do
683 |> assign(:user, user)
684 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
686 user = refresh_record(user)
687 assert user.info.background["type"] == "Image"
688 assert %{"url" => _} = json_response(conn, 200)
691 test "background image can be reset", %{conn: conn} do
696 |> assign(:user, user)
697 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
699 user = refresh_record(user)
700 assert user.info.background == %{}
701 assert %{"url" => nil} = json_response(conn, 200)
704 test "creates an oauth app", %{conn: conn} do
706 app_attrs = build(:oauth_app)
710 |> assign(:user, user)
711 |> post("/api/v1/apps", %{
712 client_name: app_attrs.client_name,
713 redirect_uris: app_attrs.redirect_uris
716 [app] = Repo.all(App)
719 "name" => app.client_name,
720 "website" => app.website,
721 "client_id" => app.client_id,
722 "client_secret" => app.client_secret,
723 "id" => app.id |> to_string(),
724 "redirect_uri" => app.redirect_uris,
725 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
728 assert expected == json_response(conn, 200)
731 test "get a status", %{conn: conn} do
732 activity = insert(:note_activity)
736 |> get("/api/v1/statuses/#{activity.id}")
738 assert %{"id" => id} = json_response(conn, 200)
739 assert id == to_string(activity.id)
742 describe "deleting a status" do
743 test "when you created it", %{conn: conn} do
744 activity = insert(:note_activity)
745 author = User.get_cached_by_ap_id(activity.data["actor"])
749 |> assign(:user, author)
750 |> delete("/api/v1/statuses/#{activity.id}")
752 assert %{} = json_response(conn, 200)
754 refute Activity.get_by_id(activity.id)
757 test "when you didn't create it", %{conn: conn} do
758 activity = insert(:note_activity)
763 |> assign(:user, user)
764 |> delete("/api/v1/statuses/#{activity.id}")
766 assert %{"error" => _} = json_response(conn, 403)
768 assert Activity.get_by_id(activity.id) == activity
771 test "when you're an admin or moderator", %{conn: conn} do
772 activity1 = insert(:note_activity)
773 activity2 = insert(:note_activity)
774 admin = insert(:user, info: %{is_admin: true})
775 moderator = insert(:user, info: %{is_moderator: true})
779 |> assign(:user, admin)
780 |> delete("/api/v1/statuses/#{activity1.id}")
782 assert %{} = json_response(res_conn, 200)
786 |> assign(:user, moderator)
787 |> delete("/api/v1/statuses/#{activity2.id}")
789 assert %{} = json_response(res_conn, 200)
791 refute Activity.get_by_id(activity1.id)
792 refute Activity.get_by_id(activity2.id)
796 describe "filters" do
797 test "creating a filter", %{conn: conn} do
800 filter = %Pleroma.Filter{
807 |> assign(:user, user)
808 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
810 assert response = json_response(conn, 200)
811 assert response["phrase"] == filter.phrase
812 assert response["context"] == filter.context
813 assert response["irreversible"] == false
814 assert response["id"] != nil
815 assert response["id"] != ""
818 test "fetching a list of filters", %{conn: conn} do
821 query_one = %Pleroma.Filter{
828 query_two = %Pleroma.Filter{
835 {:ok, filter_one} = Pleroma.Filter.create(query_one)
836 {:ok, filter_two} = Pleroma.Filter.create(query_two)
840 |> assign(:user, user)
841 |> get("/api/v1/filters")
842 |> json_response(200)
848 filters: [filter_two, filter_one]
852 test "get a filter", %{conn: conn} do
855 query = %Pleroma.Filter{
862 {:ok, filter} = Pleroma.Filter.create(query)
866 |> assign(:user, user)
867 |> get("/api/v1/filters/#{filter.filter_id}")
869 assert _response = json_response(conn, 200)
872 test "update a filter", %{conn: conn} do
875 query = %Pleroma.Filter{
882 {:ok, _filter} = Pleroma.Filter.create(query)
884 new = %Pleroma.Filter{
891 |> assign(:user, user)
892 |> put("/api/v1/filters/#{query.filter_id}", %{
897 assert response = json_response(conn, 200)
898 assert response["phrase"] == new.phrase
899 assert response["context"] == new.context
902 test "delete a filter", %{conn: conn} do
905 query = %Pleroma.Filter{
912 {:ok, filter} = Pleroma.Filter.create(query)
916 |> assign(:user, user)
917 |> delete("/api/v1/filters/#{filter.filter_id}")
919 assert response = json_response(conn, 200)
920 assert response == %{}
925 test "creating a list", %{conn: conn} do
930 |> assign(:user, user)
931 |> post("/api/v1/lists", %{"title" => "cuties"})
933 assert %{"title" => title} = json_response(conn, 200)
934 assert title == "cuties"
937 test "adding users to a list", %{conn: conn} do
939 other_user = insert(:user)
940 {:ok, list} = Pleroma.List.create("name", user)
944 |> assign(:user, user)
945 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
947 assert %{} == json_response(conn, 200)
948 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
949 assert following == [other_user.follower_address]
952 test "removing users from a list", %{conn: conn} do
954 other_user = insert(:user)
955 third_user = insert(:user)
956 {:ok, list} = Pleroma.List.create("name", user)
957 {:ok, list} = Pleroma.List.follow(list, other_user)
958 {:ok, list} = Pleroma.List.follow(list, third_user)
962 |> assign(:user, user)
963 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
965 assert %{} == json_response(conn, 200)
966 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
967 assert following == [third_user.follower_address]
970 test "listing users in a list", %{conn: conn} do
972 other_user = insert(:user)
973 {:ok, list} = Pleroma.List.create("name", user)
974 {:ok, list} = Pleroma.List.follow(list, other_user)
978 |> assign(:user, user)
979 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
981 assert [%{"id" => id}] = json_response(conn, 200)
982 assert id == to_string(other_user.id)
985 test "retrieving a list", %{conn: conn} do
987 {:ok, list} = Pleroma.List.create("name", user)
991 |> assign(:user, user)
992 |> get("/api/v1/lists/#{list.id}")
994 assert %{"id" => id} = json_response(conn, 200)
995 assert id == to_string(list.id)
998 test "renaming a list", %{conn: conn} do
1000 {:ok, list} = Pleroma.List.create("name", user)
1004 |> assign(:user, user)
1005 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
1007 assert %{"title" => name} = json_response(conn, 200)
1008 assert name == "newname"
1011 test "deleting a list", %{conn: conn} do
1012 user = insert(:user)
1013 {:ok, list} = Pleroma.List.create("name", user)
1017 |> assign(:user, user)
1018 |> delete("/api/v1/lists/#{list.id}")
1020 assert %{} = json_response(conn, 200)
1021 assert is_nil(Repo.get(Pleroma.List, list.id))
1024 test "list timeline", %{conn: conn} do
1025 user = insert(:user)
1026 other_user = insert(:user)
1027 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1028 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1029 {:ok, list} = Pleroma.List.create("name", user)
1030 {:ok, list} = Pleroma.List.follow(list, other_user)
1034 |> assign(:user, user)
1035 |> get("/api/v1/timelines/list/#{list.id}")
1037 assert [%{"id" => id}] = json_response(conn, 200)
1039 assert id == to_string(activity_two.id)
1042 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1043 user = insert(:user)
1044 other_user = insert(:user)
1045 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1047 {:ok, _activity_two} =
1048 CommonAPI.post(other_user, %{
1049 "status" => "Marisa is cute.",
1050 "visibility" => "private"
1053 {:ok, list} = Pleroma.List.create("name", user)
1054 {:ok, list} = Pleroma.List.follow(list, other_user)
1058 |> assign(:user, user)
1059 |> get("/api/v1/timelines/list/#{list.id}")
1061 assert [%{"id" => id}] = json_response(conn, 200)
1063 assert id == to_string(activity_one.id)
1067 describe "notifications" do
1068 test "list of notifications", %{conn: conn} do
1069 user = insert(:user)
1070 other_user = insert(:user)
1072 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1074 {:ok, [_notification]} = Notification.create_notifications(activity)
1078 |> assign(:user, user)
1079 |> get("/api/v1/notifications")
1082 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1084 }\">@<span>#{user.nickname}</span></a></span>"
1086 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1087 assert response == expected_response
1090 test "getting a single notification", %{conn: conn} do
1091 user = insert(:user)
1092 other_user = insert(:user)
1094 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1096 {:ok, [notification]} = Notification.create_notifications(activity)
1100 |> assign(:user, user)
1101 |> get("/api/v1/notifications/#{notification.id}")
1104 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1106 }\">@<span>#{user.nickname}</span></a></span>"
1108 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1109 assert response == expected_response
1112 test "dismissing a single notification", %{conn: conn} do
1113 user = insert(:user)
1114 other_user = insert(:user)
1116 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1118 {:ok, [notification]} = Notification.create_notifications(activity)
1122 |> assign(:user, user)
1123 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1125 assert %{} = json_response(conn, 200)
1128 test "clearing all notifications", %{conn: conn} do
1129 user = insert(:user)
1130 other_user = insert(:user)
1132 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1134 {:ok, [_notification]} = Notification.create_notifications(activity)
1138 |> assign(:user, user)
1139 |> post("/api/v1/notifications/clear")
1141 assert %{} = json_response(conn, 200)
1145 |> assign(:user, user)
1146 |> get("/api/v1/notifications")
1148 assert all = json_response(conn, 200)
1152 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1153 user = insert(:user)
1154 other_user = insert(:user)
1156 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1157 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1158 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1159 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1161 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1162 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1163 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1164 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1168 |> assign(:user, user)
1173 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1175 result = json_response(conn_res, 200)
1176 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1181 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1183 result = json_response(conn_res, 200)
1184 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1189 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1191 result = json_response(conn_res, 200)
1192 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1195 test "filters notifications using exclude_types", %{conn: conn} do
1196 user = insert(:user)
1197 other_user = insert(:user)
1199 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1200 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1201 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1202 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1203 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1205 mention_notification_id =
1206 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1208 favorite_notification_id =
1209 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1211 reblog_notification_id =
1212 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1214 follow_notification_id =
1215 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1219 |> assign(:user, user)
1222 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1224 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1227 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1229 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1232 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1234 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1237 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1239 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1242 test "destroy multiple", %{conn: conn} do
1243 user = insert(:user)
1244 other_user = insert(:user)
1246 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1247 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1248 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1249 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1251 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1252 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1253 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1254 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1258 |> assign(:user, user)
1262 |> get("/api/v1/notifications")
1264 result = json_response(conn_res, 200)
1265 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1269 |> assign(:user, other_user)
1273 |> get("/api/v1/notifications")
1275 result = json_response(conn_res, 200)
1276 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1280 |> delete("/api/v1/notifications/destroy_multiple", %{
1281 "ids" => [notification1_id, notification2_id]
1284 assert json_response(conn_destroy, 200) == %{}
1288 |> get("/api/v1/notifications")
1290 result = json_response(conn_res, 200)
1291 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1294 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1295 user = insert(:user)
1296 user2 = insert(:user)
1298 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1299 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1301 conn = assign(conn, :user, user)
1303 conn = get(conn, "/api/v1/notifications")
1305 assert length(json_response(conn, 200)) == 1
1307 {:ok, user} = User.mute(user, user2)
1309 conn = assign(build_conn(), :user, user)
1310 conn = get(conn, "/api/v1/notifications")
1312 assert json_response(conn, 200) == []
1315 test "see notifications after muting user without notifications", %{conn: conn} do
1316 user = insert(:user)
1317 user2 = insert(:user)
1319 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1320 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1322 conn = assign(conn, :user, user)
1324 conn = get(conn, "/api/v1/notifications")
1326 assert length(json_response(conn, 200)) == 1
1328 {:ok, user} = User.mute(user, user2, false)
1330 conn = assign(build_conn(), :user, user)
1331 conn = get(conn, "/api/v1/notifications")
1333 assert length(json_response(conn, 200)) == 1
1336 test "see notifications after muting user with notifications and with_muted parameter", %{
1339 user = insert(:user)
1340 user2 = insert(:user)
1342 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1343 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1345 conn = assign(conn, :user, user)
1347 conn = get(conn, "/api/v1/notifications")
1349 assert length(json_response(conn, 200)) == 1
1351 {:ok, user} = User.mute(user, user2)
1353 conn = assign(build_conn(), :user, user)
1354 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1356 assert length(json_response(conn, 200)) == 1
1360 describe "reblogging" do
1361 test "reblogs and returns the reblogged status", %{conn: conn} do
1362 activity = insert(:note_activity)
1363 user = insert(:user)
1367 |> assign(:user, user)
1368 |> post("/api/v1/statuses/#{activity.id}/reblog")
1371 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1373 } = json_response(conn, 200)
1375 assert to_string(activity.id) == id
1378 test "reblogged status for another user", %{conn: conn} do
1379 activity = insert(:note_activity)
1380 user1 = insert(:user)
1381 user2 = insert(:user)
1382 user3 = insert(:user)
1383 CommonAPI.favorite(activity.id, user2)
1384 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1385 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1386 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1390 |> assign(:user, user3)
1391 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1394 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1395 "reblogged" => false,
1396 "favourited" => false,
1397 "bookmarked" => false
1398 } = json_response(conn_res, 200)
1402 |> assign(:user, user2)
1403 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1406 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1407 "reblogged" => true,
1408 "favourited" => true,
1409 "bookmarked" => true
1410 } = json_response(conn_res, 200)
1412 assert to_string(activity.id) == id
1415 test "returns 400 error when activity is not exist", %{conn: conn} do
1416 user = insert(:user)
1420 |> assign(:user, user)
1421 |> post("/api/v1/statuses/foo/reblog")
1423 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1427 describe "unreblogging" do
1428 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1429 activity = insert(:note_activity)
1430 user = insert(:user)
1432 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1436 |> assign(:user, user)
1437 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1439 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1441 assert to_string(activity.id) == id
1444 test "returns 400 error when activity is not exist", %{conn: conn} do
1445 user = insert(:user)
1449 |> assign(:user, user)
1450 |> post("/api/v1/statuses/foo/unreblog")
1452 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1456 describe "favoriting" do
1457 test "favs a status and returns it", %{conn: conn} do
1458 activity = insert(:note_activity)
1459 user = insert(:user)
1463 |> assign(:user, user)
1464 |> post("/api/v1/statuses/#{activity.id}/favourite")
1466 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1467 json_response(conn, 200)
1469 assert to_string(activity.id) == id
1472 test "returns 400 error for a wrong id", %{conn: conn} do
1473 user = insert(:user)
1477 |> assign(:user, user)
1478 |> post("/api/v1/statuses/1/favourite")
1480 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1484 describe "unfavoriting" do
1485 test "unfavorites a status and returns it", %{conn: conn} do
1486 activity = insert(:note_activity)
1487 user = insert(:user)
1489 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1493 |> assign(:user, user)
1494 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1496 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1497 json_response(conn, 200)
1499 assert to_string(activity.id) == id
1502 test "returns 400 error for a wrong id", %{conn: conn} do
1503 user = insert(:user)
1507 |> assign(:user, user)
1508 |> post("/api/v1/statuses/1/unfavourite")
1510 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1514 describe "user timelines" do
1515 test "gets a users statuses", %{conn: conn} do
1516 user_one = insert(:user)
1517 user_two = insert(:user)
1518 user_three = insert(:user)
1520 {:ok, user_three} = User.follow(user_three, user_one)
1522 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1524 {:ok, direct_activity} =
1525 CommonAPI.post(user_one, %{
1526 "status" => "Hi, @#{user_two.nickname}.",
1527 "visibility" => "direct"
1530 {:ok, private_activity} =
1531 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1535 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1537 assert [%{"id" => id}] = json_response(resp, 200)
1538 assert id == to_string(activity.id)
1542 |> assign(:user, user_two)
1543 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1545 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1546 assert id_one == to_string(direct_activity.id)
1547 assert id_two == to_string(activity.id)
1551 |> assign(:user, user_three)
1552 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1554 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1555 assert id_one == to_string(private_activity.id)
1556 assert id_two == to_string(activity.id)
1559 test "unimplemented pinned statuses feature", %{conn: conn} do
1560 note = insert(:note_activity)
1561 user = User.get_cached_by_ap_id(note.data["actor"])
1565 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1567 assert json_response(conn, 200) == []
1570 test "gets an users media", %{conn: conn} do
1571 note = insert(:note_activity)
1572 user = User.get_cached_by_ap_id(note.data["actor"])
1574 file = %Plug.Upload{
1575 content_type: "image/jpg",
1576 path: Path.absname("test/fixtures/image.jpg"),
1577 filename: "an_image.jpg"
1581 TwitterAPI.upload(file, user, "json")
1585 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1589 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1591 assert [%{"id" => id}] = json_response(conn, 200)
1592 assert id == to_string(image_post.id)
1596 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1598 assert [%{"id" => id}] = json_response(conn, 200)
1599 assert id == to_string(image_post.id)
1602 test "gets a user's statuses without reblogs", %{conn: conn} do
1603 user = insert(:user)
1604 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1605 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1609 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1611 assert [%{"id" => id}] = json_response(conn, 200)
1612 assert id == to_string(post.id)
1616 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1618 assert [%{"id" => id}] = json_response(conn, 200)
1619 assert id == to_string(post.id)
1622 test "filters user's statuses by a hashtag", %{conn: conn} do
1623 user = insert(:user)
1624 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1625 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1629 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1631 assert [%{"id" => id}] = json_response(conn, 200)
1632 assert id == to_string(post.id)
1636 describe "user relationships" do
1637 test "returns the relationships for the current user", %{conn: conn} do
1638 user = insert(:user)
1639 other_user = insert(:user)
1640 {:ok, user} = User.follow(user, other_user)
1644 |> assign(:user, user)
1645 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1647 assert [relationship] = json_response(conn, 200)
1649 assert to_string(other_user.id) == relationship["id"]
1653 describe "media upload" do
1655 upload_config = Pleroma.Config.get([Pleroma.Upload])
1656 proxy_config = Pleroma.Config.get([:media_proxy])
1659 Pleroma.Config.put([Pleroma.Upload], upload_config)
1660 Pleroma.Config.put([:media_proxy], proxy_config)
1663 user = insert(:user)
1667 |> assign(:user, user)
1669 image = %Plug.Upload{
1670 content_type: "image/jpg",
1671 path: Path.absname("test/fixtures/image.jpg"),
1672 filename: "an_image.jpg"
1675 [conn: conn, image: image]
1678 test "returns uploaded image", %{conn: conn, image: image} do
1679 desc = "Description of the image"
1683 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1684 |> json_response(:ok)
1686 assert media["type"] == "image"
1687 assert media["description"] == desc
1690 object = Repo.get(Object, media["id"])
1691 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1694 test "returns proxied url when media proxy is enabled", %{conn: conn, image: image} do
1695 Pleroma.Config.put([Pleroma.Upload, :base_url], "https://media.pleroma.social")
1697 proxy_url = "https://cache.pleroma.social"
1698 Pleroma.Config.put([:media_proxy, :enabled], true)
1699 Pleroma.Config.put([:media_proxy, :base_url], proxy_url)
1703 |> post("/api/v1/media", %{"file" => image})
1704 |> json_response(:ok)
1706 assert String.starts_with?(media["url"], proxy_url)
1709 test "returns media url when proxy is enabled but media url is whitelisted", %{
1713 media_url = "https://media.pleroma.social"
1714 Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
1716 Pleroma.Config.put([:media_proxy, :enabled], true)
1717 Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
1718 Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
1722 |> post("/api/v1/media", %{"file" => image})
1723 |> json_response(:ok)
1725 assert String.starts_with?(media["url"], media_url)
1729 describe "locked accounts" do
1730 test "/api/v1/follow_requests works" do
1731 user = insert(:user, %{info: %User.Info{locked: true}})
1732 other_user = insert(:user)
1734 {:ok, _activity} = ActivityPub.follow(other_user, user)
1736 user = User.get_cached_by_id(user.id)
1737 other_user = User.get_cached_by_id(other_user.id)
1739 assert User.following?(other_user, user) == false
1743 |> assign(:user, user)
1744 |> get("/api/v1/follow_requests")
1746 assert [relationship] = json_response(conn, 200)
1747 assert to_string(other_user.id) == relationship["id"]
1750 test "/api/v1/follow_requests/:id/authorize works" do
1751 user = insert(:user, %{info: %User.Info{locked: true}})
1752 other_user = insert(:user)
1754 {:ok, _activity} = ActivityPub.follow(other_user, user)
1756 user = User.get_cached_by_id(user.id)
1757 other_user = User.get_cached_by_id(other_user.id)
1759 assert User.following?(other_user, user) == false
1763 |> assign(:user, user)
1764 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1766 assert relationship = json_response(conn, 200)
1767 assert to_string(other_user.id) == relationship["id"]
1769 user = User.get_cached_by_id(user.id)
1770 other_user = User.get_cached_by_id(other_user.id)
1772 assert User.following?(other_user, user) == true
1775 test "verify_credentials", %{conn: conn} do
1776 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1780 |> assign(:user, user)
1781 |> get("/api/v1/accounts/verify_credentials")
1783 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1784 assert id == to_string(user.id)
1787 test "/api/v1/follow_requests/:id/reject works" do
1788 user = insert(:user, %{info: %User.Info{locked: true}})
1789 other_user = insert(:user)
1791 {:ok, _activity} = ActivityPub.follow(other_user, user)
1793 user = User.get_cached_by_id(user.id)
1797 |> assign(:user, user)
1798 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1800 assert relationship = json_response(conn, 200)
1801 assert to_string(other_user.id) == relationship["id"]
1803 user = User.get_cached_by_id(user.id)
1804 other_user = User.get_cached_by_id(other_user.id)
1806 assert User.following?(other_user, user) == false
1810 test "account fetching", %{conn: conn} do
1811 user = insert(:user)
1815 |> get("/api/v1/accounts/#{user.id}")
1817 assert %{"id" => id} = json_response(conn, 200)
1818 assert id == to_string(user.id)
1822 |> get("/api/v1/accounts/-1")
1824 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1827 test "account fetching also works nickname", %{conn: conn} do
1828 user = insert(:user)
1832 |> get("/api/v1/accounts/#{user.nickname}")
1834 assert %{"id" => id} = json_response(conn, 200)
1835 assert id == user.id
1838 test "mascot upload", %{conn: conn} do
1839 user = insert(:user)
1841 non_image_file = %Plug.Upload{
1842 content_type: "audio/mpeg",
1843 path: Path.absname("test/fixtures/sound.mp3"),
1844 filename: "sound.mp3"
1849 |> assign(:user, user)
1850 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1852 assert json_response(conn, 415)
1854 file = %Plug.Upload{
1855 content_type: "image/jpg",
1856 path: Path.absname("test/fixtures/image.jpg"),
1857 filename: "an_image.jpg"
1862 |> assign(:user, user)
1863 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1865 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1868 test "mascot retrieving", %{conn: conn} do
1869 user = insert(:user)
1870 # When user hasn't set a mascot, we should just get pleroma tan back
1873 |> assign(:user, user)
1874 |> get("/api/v1/pleroma/mascot")
1876 assert %{"url" => url} = json_response(conn, 200)
1877 assert url =~ "pleroma-fox-tan-smol"
1879 # When a user sets their mascot, we should get that back
1880 file = %Plug.Upload{
1881 content_type: "image/jpg",
1882 path: Path.absname("test/fixtures/image.jpg"),
1883 filename: "an_image.jpg"
1888 |> assign(:user, user)
1889 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1891 assert json_response(conn, 200)
1893 user = User.get_cached_by_id(user.id)
1897 |> assign(:user, user)
1898 |> get("/api/v1/pleroma/mascot")
1900 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1901 assert url =~ "an_image"
1904 test "hashtag timeline", %{conn: conn} do
1905 following = insert(:user)
1908 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1910 {:ok, [_activity]} =
1911 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1915 |> get("/api/v1/timelines/tag/2hu")
1917 assert [%{"id" => id}] = json_response(nconn, 200)
1919 assert id == to_string(activity.id)
1921 # works for different capitalization too
1924 |> get("/api/v1/timelines/tag/2HU")
1926 assert [%{"id" => id}] = json_response(nconn, 200)
1928 assert id == to_string(activity.id)
1932 test "multi-hashtag timeline", %{conn: conn} do
1933 user = insert(:user)
1935 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1936 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1937 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1941 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1943 [status_none, status_test1, status_test] = json_response(any_test, 200)
1945 assert to_string(activity_test.id) == status_test["id"]
1946 assert to_string(activity_test1.id) == status_test1["id"]
1947 assert to_string(activity_none.id) == status_none["id"]
1951 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1953 assert [status_test1] == json_response(restricted_test, 200)
1955 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1957 assert [status_none] == json_response(all_test, 200)
1960 test "getting followers", %{conn: conn} do
1961 user = insert(:user)
1962 other_user = insert(:user)
1963 {:ok, user} = User.follow(user, other_user)
1967 |> get("/api/v1/accounts/#{other_user.id}/followers")
1969 assert [%{"id" => id}] = json_response(conn, 200)
1970 assert id == to_string(user.id)
1973 test "getting followers, hide_followers", %{conn: conn} do
1974 user = insert(:user)
1975 other_user = insert(:user, %{info: %{hide_followers: true}})
1976 {:ok, _user} = User.follow(user, other_user)
1980 |> get("/api/v1/accounts/#{other_user.id}/followers")
1982 assert [] == json_response(conn, 200)
1985 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1986 user = insert(:user)
1987 other_user = insert(:user, %{info: %{hide_followers: true}})
1988 {:ok, _user} = User.follow(user, other_user)
1992 |> assign(:user, other_user)
1993 |> get("/api/v1/accounts/#{other_user.id}/followers")
1995 refute [] == json_response(conn, 200)
1998 test "getting followers, pagination", %{conn: conn} do
1999 user = insert(:user)
2000 follower1 = insert(:user)
2001 follower2 = insert(:user)
2002 follower3 = insert(:user)
2003 {:ok, _} = User.follow(follower1, user)
2004 {:ok, _} = User.follow(follower2, user)
2005 {:ok, _} = User.follow(follower3, user)
2009 |> assign(:user, user)
2013 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
2015 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2016 assert id3 == follower3.id
2017 assert id2 == follower2.id
2021 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
2023 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2024 assert id2 == follower2.id
2025 assert id1 == follower1.id
2029 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
2031 assert [%{"id" => id2}] = json_response(res_conn, 200)
2032 assert id2 == follower2.id
2034 assert [link_header] = get_resp_header(res_conn, "link")
2035 assert link_header =~ ~r/min_id=#{follower2.id}/
2036 assert link_header =~ ~r/max_id=#{follower2.id}/
2039 test "getting following", %{conn: conn} do
2040 user = insert(:user)
2041 other_user = insert(:user)
2042 {:ok, user} = User.follow(user, other_user)
2046 |> get("/api/v1/accounts/#{user.id}/following")
2048 assert [%{"id" => id}] = json_response(conn, 200)
2049 assert id == to_string(other_user.id)
2052 test "getting following, hide_follows", %{conn: conn} do
2053 user = insert(:user, %{info: %{hide_follows: true}})
2054 other_user = insert(:user)
2055 {:ok, user} = User.follow(user, other_user)
2059 |> get("/api/v1/accounts/#{user.id}/following")
2061 assert [] == json_response(conn, 200)
2064 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2065 user = insert(:user, %{info: %{hide_follows: true}})
2066 other_user = insert(:user)
2067 {:ok, user} = User.follow(user, other_user)
2071 |> assign(:user, user)
2072 |> get("/api/v1/accounts/#{user.id}/following")
2074 refute [] == json_response(conn, 200)
2077 test "getting following, pagination", %{conn: conn} do
2078 user = insert(:user)
2079 following1 = insert(:user)
2080 following2 = insert(:user)
2081 following3 = insert(:user)
2082 {:ok, _} = User.follow(user, following1)
2083 {:ok, _} = User.follow(user, following2)
2084 {:ok, _} = User.follow(user, following3)
2088 |> assign(:user, user)
2092 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2094 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2095 assert id3 == following3.id
2096 assert id2 == following2.id
2100 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2102 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2103 assert id2 == following2.id
2104 assert id1 == following1.id
2108 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2110 assert [%{"id" => id2}] = json_response(res_conn, 200)
2111 assert id2 == following2.id
2113 assert [link_header] = get_resp_header(res_conn, "link")
2114 assert link_header =~ ~r/min_id=#{following2.id}/
2115 assert link_header =~ ~r/max_id=#{following2.id}/
2118 test "following / unfollowing a user", %{conn: conn} do
2119 user = insert(:user)
2120 other_user = insert(:user)
2124 |> assign(:user, user)
2125 |> post("/api/v1/accounts/#{other_user.id}/follow")
2127 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2129 user = User.get_cached_by_id(user.id)
2133 |> assign(:user, user)
2134 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2136 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2138 user = User.get_cached_by_id(user.id)
2142 |> assign(:user, user)
2143 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2145 assert %{"id" => id} = json_response(conn, 200)
2146 assert id == to_string(other_user.id)
2149 test "following without reblogs" do
2150 follower = insert(:user)
2151 followed = insert(:user)
2152 other_user = insert(:user)
2156 |> assign(:user, follower)
2157 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2159 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2161 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2162 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2166 |> assign(:user, User.get_cached_by_id(follower.id))
2167 |> get("/api/v1/timelines/home")
2169 assert [] == json_response(conn, 200)
2173 |> assign(:user, follower)
2174 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2176 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2180 |> assign(:user, User.get_cached_by_id(follower.id))
2181 |> get("/api/v1/timelines/home")
2183 expected_activity_id = reblog.id
2184 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2187 test "following / unfollowing errors" do
2188 user = insert(:user)
2192 |> assign(:user, user)
2195 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2196 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2199 user = User.get_cached_by_id(user.id)
2200 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2201 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2203 # self follow via uri
2204 user = User.get_cached_by_id(user.id)
2205 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2206 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2208 # follow non existing user
2209 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2210 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2212 # follow non existing user via uri
2213 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2214 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2216 # unfollow non existing user
2217 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2218 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2221 describe "mute/unmute" do
2222 test "with notifications", %{conn: conn} do
2223 user = insert(:user)
2224 other_user = insert(:user)
2228 |> assign(:user, user)
2229 |> post("/api/v1/accounts/#{other_user.id}/mute")
2231 response = json_response(conn, 200)
2233 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2234 user = User.get_cached_by_id(user.id)
2238 |> assign(:user, user)
2239 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2241 response = json_response(conn, 200)
2242 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2245 test "without notifications", %{conn: conn} do
2246 user = insert(:user)
2247 other_user = insert(:user)
2251 |> assign(:user, user)
2252 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2254 response = json_response(conn, 200)
2256 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2257 user = User.get_cached_by_id(user.id)
2261 |> assign(:user, user)
2262 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2264 response = json_response(conn, 200)
2265 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2269 test "subscribing / unsubscribing to a user", %{conn: conn} do
2270 user = insert(:user)
2271 subscription_target = insert(:user)
2275 |> assign(:user, user)
2276 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2278 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2282 |> assign(:user, user)
2283 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2285 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2288 test "getting a list of mutes", %{conn: conn} do
2289 user = insert(:user)
2290 other_user = insert(:user)
2292 {:ok, user} = User.mute(user, other_user)
2296 |> assign(:user, user)
2297 |> get("/api/v1/mutes")
2299 other_user_id = to_string(other_user.id)
2300 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2303 test "blocking / unblocking a user", %{conn: conn} do
2304 user = insert(:user)
2305 other_user = insert(:user)
2309 |> assign(:user, user)
2310 |> post("/api/v1/accounts/#{other_user.id}/block")
2312 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2314 user = User.get_cached_by_id(user.id)
2318 |> assign(:user, user)
2319 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2321 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2324 test "getting a list of blocks", %{conn: conn} do
2325 user = insert(:user)
2326 other_user = insert(:user)
2328 {:ok, user} = User.block(user, other_user)
2332 |> assign(:user, user)
2333 |> get("/api/v1/blocks")
2335 other_user_id = to_string(other_user.id)
2336 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2339 test "blocking / unblocking a domain", %{conn: conn} do
2340 user = insert(:user)
2341 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2345 |> assign(:user, user)
2346 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2348 assert %{} = json_response(conn, 200)
2349 user = User.get_cached_by_ap_id(user.ap_id)
2350 assert User.blocks?(user, other_user)
2354 |> assign(:user, user)
2355 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2357 assert %{} = json_response(conn, 200)
2358 user = User.get_cached_by_ap_id(user.ap_id)
2359 refute User.blocks?(user, other_user)
2362 test "getting a list of domain blocks", %{conn: conn} do
2363 user = insert(:user)
2365 {:ok, user} = User.block_domain(user, "bad.site")
2366 {:ok, user} = User.block_domain(user, "even.worse.site")
2370 |> assign(:user, user)
2371 |> get("/api/v1/domain_blocks")
2373 domain_blocks = json_response(conn, 200)
2375 assert "bad.site" in domain_blocks
2376 assert "even.worse.site" in domain_blocks
2379 test "unimplemented follow_requests, blocks, domain blocks" do
2380 user = insert(:user)
2382 ["blocks", "domain_blocks", "follow_requests"]
2383 |> Enum.each(fn endpoint ->
2386 |> assign(:user, user)
2387 |> get("/api/v1/#{endpoint}")
2389 assert [] = json_response(conn, 200)
2393 test "returns the favorites of a user", %{conn: conn} do
2394 user = insert(:user)
2395 other_user = insert(:user)
2397 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2398 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2400 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2404 |> assign(:user, user)
2405 |> get("/api/v1/favourites")
2407 assert [status] = json_response(first_conn, 200)
2408 assert status["id"] == to_string(activity.id)
2410 assert [{"link", _link_header}] =
2411 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2413 # Honours query params
2414 {:ok, second_activity} =
2415 CommonAPI.post(other_user, %{
2417 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2420 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2422 last_like = status["id"]
2426 |> assign(:user, user)
2427 |> get("/api/v1/favourites?since_id=#{last_like}")
2429 assert [second_status] = json_response(second_conn, 200)
2430 assert second_status["id"] == to_string(second_activity.id)
2434 |> assign(:user, user)
2435 |> get("/api/v1/favourites?limit=0")
2437 assert [] = json_response(third_conn, 200)
2440 describe "getting favorites timeline of specified user" do
2442 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2443 [current_user: current_user, user: user]
2446 test "returns list of statuses favorited by specified user", %{
2448 current_user: current_user,
2451 [activity | _] = insert_pair(:note_activity)
2452 CommonAPI.favorite(activity.id, user)
2456 |> assign(:user, current_user)
2457 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2458 |> json_response(:ok)
2462 assert length(response) == 1
2463 assert like["id"] == activity.id
2466 test "returns favorites for specified user_id when user is not logged in", %{
2470 activity = insert(:note_activity)
2471 CommonAPI.favorite(activity.id, user)
2475 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2476 |> json_response(:ok)
2478 assert length(response) == 1
2481 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2483 current_user: current_user,
2487 CommonAPI.post(current_user, %{
2488 "status" => "Hi @#{user.nickname}!",
2489 "visibility" => "direct"
2492 CommonAPI.favorite(direct.id, user)
2496 |> assign(:user, current_user)
2497 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2498 |> json_response(:ok)
2500 assert length(response) == 1
2502 anonymous_response =
2504 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2505 |> json_response(:ok)
2507 assert Enum.empty?(anonymous_response)
2510 test "does not return others' favorited DM when user is not one of recipients", %{
2512 current_user: current_user,
2515 user_two = insert(:user)
2518 CommonAPI.post(user_two, %{
2519 "status" => "Hi @#{user.nickname}!",
2520 "visibility" => "direct"
2523 CommonAPI.favorite(direct.id, user)
2527 |> assign(:user, current_user)
2528 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2529 |> json_response(:ok)
2531 assert Enum.empty?(response)
2534 test "paginates favorites using since_id and max_id", %{
2536 current_user: current_user,
2539 activities = insert_list(10, :note_activity)
2541 Enum.each(activities, fn activity ->
2542 CommonAPI.favorite(activity.id, user)
2545 third_activity = Enum.at(activities, 2)
2546 seventh_activity = Enum.at(activities, 6)
2550 |> assign(:user, current_user)
2551 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2552 since_id: third_activity.id,
2553 max_id: seventh_activity.id
2555 |> json_response(:ok)
2557 assert length(response) == 3
2558 refute third_activity in response
2559 refute seventh_activity in response
2562 test "limits favorites using limit parameter", %{
2564 current_user: current_user,
2568 |> insert_list(:note_activity)
2569 |> Enum.each(fn activity ->
2570 CommonAPI.favorite(activity.id, user)
2575 |> assign(:user, current_user)
2576 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2577 |> json_response(:ok)
2579 assert length(response) == 3
2582 test "returns empty response when user does not have any favorited statuses", %{
2584 current_user: current_user,
2589 |> assign(:user, current_user)
2590 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2591 |> json_response(:ok)
2593 assert Enum.empty?(response)
2596 test "returns 404 error when specified user is not exist", %{conn: conn} do
2597 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2599 assert json_response(conn, 404) == %{"error" => "Record not found"}
2602 test "returns 403 error when user has hidden own favorites", %{
2604 current_user: current_user
2606 user = insert(:user, %{info: %{hide_favorites: true}})
2607 activity = insert(:note_activity)
2608 CommonAPI.favorite(activity.id, user)
2612 |> assign(:user, current_user)
2613 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2615 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2618 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2619 user = insert(:user)
2620 activity = insert(:note_activity)
2621 CommonAPI.favorite(activity.id, user)
2625 |> assign(:user, current_user)
2626 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2628 assert user.info.hide_favorites
2629 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2633 test "get instance information", %{conn: conn} do
2634 conn = get(conn, "/api/v1/instance")
2635 assert result = json_response(conn, 200)
2637 email = Pleroma.Config.get([:instance, :email])
2638 # Note: not checking for "max_toot_chars" since it's optional
2644 "email" => from_config_email,
2646 "streaming_api" => _
2651 "registrations" => _,
2655 assert email == from_config_email
2658 test "get instance stats", %{conn: conn} do
2659 user = insert(:user, %{local: true})
2661 user2 = insert(:user, %{local: true})
2662 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2664 insert(:user, %{local: false, nickname: "u@peer1.com"})
2665 insert(:user, %{local: false, nickname: "u@peer2.com"})
2667 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2669 # Stats should count users with missing or nil `info.deactivated` value
2670 user = User.get_cached_by_id(user.id)
2671 info_change = Changeset.change(user.info, %{deactivated: nil})
2675 |> Changeset.change()
2676 |> Changeset.put_embed(:info, info_change)
2677 |> User.update_and_set_cache()
2679 Pleroma.Stats.update_stats()
2681 conn = get(conn, "/api/v1/instance")
2683 assert result = json_response(conn, 200)
2685 stats = result["stats"]
2688 assert stats["user_count"] == 1
2689 assert stats["status_count"] == 1
2690 assert stats["domain_count"] == 2
2693 test "get peers", %{conn: conn} do
2694 insert(:user, %{local: false, nickname: "u@peer1.com"})
2695 insert(:user, %{local: false, nickname: "u@peer2.com"})
2697 Pleroma.Stats.update_stats()
2699 conn = get(conn, "/api/v1/instance/peers")
2701 assert result = json_response(conn, 200)
2703 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2706 test "put settings", %{conn: conn} do
2707 user = insert(:user)
2711 |> assign(:user, user)
2712 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2714 assert _result = json_response(conn, 200)
2716 user = User.get_cached_by_ap_id(user.ap_id)
2717 assert user.info.settings == %{"programming" => "socks"}
2720 describe "pinned statuses" do
2722 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2724 user = insert(:user)
2725 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2727 [user: user, activity: activity]
2730 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2731 {:ok, _} = CommonAPI.pin(activity.id, user)
2735 |> assign(:user, user)
2736 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2737 |> json_response(200)
2739 id_str = to_string(activity.id)
2741 assert [%{"id" => ^id_str, "pinned" => true}] = result
2744 test "pin status", %{conn: conn, user: user, activity: activity} do
2745 id_str = to_string(activity.id)
2747 assert %{"id" => ^id_str, "pinned" => true} =
2749 |> assign(:user, user)
2750 |> post("/api/v1/statuses/#{activity.id}/pin")
2751 |> json_response(200)
2753 assert [%{"id" => ^id_str, "pinned" => true}] =
2755 |> assign(:user, user)
2756 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2757 |> json_response(200)
2760 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2761 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2765 |> assign(:user, user)
2766 |> post("/api/v1/statuses/#{dm.id}/pin")
2768 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2771 test "unpin status", %{conn: conn, user: user, activity: activity} do
2772 {:ok, _} = CommonAPI.pin(activity.id, user)
2774 id_str = to_string(activity.id)
2775 user = refresh_record(user)
2777 assert %{"id" => ^id_str, "pinned" => false} =
2779 |> assign(:user, user)
2780 |> post("/api/v1/statuses/#{activity.id}/unpin")
2781 |> json_response(200)
2785 |> assign(:user, user)
2786 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2787 |> json_response(200)
2790 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2793 |> assign(:user, user)
2794 |> post("/api/v1/statuses/1/unpin")
2796 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2799 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2800 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2802 id_str_one = to_string(activity_one.id)
2804 assert %{"id" => ^id_str_one, "pinned" => true} =
2806 |> assign(:user, user)
2807 |> post("/api/v1/statuses/#{id_str_one}/pin")
2808 |> json_response(200)
2810 user = refresh_record(user)
2812 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2814 |> assign(:user, user)
2815 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2816 |> json_response(400)
2822 Pleroma.Config.put([:rich_media, :enabled], true)
2825 Pleroma.Config.put([:rich_media, :enabled], false)
2828 user = insert(:user)
2832 test "returns rich-media card", %{conn: conn, user: user} do
2833 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2836 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2837 "provider_name" => "example.com",
2838 "provider_url" => "https://example.com",
2839 "title" => "The Rock",
2841 "url" => "https://example.com/ogp",
2843 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2846 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2847 "title" => "The Rock",
2848 "type" => "video.movie",
2849 "url" => "https://example.com/ogp",
2851 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2858 |> get("/api/v1/statuses/#{activity.id}/card")
2859 |> json_response(200)
2861 assert response == card_data
2863 # works with private posts
2865 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2869 |> assign(:user, user)
2870 |> get("/api/v1/statuses/#{activity.id}/card")
2871 |> json_response(200)
2873 assert response_two == card_data
2876 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2878 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2882 |> get("/api/v1/statuses/#{activity.id}/card")
2883 |> json_response(:ok)
2885 assert response == %{
2887 "title" => "Pleroma",
2888 "description" => "",
2890 "provider_name" => "example.com",
2891 "provider_url" => "https://example.com",
2892 "url" => "https://example.com/ogp-missing-data",
2895 "title" => "Pleroma",
2896 "type" => "website",
2897 "url" => "https://example.com/ogp-missing-data"
2905 user = insert(:user)
2906 for_user = insert(:user)
2909 CommonAPI.post(user, %{
2910 "status" => "heweoo?"
2914 CommonAPI.post(user, %{
2915 "status" => "heweoo!"
2920 |> assign(:user, for_user)
2921 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2923 assert json_response(response1, 200)["bookmarked"] == true
2927 |> assign(:user, for_user)
2928 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2930 assert json_response(response2, 200)["bookmarked"] == true
2934 |> assign(:user, for_user)
2935 |> get("/api/v1/bookmarks")
2937 assert [json_response(response2, 200), json_response(response1, 200)] ==
2938 json_response(bookmarks, 200)
2942 |> assign(:user, for_user)
2943 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2945 assert json_response(response1, 200)["bookmarked"] == false
2949 |> assign(:user, for_user)
2950 |> get("/api/v1/bookmarks")
2952 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2955 describe "conversation muting" do
2957 user = insert(:user)
2958 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2960 [user: user, activity: activity]
2963 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2964 id_str = to_string(activity.id)
2966 assert %{"id" => ^id_str, "muted" => true} =
2968 |> assign(:user, user)
2969 |> post("/api/v1/statuses/#{activity.id}/mute")
2970 |> json_response(200)
2973 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2974 {:ok, _} = CommonAPI.add_mute(user, activity)
2978 |> assign(:user, user)
2979 |> post("/api/v1/statuses/#{activity.id}/mute")
2981 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2984 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2985 {:ok, _} = CommonAPI.add_mute(user, activity)
2987 id_str = to_string(activity.id)
2988 user = refresh_record(user)
2990 assert %{"id" => ^id_str, "muted" => false} =
2992 |> assign(:user, user)
2993 |> post("/api/v1/statuses/#{activity.id}/unmute")
2994 |> json_response(200)
2998 describe "reports" do
3000 reporter = insert(:user)
3001 target_user = insert(:user)
3003 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
3005 [reporter: reporter, target_user: target_user, activity: activity]
3008 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
3009 assert %{"action_taken" => false, "id" => _} =
3011 |> assign(:user, reporter)
3012 |> post("/api/v1/reports", %{"account_id" => target_user.id})
3013 |> json_response(200)
3016 test "submit a report with statuses and comment", %{
3019 target_user: target_user,
3022 assert %{"action_taken" => false, "id" => _} =
3024 |> assign(:user, reporter)
3025 |> post("/api/v1/reports", %{
3026 "account_id" => target_user.id,
3027 "status_ids" => [activity.id],
3028 "comment" => "bad status!",
3029 "forward" => "false"
3031 |> json_response(200)
3034 test "account_id is required", %{
3039 assert %{"error" => "Valid `account_id` required"} =
3041 |> assign(:user, reporter)
3042 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3043 |> json_response(400)
3046 test "comment must be up to the size specified in the config", %{
3049 target_user: target_user
3051 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
3052 comment = String.pad_trailing("a", max_size + 1, "a")
3054 error = %{"error" => "Comment must be up to #{max_size} characters"}
3058 |> assign(:user, reporter)
3059 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3060 |> json_response(400)
3063 test "returns error when account is not exist", %{
3070 |> assign(:user, reporter)
3071 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3073 assert json_response(conn, 400) == %{"error" => "Account not found"}
3077 describe "link headers" do
3078 test "preserves parameters in link headers", %{conn: conn} do
3079 user = insert(:user)
3080 other_user = insert(:user)
3083 CommonAPI.post(other_user, %{
3084 "status" => "hi @#{user.nickname}",
3085 "visibility" => "public"
3089 CommonAPI.post(other_user, %{
3090 "status" => "hi @#{user.nickname}",
3091 "visibility" => "public"
3094 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3095 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3099 |> assign(:user, user)
3100 |> get("/api/v1/notifications", %{media_only: true})
3102 assert [link_header] = get_resp_header(conn, "link")
3103 assert link_header =~ ~r/media_only=true/
3104 assert link_header =~ ~r/min_id=#{notification2.id}/
3105 assert link_header =~ ~r/max_id=#{notification1.id}/
3109 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3110 # Need to set an old-style integer ID to reproduce the problem
3111 # (these are no longer assigned to new accounts but were preserved
3112 # for existing accounts during the migration to flakeIDs)
3113 user_one = insert(:user, %{id: 1212})
3114 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3118 |> get("/api/v1/accounts/#{user_one.id}")
3122 |> get("/api/v1/accounts/#{user_two.nickname}")
3126 |> get("/api/v1/accounts/#{user_two.id}")
3128 acc_one = json_response(resp_one, 200)
3129 acc_two = json_response(resp_two, 200)
3130 acc_three = json_response(resp_three, 200)
3131 refute acc_one == acc_two
3132 assert acc_two == acc_three
3135 describe "custom emoji" do
3136 test "with tags", %{conn: conn} do
3139 |> get("/api/v1/custom_emojis")
3140 |> json_response(200)
3142 assert Map.has_key?(emoji, "shortcode")
3143 assert Map.has_key?(emoji, "static_url")
3144 assert Map.has_key?(emoji, "tags")
3145 assert is_list(emoji["tags"])
3146 assert Map.has_key?(emoji, "category")
3147 assert Map.has_key?(emoji, "url")
3148 assert Map.has_key?(emoji, "visible_in_picker")
3152 describe "index/2 redirections" do
3153 setup %{conn: conn} do
3157 signing_salt: "cooldude"
3162 |> Plug.Session.call(Plug.Session.init(session_opts))
3165 test_path = "/web/statuses/test"
3166 %{conn: conn, path: test_path}
3169 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3170 conn = get(conn, path)
3172 assert conn.status == 302
3173 assert redirected_to(conn) == "/web/login"
3176 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3177 token = insert(:oauth_token)
3181 |> assign(:user, token.user)
3182 |> put_session(:oauth_token, token.token)
3185 assert conn.status == 200
3188 test "saves referer path to session", %{conn: conn, path: path} do
3189 conn = get(conn, path)
3190 return_to = Plug.Conn.get_session(conn, :return_to)
3192 assert return_to == path
3195 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3196 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3197 auth = insert(:oauth_authorization, app: app)
3201 |> put_session(:return_to, path)
3202 |> get("/web/login", %{code: auth.token})
3204 assert conn.status == 302
3205 assert redirected_to(conn) == path
3208 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3209 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3210 auth = insert(:oauth_authorization, app: app)
3212 conn = get(conn, "/web/login", %{code: auth.token})
3214 assert conn.status == 302
3215 assert redirected_to(conn) == "/web/getting-started"
3219 describe "scheduled activities" do
3220 test "creates a scheduled activity", %{conn: conn} do
3221 user = insert(:user)
3222 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3226 |> assign(:user, user)
3227 |> post("/api/v1/statuses", %{
3228 "status" => "scheduled",
3229 "scheduled_at" => scheduled_at
3232 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3233 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3234 assert [] == Repo.all(Activity)
3237 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3238 user = insert(:user)
3239 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3241 file = %Plug.Upload{
3242 content_type: "image/jpg",
3243 path: Path.absname("test/fixtures/image.jpg"),
3244 filename: "an_image.jpg"
3247 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3251 |> assign(:user, user)
3252 |> post("/api/v1/statuses", %{
3253 "media_ids" => [to_string(upload.id)],
3254 "status" => "scheduled",
3255 "scheduled_at" => scheduled_at
3258 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3259 assert %{"type" => "image"} = media_attachment
3262 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3264 user = insert(:user)
3267 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3271 |> assign(:user, user)
3272 |> post("/api/v1/statuses", %{
3273 "status" => "not scheduled",
3274 "scheduled_at" => scheduled_at
3277 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3278 assert [] == Repo.all(ScheduledActivity)
3281 test "returns error when daily user limit is exceeded", %{conn: conn} do
3282 user = insert(:user)
3285 NaiveDateTime.utc_now()
3286 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3287 |> NaiveDateTime.to_iso8601()
3289 attrs = %{params: %{}, scheduled_at: today}
3290 {:ok, _} = ScheduledActivity.create(user, attrs)
3291 {:ok, _} = ScheduledActivity.create(user, attrs)
3295 |> assign(:user, user)
3296 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3298 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3301 test "returns error when total user limit is exceeded", %{conn: conn} do
3302 user = insert(:user)
3305 NaiveDateTime.utc_now()
3306 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3307 |> NaiveDateTime.to_iso8601()
3310 NaiveDateTime.utc_now()
3311 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3312 |> NaiveDateTime.to_iso8601()
3314 attrs = %{params: %{}, scheduled_at: today}
3315 {:ok, _} = ScheduledActivity.create(user, attrs)
3316 {:ok, _} = ScheduledActivity.create(user, attrs)
3317 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3321 |> assign(:user, user)
3322 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3324 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3327 test "shows scheduled activities", %{conn: conn} do
3328 user = insert(:user)
3329 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3330 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3331 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3332 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3336 |> assign(:user, user)
3341 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3343 result = json_response(conn_res, 200)
3344 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3349 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3351 result = json_response(conn_res, 200)
3352 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3357 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3359 result = json_response(conn_res, 200)
3360 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3363 test "shows a scheduled activity", %{conn: conn} do
3364 user = insert(:user)
3365 scheduled_activity = insert(:scheduled_activity, user: user)
3369 |> assign(:user, user)
3370 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3372 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3373 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3377 |> assign(:user, user)
3378 |> get("/api/v1/scheduled_statuses/404")
3380 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3383 test "updates a scheduled activity", %{conn: conn} do
3384 user = insert(:user)
3385 scheduled_activity = insert(:scheduled_activity, user: user)
3388 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3392 |> assign(:user, user)
3393 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3394 scheduled_at: new_scheduled_at
3397 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3398 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3402 |> assign(:user, user)
3403 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3405 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3408 test "deletes a scheduled activity", %{conn: conn} do
3409 user = insert(:user)
3410 scheduled_activity = insert(:scheduled_activity, user: user)
3414 |> assign(:user, user)
3415 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3417 assert %{} = json_response(res_conn, 200)
3418 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3422 |> assign(:user, user)
3423 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3425 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3429 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3430 user1 = insert(:user)
3431 user2 = insert(:user)
3432 user3 = insert(:user)
3434 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3436 # Reply to status from another user
3439 |> assign(:user, user2)
3440 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3442 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3444 activity = Activity.get_by_id_with_object(id)
3446 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3447 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3449 # Reblog from the third user
3452 |> assign(:user, user3)
3453 |> post("/api/v1/statuses/#{activity.id}/reblog")
3455 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3456 json_response(conn2, 200)
3458 assert to_string(activity.id) == id
3460 # Getting third user status
3463 |> assign(:user, user3)
3464 |> get("api/v1/timelines/home")
3466 [reblogged_activity] = json_response(conn3, 200)
3468 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3470 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3471 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3474 describe "create account by app" do
3475 test "Account registration via Application", %{conn: conn} do
3478 |> post("/api/v1/apps", %{
3479 client_name: "client_name",
3480 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3481 scopes: "read, write, follow"
3485 "client_id" => client_id,
3486 "client_secret" => client_secret,
3488 "name" => "client_name",
3489 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3492 } = json_response(conn, 200)
3496 |> post("/oauth/token", %{
3497 grant_type: "client_credentials",
3498 client_id: client_id,
3499 client_secret: client_secret
3502 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3503 json_response(conn, 200)
3506 token_from_db = Repo.get_by(Token, token: token)
3507 assert token_from_db
3509 assert scope == "read write follow"
3513 |> put_req_header("authorization", "Bearer " <> token)
3514 |> post("/api/v1/accounts", %{
3516 email: "lain@example.org",
3517 password: "PlzDontHackLain",
3522 "access_token" => token,
3523 "created_at" => _created_at,
3525 "token_type" => "Bearer"
3526 } = json_response(conn, 200)
3528 token_from_db = Repo.get_by(Token, token: token)
3529 assert token_from_db
3530 token_from_db = Repo.preload(token_from_db, :user)
3531 assert token_from_db.user
3533 assert token_from_db.user.info.confirmation_pending
3536 test "rate limit", %{conn: conn} do
3537 app_token = insert(:oauth_token, user: nil)
3540 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3541 |> Map.put(:remote_ip, {15, 15, 15, 15})
3546 |> post("/api/v1/accounts", %{
3547 username: "#{i}lain",
3548 email: "#{i}lain@example.org",
3549 password: "PlzDontHackLain",
3554 "access_token" => token,
3555 "created_at" => _created_at,
3557 "token_type" => "Bearer"
3558 } = json_response(conn, 200)
3560 token_from_db = Repo.get_by(Token, token: token)
3561 assert token_from_db
3562 token_from_db = Repo.preload(token_from_db, :user)
3563 assert token_from_db.user
3565 assert token_from_db.user.info.confirmation_pending
3570 |> post("/api/v1/accounts", %{
3572 email: "6lain@example.org",
3573 password: "PlzDontHackLain",
3577 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3581 describe "GET /api/v1/polls/:id" do
3582 test "returns poll entity for object id", %{conn: conn} do
3583 user = insert(:user)
3586 CommonAPI.post(user, %{
3587 "status" => "Pleroma does",
3588 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3591 object = Object.normalize(activity)
3595 |> assign(:user, user)
3596 |> get("/api/v1/polls/#{object.id}")
3598 response = json_response(conn, 200)
3599 id = to_string(object.id)
3600 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3603 test "does not expose polls for private statuses", %{conn: conn} do
3604 user = insert(:user)
3605 other_user = insert(:user)
3608 CommonAPI.post(user, %{
3609 "status" => "Pleroma does",
3610 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3611 "visibility" => "private"
3614 object = Object.normalize(activity)
3618 |> assign(:user, other_user)
3619 |> get("/api/v1/polls/#{object.id}")
3621 assert json_response(conn, 404)
3625 describe "POST /api/v1/polls/:id/votes" do
3626 test "votes are added to the poll", %{conn: conn} do
3627 user = insert(:user)
3628 other_user = insert(:user)
3631 CommonAPI.post(user, %{
3632 "status" => "A very delicious sandwich",
3634 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3640 object = Object.normalize(activity)
3644 |> assign(:user, other_user)
3645 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3647 assert json_response(conn, 200)
3648 object = Object.get_by_id(object.id)
3650 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3655 test "author can't vote", %{conn: conn} do
3656 user = insert(:user)
3659 CommonAPI.post(user, %{
3660 "status" => "Am I cute?",
3661 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3664 object = Object.normalize(activity)
3667 |> assign(:user, user)
3668 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3669 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3671 object = Object.get_by_id(object.id)
3673 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3676 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3677 user = insert(:user)
3678 other_user = insert(:user)
3681 CommonAPI.post(user, %{
3682 "status" => "The glass is",
3683 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3686 object = Object.normalize(activity)
3689 |> assign(:user, other_user)
3690 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3691 |> json_response(422) == %{"error" => "Too many choices"}
3693 object = Object.get_by_id(object.id)
3695 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3700 test "does not allow choice index to be greater than options count", %{conn: conn} do
3701 user = insert(:user)
3702 other_user = insert(:user)
3705 CommonAPI.post(user, %{
3706 "status" => "Am I cute?",
3707 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3710 object = Object.normalize(activity)
3714 |> assign(:user, other_user)
3715 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3717 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3720 test "returns 404 error when object is not exist", %{conn: conn} do
3721 user = insert(:user)
3725 |> assign(:user, user)
3726 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3728 assert json_response(conn, 404) == %{"error" => "Record not found"}
3731 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3732 user = insert(:user)
3733 other_user = insert(:user)
3736 CommonAPI.post(user, %{
3737 "status" => "Am I cute?",
3738 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3739 "visibility" => "private"
3742 object = Object.normalize(activity)
3746 |> assign(:user, other_user)
3747 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3749 assert json_response(conn, 404) == %{"error" => "Record not found"}
3753 describe "GET /api/v1/statuses/:id/favourited_by" do
3755 user = insert(:user)
3756 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3760 |> assign(:user, user)
3762 [conn: conn, activity: activity]
3765 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3766 other_user = insert(:user)
3767 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3771 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3772 |> json_response(:ok)
3774 [%{"id" => id}] = response
3776 assert id == other_user.id
3779 test "returns empty array when status has not been favorited yet", %{
3785 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3786 |> json_response(:ok)
3788 assert Enum.empty?(response)
3791 test "does not return users who have favorited the status but are blocked", %{
3792 conn: %{assigns: %{user: user}} = conn,
3795 other_user = insert(:user)
3796 {:ok, user} = User.block(user, other_user)
3798 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3802 |> assign(:user, user)
3803 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3804 |> json_response(:ok)
3806 assert Enum.empty?(response)
3809 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3810 other_user = insert(:user)
3811 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3815 |> assign(:user, nil)
3816 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3817 |> json_response(:ok)
3819 [%{"id" => id}] = response
3820 assert id == other_user.id
3824 describe "GET /api/v1/statuses/:id/reblogged_by" do
3826 user = insert(:user)
3827 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3831 |> assign(:user, user)
3833 [conn: conn, activity: activity]
3836 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3837 other_user = insert(:user)
3838 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3842 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3843 |> json_response(:ok)
3845 [%{"id" => id}] = response
3847 assert id == other_user.id
3850 test "returns empty array when status has not been reblogged yet", %{
3856 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3857 |> json_response(:ok)
3859 assert Enum.empty?(response)
3862 test "does not return users who have reblogged the status but are blocked", %{
3863 conn: %{assigns: %{user: user}} = conn,
3866 other_user = insert(:user)
3867 {:ok, user} = User.block(user, other_user)
3869 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3873 |> assign(:user, user)
3874 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3875 |> json_response(:ok)
3877 assert Enum.empty?(response)
3880 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3881 other_user = insert(:user)
3882 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3886 |> assign(:user, nil)
3887 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3888 |> json_response(:ok)
3890 [%{"id" => id}] = response
3891 assert id == other_user.id
3895 describe "POST /auth/password, with valid parameters" do
3896 setup %{conn: conn} do
3897 user = insert(:user)
3898 conn = post(conn, "/auth/password?email=#{user.email}")
3899 %{conn: conn, user: user}
3902 test "it returns 204", %{conn: conn} do
3903 assert json_response(conn, :no_content)
3906 test "it creates a PasswordResetToken record for user", %{user: user} do
3907 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3911 test "it sends an email to user", %{user: user} do
3912 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3914 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3915 notify_email = Pleroma.Config.get([:instance, :notify_email])
3916 instance_name = Pleroma.Config.get([:instance, :name])
3919 from: {instance_name, notify_email},
3920 to: {user.name, user.email},
3921 html_body: email.html_body
3926 describe "POST /auth/password, with invalid parameters" do
3928 user = insert(:user)
3932 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3933 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3934 assert conn.status == 404
3935 assert conn.resp_body == ""
3938 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3939 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3940 conn = post(conn, "/auth/password?email=#{user.email}")
3941 assert conn.status == 400
3942 assert conn.resp_body == ""