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
12 alias Pleroma.Notification
15 alias Pleroma.ScheduledActivity
16 alias Pleroma.Tests.ObanHelpers
18 alias Pleroma.Web.ActivityPub.ActivityPub
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.MastodonAPI.FilterView
21 alias Pleroma.Web.OAuth.App
22 alias Pleroma.Web.OAuth.Token
23 alias Pleroma.Web.OStatus
24 alias Pleroma.Web.Push
25 import Pleroma.Factory
26 import ExUnit.CaptureLog
28 import Swoosh.TestAssertions
30 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
33 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
37 clear_config([:instance, :public])
38 clear_config([:rich_media, :enabled])
40 test "the home timeline", %{conn: conn} do
42 following = insert(:user)
44 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
51 assert Enum.empty?(json_response(conn, 200))
53 {:ok, user} = User.follow(user, following)
57 |> assign(:user, user)
58 |> get("/api/v1/timelines/home")
60 assert [%{"content" => "test"}] = json_response(conn, 200)
63 test "the public timeline", %{conn: conn} do
64 following = insert(:user)
67 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
70 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
74 |> get("/api/v1/timelines/public", %{"local" => "False"})
76 assert length(json_response(conn, 200)) == 2
80 |> get("/api/v1/timelines/public", %{"local" => "True"})
82 assert [%{"content" => "test"}] = json_response(conn, 200)
86 |> get("/api/v1/timelines/public", %{"local" => "1"})
88 assert [%{"content" => "test"}] = json_response(conn, 200)
92 test "the public timeline when public is set to false", %{conn: conn} do
93 Config.put([:instance, :public], false)
96 |> get("/api/v1/timelines/public", %{"local" => "False"})
97 |> json_response(403) == %{"error" => "This resource requires authentication."}
100 test "the public timeline includes only public statuses for an authenticated user" do
105 |> assign(:user, user)
107 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test"})
108 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "private"})
109 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "unlisted"})
110 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
112 res_conn = get(conn, "/api/v1/timelines/public")
113 assert length(json_response(res_conn, 200)) == 1
116 describe "posting statuses" do
122 |> assign(:user, user)
127 test "posting a status", %{conn: conn} do
128 idempotency_key = "Pikachu rocks!"
132 |> put_req_header("idempotency-key", idempotency_key)
133 |> post("/api/v1/statuses", %{
135 "spoiler_text" => "2hu",
136 "sensitive" => "false"
139 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
141 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
143 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
144 json_response(conn_one, 200)
146 assert Activity.get_by_id(id)
150 |> put_req_header("idempotency-key", idempotency_key)
151 |> post("/api/v1/statuses", %{
153 "spoiler_text" => "2hu",
154 "sensitive" => "false"
157 assert %{"id" => second_id} = json_response(conn_two, 200)
158 assert id == second_id
162 |> post("/api/v1/statuses", %{
164 "spoiler_text" => "2hu",
165 "sensitive" => "false"
168 assert %{"id" => third_id} = json_response(conn_three, 200)
169 refute id == third_id
171 # An activity that will expire:
173 expires_in = 120 * 60
177 |> post("api/v1/statuses", %{
178 "status" => "oolong",
179 "expires_in" => expires_in
182 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
183 assert activity = Activity.get_by_id(fourth_id)
184 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
186 estimated_expires_at =
187 NaiveDateTime.utc_now()
188 |> NaiveDateTime.add(expires_in)
189 |> NaiveDateTime.truncate(:second)
191 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
192 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
194 assert fourth_response["pleroma"]["expires_at"] ==
195 NaiveDateTime.to_iso8601(expiration.scheduled_at)
198 test "replying to a status", %{conn: conn} do
200 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
204 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
206 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
208 activity = Activity.get_by_id(id)
210 assert activity.data["context"] == replied_to.data["context"]
211 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
214 test "replying to a direct message with visibility other than direct", %{conn: conn} do
216 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
218 Enum.each(["public", "private", "unlisted"], fn visibility ->
221 |> post("/api/v1/statuses", %{
222 "status" => "@#{user.nickname} hey",
223 "in_reply_to_id" => replied_to.id,
224 "visibility" => visibility
227 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
231 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
234 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
236 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
237 assert Activity.get_by_id(id)
240 test "posting a sensitive status", %{conn: conn} do
243 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
245 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
246 assert Activity.get_by_id(id)
249 test "posting a fake status", %{conn: conn} do
252 |> post("/api/v1/statuses", %{
254 "\"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"
257 real_status = json_response(real_conn, 200)
260 assert Object.get_by_ap_id(real_status["uri"])
264 |> Map.put("id", nil)
265 |> Map.put("url", nil)
266 |> Map.put("uri", nil)
267 |> Map.put("created_at", nil)
268 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
272 |> post("/api/v1/statuses", %{
274 "\"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",
278 fake_status = json_response(fake_conn, 200)
281 refute Object.get_by_ap_id(fake_status["uri"])
285 |> Map.put("id", nil)
286 |> Map.put("url", nil)
287 |> Map.put("uri", nil)
288 |> Map.put("created_at", nil)
289 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
291 assert real_status == fake_status
294 test "posting a status with OGP link preview", %{conn: conn} do
295 Config.put([:rich_media, :enabled], true)
299 |> post("/api/v1/statuses", %{
300 "status" => "https://example.com/ogp"
303 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
304 assert Activity.get_by_id(id)
307 test "posting a direct status", %{conn: conn} do
308 user2 = insert(:user)
309 content = "direct cofe @#{user2.nickname}"
313 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
315 assert %{"id" => id} = response = json_response(conn, 200)
316 assert response["visibility"] == "direct"
317 assert response["pleroma"]["direct_conversation_id"]
318 assert activity = Activity.get_by_id(id)
319 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
320 assert activity.data["to"] == [user2.ap_id]
321 assert activity.data["cc"] == []
325 describe "posting polls" do
326 test "posting a poll", %{conn: conn} do
328 time = NaiveDateTime.utc_now()
332 |> assign(:user, user)
333 |> post("/api/v1/statuses", %{
334 "status" => "Who is the #bestgrill?",
335 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
338 response = json_response(conn, 200)
340 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
341 title in ["Rei", "Asuka", "Misato"]
344 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
345 refute response["poll"]["expred"]
348 test "option limit is enforced", %{conn: conn} do
350 limit = Config.get([:instance, :poll_limits, :max_options])
354 |> assign(:user, user)
355 |> post("/api/v1/statuses", %{
357 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
360 %{"error" => error} = json_response(conn, 422)
361 assert error == "Poll can't contain more than #{limit} options"
364 test "option character limit is enforced", %{conn: conn} do
366 limit = Config.get([:instance, :poll_limits, :max_option_chars])
370 |> assign(:user, user)
371 |> post("/api/v1/statuses", %{
374 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
379 %{"error" => error} = json_response(conn, 422)
380 assert error == "Poll options cannot be longer than #{limit} characters each"
383 test "minimal date limit is enforced", %{conn: conn} do
385 limit = Config.get([:instance, :poll_limits, :min_expiration])
389 |> assign(:user, user)
390 |> post("/api/v1/statuses", %{
391 "status" => "imagine arbitrary limits",
393 "options" => ["this post was made by pleroma gang"],
394 "expires_in" => limit - 1
398 %{"error" => error} = json_response(conn, 422)
399 assert error == "Expiration date is too soon"
402 test "maximum date limit is enforced", %{conn: conn} do
404 limit = Config.get([:instance, :poll_limits, :max_expiration])
408 |> assign(:user, user)
409 |> post("/api/v1/statuses", %{
410 "status" => "imagine arbitrary limits",
412 "options" => ["this post was made by pleroma gang"],
413 "expires_in" => limit + 1
417 %{"error" => error} = json_response(conn, 422)
418 assert error == "Expiration date is too far in the future"
422 test "direct timeline", %{conn: conn} do
423 user_one = insert(:user)
424 user_two = insert(:user)
426 {:ok, user_two} = User.follow(user_two, user_one)
429 CommonAPI.post(user_one, %{
430 "status" => "Hi @#{user_two.nickname}!",
431 "visibility" => "direct"
434 {:ok, _follower_only} =
435 CommonAPI.post(user_one, %{
436 "status" => "Hi @#{user_two.nickname}!",
437 "visibility" => "private"
440 # Only direct should be visible here
443 |> assign(:user, user_two)
444 |> get("api/v1/timelines/direct")
446 [status] = json_response(res_conn, 200)
448 assert %{"visibility" => "direct"} = status
449 assert status["url"] != direct.data["id"]
451 # User should be able to see their own direct message
454 |> assign(:user, user_one)
455 |> get("api/v1/timelines/direct")
457 [status] = json_response(res_conn, 200)
459 assert %{"visibility" => "direct"} = status
461 # Both should be visible here
464 |> assign(:user, user_two)
465 |> get("api/v1/timelines/home")
467 [_s1, _s2] = json_response(res_conn, 200)
470 Enum.each(1..20, fn _ ->
472 CommonAPI.post(user_one, %{
473 "status" => "Hi @#{user_two.nickname}!",
474 "visibility" => "direct"
480 |> assign(:user, user_two)
481 |> get("api/v1/timelines/direct")
483 statuses = json_response(res_conn, 200)
484 assert length(statuses) == 20
488 |> assign(:user, user_two)
489 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
491 [status] = json_response(res_conn, 200)
493 assert status["url"] != direct.data["id"]
496 test "Conversations", %{conn: conn} do
497 user_one = insert(:user)
498 user_two = insert(:user)
499 user_three = insert(:user)
501 {:ok, user_two} = User.follow(user_two, user_one)
504 CommonAPI.post(user_one, %{
505 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
506 "visibility" => "direct"
509 {:ok, _follower_only} =
510 CommonAPI.post(user_one, %{
511 "status" => "Hi @#{user_two.nickname}!",
512 "visibility" => "private"
517 |> assign(:user, user_one)
518 |> get("/api/v1/conversations")
520 assert response = json_response(res_conn, 200)
525 "accounts" => res_accounts,
526 "last_status" => res_last_status,
531 account_ids = Enum.map(res_accounts, & &1["id"])
532 assert length(res_accounts) == 2
533 assert user_two.id in account_ids
534 assert user_three.id in account_ids
535 assert is_binary(res_id)
536 assert unread == true
537 assert res_last_status["id"] == direct.id
539 # Apparently undocumented API endpoint
542 |> assign(:user, user_one)
543 |> post("/api/v1/conversations/#{res_id}/read")
545 assert response = json_response(res_conn, 200)
546 assert length(response["accounts"]) == 2
547 assert response["last_status"]["id"] == direct.id
548 assert response["unread"] == false
550 # (vanilla) Mastodon frontend behaviour
553 |> assign(:user, user_one)
554 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
556 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
559 test "doesn't include DMs from blocked users", %{conn: conn} do
560 blocker = insert(:user)
561 blocked = insert(:user)
563 {:ok, blocker} = User.block(blocker, blocked)
565 {:ok, _blocked_direct} =
566 CommonAPI.post(blocked, %{
567 "status" => "Hi @#{blocker.nickname}!",
568 "visibility" => "direct"
572 CommonAPI.post(user, %{
573 "status" => "Hi @#{blocker.nickname}!",
574 "visibility" => "direct"
579 |> assign(:user, user)
580 |> get("api/v1/timelines/direct")
582 [status] = json_response(res_conn, 200)
583 assert status["id"] == direct.id
586 test "verify_credentials", %{conn: conn} do
591 |> assign(:user, user)
592 |> get("/api/v1/accounts/verify_credentials")
594 response = json_response(conn, 200)
596 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
597 assert response["pleroma"]["chat_token"]
598 assert id == to_string(user.id)
601 test "verify_credentials default scope unlisted", %{conn: conn} do
602 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
606 |> assign(:user, user)
607 |> get("/api/v1/accounts/verify_credentials")
609 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
610 assert id == to_string(user.id)
613 test "apps/verify_credentials", %{conn: conn} do
614 token = insert(:oauth_token)
618 |> assign(:user, token.user)
619 |> assign(:token, token)
620 |> get("/api/v1/apps/verify_credentials")
622 app = Repo.preload(token, :app).app
625 "name" => app.client_name,
626 "website" => app.website,
627 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
630 assert expected == json_response(conn, 200)
633 test "user avatar can be set", %{conn: conn} do
635 avatar_image = File.read!("test/fixtures/avatar_data_uri")
639 |> assign(:user, user)
640 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
642 user = refresh_record(user)
656 assert %{"url" => _} = json_response(conn, 200)
659 test "user avatar can be reset", %{conn: conn} do
664 |> assign(:user, user)
665 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
667 user = User.get_cached_by_id(user.id)
669 assert user.avatar == nil
671 assert %{"url" => nil} = json_response(conn, 200)
674 test "can set profile banner", %{conn: conn} do
679 |> assign(:user, user)
680 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
682 user = refresh_record(user)
683 assert user.info.banner["type"] == "Image"
685 assert %{"url" => _} = json_response(conn, 200)
688 test "can reset profile banner", %{conn: conn} do
693 |> assign(:user, user)
694 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
696 user = refresh_record(user)
697 assert user.info.banner == %{}
699 assert %{"url" => nil} = json_response(conn, 200)
702 test "background image can be set", %{conn: conn} do
707 |> assign(:user, user)
708 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
710 user = refresh_record(user)
711 assert user.info.background["type"] == "Image"
712 assert %{"url" => _} = json_response(conn, 200)
715 test "background image can be reset", %{conn: conn} do
720 |> assign(:user, user)
721 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
723 user = refresh_record(user)
724 assert user.info.background == %{}
725 assert %{"url" => nil} = json_response(conn, 200)
728 test "creates an oauth app", %{conn: conn} do
730 app_attrs = build(:oauth_app)
734 |> assign(:user, user)
735 |> post("/api/v1/apps", %{
736 client_name: app_attrs.client_name,
737 redirect_uris: app_attrs.redirect_uris
740 [app] = Repo.all(App)
743 "name" => app.client_name,
744 "website" => app.website,
745 "client_id" => app.client_id,
746 "client_secret" => app.client_secret,
747 "id" => app.id |> to_string(),
748 "redirect_uri" => app.redirect_uris,
749 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
752 assert expected == json_response(conn, 200)
755 test "get a status", %{conn: conn} do
756 activity = insert(:note_activity)
760 |> get("/api/v1/statuses/#{activity.id}")
762 assert %{"id" => id} = json_response(conn, 200)
763 assert id == to_string(activity.id)
766 test "get statuses by IDs", %{conn: conn} do
767 %{id: id1} = insert(:note_activity)
768 %{id: id2} = insert(:note_activity)
770 query_string = "ids[]=#{id1}&ids[]=#{id2}"
771 conn = get(conn, "/api/v1/statuses/?#{query_string}")
773 assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"])
776 describe "deleting a status" do
777 test "when you created it", %{conn: conn} do
778 activity = insert(:note_activity)
779 author = User.get_cached_by_ap_id(activity.data["actor"])
783 |> assign(:user, author)
784 |> delete("/api/v1/statuses/#{activity.id}")
786 assert %{} = json_response(conn, 200)
788 refute Activity.get_by_id(activity.id)
791 test "when you didn't create it", %{conn: conn} do
792 activity = insert(:note_activity)
797 |> assign(:user, user)
798 |> delete("/api/v1/statuses/#{activity.id}")
800 assert %{"error" => _} = json_response(conn, 403)
802 assert Activity.get_by_id(activity.id) == activity
805 test "when you're an admin or moderator", %{conn: conn} do
806 activity1 = insert(:note_activity)
807 activity2 = insert(:note_activity)
808 admin = insert(:user, info: %{is_admin: true})
809 moderator = insert(:user, info: %{is_moderator: true})
813 |> assign(:user, admin)
814 |> delete("/api/v1/statuses/#{activity1.id}")
816 assert %{} = json_response(res_conn, 200)
820 |> assign(:user, moderator)
821 |> delete("/api/v1/statuses/#{activity2.id}")
823 assert %{} = json_response(res_conn, 200)
825 refute Activity.get_by_id(activity1.id)
826 refute Activity.get_by_id(activity2.id)
830 describe "filters" do
831 test "creating a filter", %{conn: conn} do
834 filter = %Pleroma.Filter{
841 |> assign(:user, user)
842 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
844 assert response = json_response(conn, 200)
845 assert response["phrase"] == filter.phrase
846 assert response["context"] == filter.context
847 assert response["irreversible"] == false
848 assert response["id"] != nil
849 assert response["id"] != ""
852 test "fetching a list of filters", %{conn: conn} do
855 query_one = %Pleroma.Filter{
862 query_two = %Pleroma.Filter{
869 {:ok, filter_one} = Pleroma.Filter.create(query_one)
870 {:ok, filter_two} = Pleroma.Filter.create(query_two)
874 |> assign(:user, user)
875 |> get("/api/v1/filters")
876 |> json_response(200)
882 filters: [filter_two, filter_one]
886 test "get a filter", %{conn: conn} do
889 query = %Pleroma.Filter{
896 {:ok, filter} = Pleroma.Filter.create(query)
900 |> assign(:user, user)
901 |> get("/api/v1/filters/#{filter.filter_id}")
903 assert _response = json_response(conn, 200)
906 test "update a filter", %{conn: conn} do
909 query = %Pleroma.Filter{
916 {:ok, _filter} = Pleroma.Filter.create(query)
918 new = %Pleroma.Filter{
925 |> assign(:user, user)
926 |> put("/api/v1/filters/#{query.filter_id}", %{
931 assert response = json_response(conn, 200)
932 assert response["phrase"] == new.phrase
933 assert response["context"] == new.context
936 test "delete a filter", %{conn: conn} do
939 query = %Pleroma.Filter{
946 {:ok, filter} = Pleroma.Filter.create(query)
950 |> assign(:user, user)
951 |> delete("/api/v1/filters/#{filter.filter_id}")
953 assert response = json_response(conn, 200)
954 assert response == %{}
958 describe "list timelines" do
959 test "list timeline", %{conn: conn} do
961 other_user = insert(:user)
962 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
963 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
964 {:ok, list} = Pleroma.List.create("name", user)
965 {:ok, list} = Pleroma.List.follow(list, other_user)
969 |> assign(:user, user)
970 |> get("/api/v1/timelines/list/#{list.id}")
972 assert [%{"id" => id}] = json_response(conn, 200)
974 assert id == to_string(activity_two.id)
977 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
979 other_user = insert(:user)
980 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
982 {:ok, _activity_two} =
983 CommonAPI.post(other_user, %{
984 "status" => "Marisa is cute.",
985 "visibility" => "private"
988 {:ok, list} = Pleroma.List.create("name", user)
989 {:ok, list} = Pleroma.List.follow(list, other_user)
993 |> assign(:user, user)
994 |> get("/api/v1/timelines/list/#{list.id}")
996 assert [%{"id" => id}] = json_response(conn, 200)
998 assert id == to_string(activity_one.id)
1002 describe "notifications" do
1003 test "list of notifications", %{conn: conn} do
1004 user = insert(:user)
1005 other_user = insert(:user)
1007 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1009 {:ok, [_notification]} = Notification.create_notifications(activity)
1013 |> assign(:user, user)
1014 |> get("/api/v1/notifications")
1017 ~s(hi <span class="h-card"><a data-user="#{user.id}" class="u-url mention" href="#{
1019 }" rel="ugc">@<span>#{user.nickname}</span></a></span>)
1021 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1022 assert response == expected_response
1025 test "getting a single notification", %{conn: conn} do
1026 user = insert(:user)
1027 other_user = insert(:user)
1029 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1031 {:ok, [notification]} = Notification.create_notifications(activity)
1035 |> assign(:user, user)
1036 |> get("/api/v1/notifications/#{notification.id}")
1039 ~s(hi <span class="h-card"><a data-user="#{user.id}" class="u-url mention" href="#{
1041 }" rel="ugc">@<span>#{user.nickname}</span></a></span>)
1043 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1044 assert response == expected_response
1047 test "dismissing a single notification", %{conn: conn} do
1048 user = insert(:user)
1049 other_user = insert(:user)
1051 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1053 {:ok, [notification]} = Notification.create_notifications(activity)
1057 |> assign(:user, user)
1058 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1060 assert %{} = json_response(conn, 200)
1063 test "clearing all notifications", %{conn: conn} do
1064 user = insert(:user)
1065 other_user = insert(:user)
1067 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1069 {:ok, [_notification]} = Notification.create_notifications(activity)
1073 |> assign(:user, user)
1074 |> post("/api/v1/notifications/clear")
1076 assert %{} = json_response(conn, 200)
1080 |> assign(:user, user)
1081 |> get("/api/v1/notifications")
1083 assert all = json_response(conn, 200)
1087 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1088 user = insert(:user)
1089 other_user = insert(:user)
1091 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1092 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1093 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1094 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1096 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1097 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1098 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1099 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1103 |> assign(:user, user)
1108 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1110 result = json_response(conn_res, 200)
1111 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1116 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1118 result = json_response(conn_res, 200)
1119 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1124 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1126 result = json_response(conn_res, 200)
1127 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1130 test "filters notifications using exclude_types", %{conn: conn} do
1131 user = insert(:user)
1132 other_user = insert(:user)
1134 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1135 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1136 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1137 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1138 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1140 mention_notification_id =
1141 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1143 favorite_notification_id =
1144 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1146 reblog_notification_id =
1147 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1149 follow_notification_id =
1150 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1154 |> assign(:user, user)
1157 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1159 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1162 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1164 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1167 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1169 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1172 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1174 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1177 test "destroy multiple", %{conn: conn} do
1178 user = insert(:user)
1179 other_user = insert(:user)
1181 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1182 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1183 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1184 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1186 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1187 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1188 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1189 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1193 |> assign(:user, user)
1197 |> get("/api/v1/notifications")
1199 result = json_response(conn_res, 200)
1200 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1204 |> assign(:user, other_user)
1208 |> get("/api/v1/notifications")
1210 result = json_response(conn_res, 200)
1211 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1215 |> delete("/api/v1/notifications/destroy_multiple", %{
1216 "ids" => [notification1_id, notification2_id]
1219 assert json_response(conn_destroy, 200) == %{}
1223 |> get("/api/v1/notifications")
1225 result = json_response(conn_res, 200)
1226 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1229 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1230 user = insert(:user)
1231 user2 = insert(:user)
1233 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1234 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1236 conn = assign(conn, :user, user)
1238 conn = get(conn, "/api/v1/notifications")
1240 assert length(json_response(conn, 200)) == 1
1242 {:ok, user} = User.mute(user, user2)
1244 conn = assign(build_conn(), :user, user)
1245 conn = get(conn, "/api/v1/notifications")
1247 assert json_response(conn, 200) == []
1250 test "see notifications after muting user without notifications", %{conn: conn} do
1251 user = insert(:user)
1252 user2 = insert(:user)
1254 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1255 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1257 conn = assign(conn, :user, user)
1259 conn = get(conn, "/api/v1/notifications")
1261 assert length(json_response(conn, 200)) == 1
1263 {:ok, user} = User.mute(user, user2, false)
1265 conn = assign(build_conn(), :user, user)
1266 conn = get(conn, "/api/v1/notifications")
1268 assert length(json_response(conn, 200)) == 1
1271 test "see notifications after muting user with notifications and with_muted parameter", %{
1274 user = insert(:user)
1275 user2 = insert(:user)
1277 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1278 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1280 conn = assign(conn, :user, user)
1282 conn = get(conn, "/api/v1/notifications")
1284 assert length(json_response(conn, 200)) == 1
1286 {:ok, user} = User.mute(user, user2)
1288 conn = assign(build_conn(), :user, user)
1289 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1291 assert length(json_response(conn, 200)) == 1
1295 describe "reblogging" do
1296 test "reblogs and returns the reblogged status", %{conn: conn} do
1297 activity = insert(:note_activity)
1298 user = insert(:user)
1302 |> assign(:user, user)
1303 |> post("/api/v1/statuses/#{activity.id}/reblog")
1306 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1308 } = json_response(conn, 200)
1310 assert to_string(activity.id) == id
1313 test "reblogged status for another user", %{conn: conn} do
1314 activity = insert(:note_activity)
1315 user1 = insert(:user)
1316 user2 = insert(:user)
1317 user3 = insert(:user)
1318 CommonAPI.favorite(activity.id, user2)
1319 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1320 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1321 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1325 |> assign(:user, user3)
1326 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1329 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1330 "reblogged" => false,
1331 "favourited" => false,
1332 "bookmarked" => false
1333 } = json_response(conn_res, 200)
1337 |> assign(:user, user2)
1338 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1341 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1342 "reblogged" => true,
1343 "favourited" => true,
1344 "bookmarked" => true
1345 } = json_response(conn_res, 200)
1347 assert to_string(activity.id) == id
1350 test "returns 400 error when activity is not exist", %{conn: conn} do
1351 user = insert(:user)
1355 |> assign(:user, user)
1356 |> post("/api/v1/statuses/foo/reblog")
1358 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1362 describe "unreblogging" do
1363 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1364 activity = insert(:note_activity)
1365 user = insert(:user)
1367 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1371 |> assign(:user, user)
1372 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1374 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1376 assert to_string(activity.id) == id
1379 test "returns 400 error when activity is not exist", %{conn: conn} do
1380 user = insert(:user)
1384 |> assign(:user, user)
1385 |> post("/api/v1/statuses/foo/unreblog")
1387 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1391 describe "favoriting" do
1392 test "favs a status and returns it", %{conn: conn} do
1393 activity = insert(:note_activity)
1394 user = insert(:user)
1398 |> assign(:user, user)
1399 |> post("/api/v1/statuses/#{activity.id}/favourite")
1401 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1402 json_response(conn, 200)
1404 assert to_string(activity.id) == id
1407 test "returns 400 error for a wrong id", %{conn: conn} do
1408 user = insert(:user)
1412 |> assign(:user, user)
1413 |> post("/api/v1/statuses/1/favourite")
1415 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1419 describe "unfavoriting" do
1420 test "unfavorites a status and returns it", %{conn: conn} do
1421 activity = insert(:note_activity)
1422 user = insert(:user)
1424 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1428 |> assign(:user, user)
1429 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1431 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1432 json_response(conn, 200)
1434 assert to_string(activity.id) == id
1437 test "returns 400 error for a wrong id", %{conn: conn} do
1438 user = insert(:user)
1442 |> assign(:user, user)
1443 |> post("/api/v1/statuses/1/unfavourite")
1445 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1449 describe "user timelines" do
1450 test "gets a users statuses", %{conn: conn} do
1451 user_one = insert(:user)
1452 user_two = insert(:user)
1453 user_three = insert(:user)
1455 {:ok, user_three} = User.follow(user_three, user_one)
1457 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1459 {:ok, direct_activity} =
1460 CommonAPI.post(user_one, %{
1461 "status" => "Hi, @#{user_two.nickname}.",
1462 "visibility" => "direct"
1465 {:ok, private_activity} =
1466 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1470 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1472 assert [%{"id" => id}] = json_response(resp, 200)
1473 assert id == to_string(activity.id)
1477 |> assign(:user, user_two)
1478 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1480 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1481 assert id_one == to_string(direct_activity.id)
1482 assert id_two == to_string(activity.id)
1486 |> assign(:user, user_three)
1487 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1489 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1490 assert id_one == to_string(private_activity.id)
1491 assert id_two == to_string(activity.id)
1494 test "unimplemented pinned statuses feature", %{conn: conn} do
1495 note = insert(:note_activity)
1496 user = User.get_cached_by_ap_id(note.data["actor"])
1500 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1502 assert json_response(conn, 200) == []
1505 test "gets an users media", %{conn: conn} do
1506 note = insert(:note_activity)
1507 user = User.get_cached_by_ap_id(note.data["actor"])
1509 file = %Plug.Upload{
1510 content_type: "image/jpg",
1511 path: Path.absname("test/fixtures/image.jpg"),
1512 filename: "an_image.jpg"
1515 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1517 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1521 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1523 assert [%{"id" => id}] = json_response(conn, 200)
1524 assert id == to_string(image_post.id)
1528 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1530 assert [%{"id" => id}] = json_response(conn, 200)
1531 assert id == to_string(image_post.id)
1534 test "gets a user's statuses without reblogs", %{conn: conn} do
1535 user = insert(:user)
1536 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1537 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1541 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1543 assert [%{"id" => id}] = json_response(conn, 200)
1544 assert id == to_string(post.id)
1548 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1550 assert [%{"id" => id}] = json_response(conn, 200)
1551 assert id == to_string(post.id)
1554 test "filters user's statuses by a hashtag", %{conn: conn} do
1555 user = insert(:user)
1556 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1557 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1561 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1563 assert [%{"id" => id}] = json_response(conn, 200)
1564 assert id == to_string(post.id)
1568 describe "user relationships" do
1569 test "returns the relationships for the current user", %{conn: conn} do
1570 user = insert(:user)
1571 other_user = insert(:user)
1572 {:ok, user} = User.follow(user, other_user)
1576 |> assign(:user, user)
1577 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1579 assert [relationship] = json_response(conn, 200)
1581 assert to_string(other_user.id) == relationship["id"]
1585 describe "media upload" do
1587 user = insert(:user)
1591 |> assign(:user, user)
1593 image = %Plug.Upload{
1594 content_type: "image/jpg",
1595 path: Path.absname("test/fixtures/image.jpg"),
1596 filename: "an_image.jpg"
1599 [conn: conn, image: image]
1602 clear_config([:media_proxy])
1603 clear_config([Pleroma.Upload])
1605 test "returns uploaded image", %{conn: conn, image: image} do
1606 desc = "Description of the image"
1610 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1611 |> json_response(:ok)
1613 assert media["type"] == "image"
1614 assert media["description"] == desc
1617 object = Repo.get(Object, media["id"])
1618 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1622 describe "locked accounts" do
1623 test "/api/v1/follow_requests works" do
1624 user = insert(:user, %{info: %User.Info{locked: true}})
1625 other_user = insert(:user)
1627 {:ok, _activity} = ActivityPub.follow(other_user, user)
1629 user = User.get_cached_by_id(user.id)
1630 other_user = User.get_cached_by_id(other_user.id)
1632 assert User.following?(other_user, user) == false
1636 |> assign(:user, user)
1637 |> get("/api/v1/follow_requests")
1639 assert [relationship] = json_response(conn, 200)
1640 assert to_string(other_user.id) == relationship["id"]
1643 test "/api/v1/follow_requests/:id/authorize works" do
1644 user = insert(:user, %{info: %User.Info{locked: true}})
1645 other_user = insert(:user)
1647 {:ok, _activity} = ActivityPub.follow(other_user, user)
1649 user = User.get_cached_by_id(user.id)
1650 other_user = User.get_cached_by_id(other_user.id)
1652 assert User.following?(other_user, user) == false
1656 |> assign(:user, user)
1657 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1659 assert relationship = json_response(conn, 200)
1660 assert to_string(other_user.id) == relationship["id"]
1662 user = User.get_cached_by_id(user.id)
1663 other_user = User.get_cached_by_id(other_user.id)
1665 assert User.following?(other_user, user) == true
1668 test "verify_credentials", %{conn: conn} do
1669 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1673 |> assign(:user, user)
1674 |> get("/api/v1/accounts/verify_credentials")
1676 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1677 assert id == to_string(user.id)
1680 test "/api/v1/follow_requests/:id/reject works" do
1681 user = insert(:user, %{info: %User.Info{locked: true}})
1682 other_user = insert(:user)
1684 {:ok, _activity} = ActivityPub.follow(other_user, user)
1686 user = User.get_cached_by_id(user.id)
1690 |> assign(:user, user)
1691 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1693 assert relationship = json_response(conn, 200)
1694 assert to_string(other_user.id) == relationship["id"]
1696 user = User.get_cached_by_id(user.id)
1697 other_user = User.get_cached_by_id(other_user.id)
1699 assert User.following?(other_user, user) == false
1703 describe "account fetching" do
1704 test "works by id" do
1705 user = insert(:user)
1709 |> get("/api/v1/accounts/#{user.id}")
1711 assert %{"id" => id} = json_response(conn, 200)
1712 assert id == to_string(user.id)
1716 |> get("/api/v1/accounts/-1")
1718 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1721 test "works by nickname" do
1722 user = insert(:user)
1726 |> get("/api/v1/accounts/#{user.nickname}")
1728 assert %{"id" => id} = json_response(conn, 200)
1729 assert id == user.id
1732 test "works by nickname for remote users" do
1733 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1734 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1735 user = insert(:user, nickname: "user@example.com", local: false)
1739 |> get("/api/v1/accounts/#{user.nickname}")
1741 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1742 assert %{"id" => id} = json_response(conn, 200)
1743 assert id == user.id
1746 test "respects limit_to_local_content == :all for remote user nicknames" do
1747 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1748 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1750 user = insert(:user, nickname: "user@example.com", local: false)
1754 |> get("/api/v1/accounts/#{user.nickname}")
1756 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1757 assert json_response(conn, 404)
1760 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1761 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1762 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1764 user = insert(:user, nickname: "user@example.com", local: false)
1765 reading_user = insert(:user)
1769 |> get("/api/v1/accounts/#{user.nickname}")
1771 assert json_response(conn, 404)
1775 |> assign(:user, reading_user)
1776 |> get("/api/v1/accounts/#{user.nickname}")
1778 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1779 assert %{"id" => id} = json_response(conn, 200)
1780 assert id == user.id
1784 test "mascot upload", %{conn: conn} do
1785 user = insert(:user)
1787 non_image_file = %Plug.Upload{
1788 content_type: "audio/mpeg",
1789 path: Path.absname("test/fixtures/sound.mp3"),
1790 filename: "sound.mp3"
1795 |> assign(:user, user)
1796 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1798 assert json_response(conn, 415)
1800 file = %Plug.Upload{
1801 content_type: "image/jpg",
1802 path: Path.absname("test/fixtures/image.jpg"),
1803 filename: "an_image.jpg"
1808 |> assign(:user, user)
1809 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1811 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1814 test "mascot retrieving", %{conn: conn} do
1815 user = insert(:user)
1816 # When user hasn't set a mascot, we should just get pleroma tan back
1819 |> assign(:user, user)
1820 |> get("/api/v1/pleroma/mascot")
1822 assert %{"url" => url} = json_response(conn, 200)
1823 assert url =~ "pleroma-fox-tan-smol"
1825 # When a user sets their mascot, we should get that back
1826 file = %Plug.Upload{
1827 content_type: "image/jpg",
1828 path: Path.absname("test/fixtures/image.jpg"),
1829 filename: "an_image.jpg"
1834 |> assign(:user, user)
1835 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1837 assert json_response(conn, 200)
1839 user = User.get_cached_by_id(user.id)
1843 |> assign(:user, user)
1844 |> get("/api/v1/pleroma/mascot")
1846 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1847 assert url =~ "an_image"
1850 test "hashtag timeline", %{conn: conn} do
1851 following = insert(:user)
1854 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1856 {:ok, [_activity]} =
1857 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1861 |> get("/api/v1/timelines/tag/2hu")
1863 assert [%{"id" => id}] = json_response(nconn, 200)
1865 assert id == to_string(activity.id)
1867 # works for different capitalization too
1870 |> get("/api/v1/timelines/tag/2HU")
1872 assert [%{"id" => id}] = json_response(nconn, 200)
1874 assert id == to_string(activity.id)
1878 test "multi-hashtag timeline", %{conn: conn} do
1879 user = insert(:user)
1881 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1882 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1883 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1887 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1889 [status_none, status_test1, status_test] = json_response(any_test, 200)
1891 assert to_string(activity_test.id) == status_test["id"]
1892 assert to_string(activity_test1.id) == status_test1["id"]
1893 assert to_string(activity_none.id) == status_none["id"]
1897 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1899 assert [status_test1] == json_response(restricted_test, 200)
1901 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1903 assert [status_none] == json_response(all_test, 200)
1906 test "getting followers", %{conn: conn} do
1907 user = insert(:user)
1908 other_user = insert(:user)
1909 {:ok, user} = User.follow(user, other_user)
1913 |> get("/api/v1/accounts/#{other_user.id}/followers")
1915 assert [%{"id" => id}] = json_response(conn, 200)
1916 assert id == to_string(user.id)
1919 test "getting followers, hide_followers", %{conn: conn} do
1920 user = insert(:user)
1921 other_user = insert(:user, %{info: %{hide_followers: true}})
1922 {:ok, _user} = User.follow(user, other_user)
1926 |> get("/api/v1/accounts/#{other_user.id}/followers")
1928 assert [] == json_response(conn, 200)
1931 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1932 user = insert(:user)
1933 other_user = insert(:user, %{info: %{hide_followers: true}})
1934 {:ok, _user} = User.follow(user, other_user)
1938 |> assign(:user, other_user)
1939 |> get("/api/v1/accounts/#{other_user.id}/followers")
1941 refute [] == json_response(conn, 200)
1944 test "getting followers, pagination", %{conn: conn} do
1945 user = insert(:user)
1946 follower1 = insert(:user)
1947 follower2 = insert(:user)
1948 follower3 = insert(:user)
1949 {:ok, _} = User.follow(follower1, user)
1950 {:ok, _} = User.follow(follower2, user)
1951 {:ok, _} = User.follow(follower3, user)
1955 |> assign(:user, user)
1959 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1961 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1962 assert id3 == follower3.id
1963 assert id2 == follower2.id
1967 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1969 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1970 assert id2 == follower2.id
1971 assert id1 == follower1.id
1975 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1977 assert [%{"id" => id2}] = json_response(res_conn, 200)
1978 assert id2 == follower2.id
1980 assert [link_header] = get_resp_header(res_conn, "link")
1981 assert link_header =~ ~r/min_id=#{follower2.id}/
1982 assert link_header =~ ~r/max_id=#{follower2.id}/
1985 test "getting following", %{conn: conn} do
1986 user = insert(:user)
1987 other_user = insert(:user)
1988 {:ok, user} = User.follow(user, other_user)
1992 |> get("/api/v1/accounts/#{user.id}/following")
1994 assert [%{"id" => id}] = json_response(conn, 200)
1995 assert id == to_string(other_user.id)
1998 test "getting following, hide_follows", %{conn: conn} do
1999 user = insert(:user, %{info: %{hide_follows: true}})
2000 other_user = insert(:user)
2001 {:ok, user} = User.follow(user, other_user)
2005 |> get("/api/v1/accounts/#{user.id}/following")
2007 assert [] == json_response(conn, 200)
2010 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2011 user = insert(:user, %{info: %{hide_follows: true}})
2012 other_user = insert(:user)
2013 {:ok, user} = User.follow(user, other_user)
2017 |> assign(:user, user)
2018 |> get("/api/v1/accounts/#{user.id}/following")
2020 refute [] == json_response(conn, 200)
2023 test "getting following, pagination", %{conn: conn} do
2024 user = insert(:user)
2025 following1 = insert(:user)
2026 following2 = insert(:user)
2027 following3 = insert(:user)
2028 {:ok, _} = User.follow(user, following1)
2029 {:ok, _} = User.follow(user, following2)
2030 {:ok, _} = User.follow(user, following3)
2034 |> assign(:user, user)
2038 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2040 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2041 assert id3 == following3.id
2042 assert id2 == following2.id
2046 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2048 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2049 assert id2 == following2.id
2050 assert id1 == following1.id
2054 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2056 assert [%{"id" => id2}] = json_response(res_conn, 200)
2057 assert id2 == following2.id
2059 assert [link_header] = get_resp_header(res_conn, "link")
2060 assert link_header =~ ~r/min_id=#{following2.id}/
2061 assert link_header =~ ~r/max_id=#{following2.id}/
2064 test "following / unfollowing a user", %{conn: conn} do
2065 user = insert(:user)
2066 other_user = insert(:user)
2070 |> assign(:user, user)
2071 |> post("/api/v1/accounts/#{other_user.id}/follow")
2073 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2075 user = User.get_cached_by_id(user.id)
2079 |> assign(:user, user)
2080 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2082 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2084 user = User.get_cached_by_id(user.id)
2088 |> assign(:user, user)
2089 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2091 assert %{"id" => id} = json_response(conn, 200)
2092 assert id == to_string(other_user.id)
2095 test "following without reblogs" do
2096 follower = insert(:user)
2097 followed = insert(:user)
2098 other_user = insert(:user)
2102 |> assign(:user, follower)
2103 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2105 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2107 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2108 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2112 |> assign(:user, User.get_cached_by_id(follower.id))
2113 |> get("/api/v1/timelines/home")
2115 assert [] == json_response(conn, 200)
2119 |> assign(:user, follower)
2120 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2122 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2126 |> assign(:user, User.get_cached_by_id(follower.id))
2127 |> get("/api/v1/timelines/home")
2129 expected_activity_id = reblog.id
2130 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2133 test "following / unfollowing errors" do
2134 user = insert(:user)
2138 |> assign(:user, user)
2141 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2142 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2145 user = User.get_cached_by_id(user.id)
2146 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2147 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2149 # self follow via uri
2150 user = User.get_cached_by_id(user.id)
2151 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2152 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2154 # follow non existing user
2155 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2156 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2158 # follow non existing user via uri
2159 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2160 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2162 # unfollow non existing user
2163 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2164 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2167 describe "mute/unmute" do
2168 test "with notifications", %{conn: conn} do
2169 user = insert(:user)
2170 other_user = insert(:user)
2174 |> assign(:user, user)
2175 |> post("/api/v1/accounts/#{other_user.id}/mute")
2177 response = json_response(conn, 200)
2179 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2180 user = User.get_cached_by_id(user.id)
2184 |> assign(:user, user)
2185 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2187 response = json_response(conn, 200)
2188 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2191 test "without notifications", %{conn: conn} do
2192 user = insert(:user)
2193 other_user = insert(:user)
2197 |> assign(:user, user)
2198 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2200 response = json_response(conn, 200)
2202 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2203 user = User.get_cached_by_id(user.id)
2207 |> assign(:user, user)
2208 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2210 response = json_response(conn, 200)
2211 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2215 test "subscribing / unsubscribing to a user", %{conn: conn} do
2216 user = insert(:user)
2217 subscription_target = insert(:user)
2221 |> assign(:user, user)
2222 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2224 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2228 |> assign(:user, user)
2229 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2231 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2234 test "getting a list of mutes", %{conn: conn} do
2235 user = insert(:user)
2236 other_user = insert(:user)
2238 {:ok, user} = User.mute(user, other_user)
2242 |> assign(:user, user)
2243 |> get("/api/v1/mutes")
2245 other_user_id = to_string(other_user.id)
2246 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2249 test "blocking / unblocking a user", %{conn: conn} do
2250 user = insert(:user)
2251 other_user = insert(:user)
2255 |> assign(:user, user)
2256 |> post("/api/v1/accounts/#{other_user.id}/block")
2258 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2260 user = User.get_cached_by_id(user.id)
2264 |> assign(:user, user)
2265 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2267 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2270 test "getting a list of blocks", %{conn: conn} do
2271 user = insert(:user)
2272 other_user = insert(:user)
2274 {:ok, user} = User.block(user, other_user)
2278 |> assign(:user, user)
2279 |> get("/api/v1/blocks")
2281 other_user_id = to_string(other_user.id)
2282 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2285 test "blocking / unblocking a domain", %{conn: conn} do
2286 user = insert(:user)
2287 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2291 |> assign(:user, user)
2292 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2294 assert %{} = json_response(conn, 200)
2295 user = User.get_cached_by_ap_id(user.ap_id)
2296 assert User.blocks?(user, other_user)
2300 |> assign(:user, user)
2301 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2303 assert %{} = json_response(conn, 200)
2304 user = User.get_cached_by_ap_id(user.ap_id)
2305 refute User.blocks?(user, other_user)
2308 test "getting a list of domain blocks", %{conn: conn} do
2309 user = insert(:user)
2311 {:ok, user} = User.block_domain(user, "bad.site")
2312 {:ok, user} = User.block_domain(user, "even.worse.site")
2316 |> assign(:user, user)
2317 |> get("/api/v1/domain_blocks")
2319 domain_blocks = json_response(conn, 200)
2321 assert "bad.site" in domain_blocks
2322 assert "even.worse.site" in domain_blocks
2325 test "unimplemented follow_requests, blocks, domain blocks" do
2326 user = insert(:user)
2328 ["blocks", "domain_blocks", "follow_requests"]
2329 |> Enum.each(fn endpoint ->
2332 |> assign(:user, user)
2333 |> get("/api/v1/#{endpoint}")
2335 assert [] = json_response(conn, 200)
2339 test "returns the favorites of a user", %{conn: conn} do
2340 user = insert(:user)
2341 other_user = insert(:user)
2343 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2344 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2346 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2350 |> assign(:user, user)
2351 |> get("/api/v1/favourites")
2353 assert [status] = json_response(first_conn, 200)
2354 assert status["id"] == to_string(activity.id)
2356 assert [{"link", _link_header}] =
2357 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2359 # Honours query params
2360 {:ok, second_activity} =
2361 CommonAPI.post(other_user, %{
2363 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2366 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2368 last_like = status["id"]
2372 |> assign(:user, user)
2373 |> get("/api/v1/favourites?since_id=#{last_like}")
2375 assert [second_status] = json_response(second_conn, 200)
2376 assert second_status["id"] == to_string(second_activity.id)
2380 |> assign(:user, user)
2381 |> get("/api/v1/favourites?limit=0")
2383 assert [] = json_response(third_conn, 200)
2386 describe "getting favorites timeline of specified user" do
2388 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2389 [current_user: current_user, user: user]
2392 test "returns list of statuses favorited by specified user", %{
2394 current_user: current_user,
2397 [activity | _] = insert_pair(:note_activity)
2398 CommonAPI.favorite(activity.id, user)
2402 |> assign(:user, current_user)
2403 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2404 |> json_response(:ok)
2408 assert length(response) == 1
2409 assert like["id"] == activity.id
2412 test "returns favorites for specified user_id when user is not logged in", %{
2416 activity = insert(:note_activity)
2417 CommonAPI.favorite(activity.id, user)
2421 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2422 |> json_response(:ok)
2424 assert length(response) == 1
2427 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2429 current_user: current_user,
2433 CommonAPI.post(current_user, %{
2434 "status" => "Hi @#{user.nickname}!",
2435 "visibility" => "direct"
2438 CommonAPI.favorite(direct.id, user)
2442 |> assign(:user, current_user)
2443 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2444 |> json_response(:ok)
2446 assert length(response) == 1
2448 anonymous_response =
2450 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2451 |> json_response(:ok)
2453 assert Enum.empty?(anonymous_response)
2456 test "does not return others' favorited DM when user is not one of recipients", %{
2458 current_user: current_user,
2461 user_two = insert(:user)
2464 CommonAPI.post(user_two, %{
2465 "status" => "Hi @#{user.nickname}!",
2466 "visibility" => "direct"
2469 CommonAPI.favorite(direct.id, user)
2473 |> assign(:user, current_user)
2474 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2475 |> json_response(:ok)
2477 assert Enum.empty?(response)
2480 test "paginates favorites using since_id and max_id", %{
2482 current_user: current_user,
2485 activities = insert_list(10, :note_activity)
2487 Enum.each(activities, fn activity ->
2488 CommonAPI.favorite(activity.id, user)
2491 third_activity = Enum.at(activities, 2)
2492 seventh_activity = Enum.at(activities, 6)
2496 |> assign(:user, current_user)
2497 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2498 since_id: third_activity.id,
2499 max_id: seventh_activity.id
2501 |> json_response(:ok)
2503 assert length(response) == 3
2504 refute third_activity in response
2505 refute seventh_activity in response
2508 test "limits favorites using limit parameter", %{
2510 current_user: current_user,
2514 |> insert_list(:note_activity)
2515 |> Enum.each(fn activity ->
2516 CommonAPI.favorite(activity.id, user)
2521 |> assign(:user, current_user)
2522 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2523 |> json_response(:ok)
2525 assert length(response) == 3
2528 test "returns empty response when user does not have any favorited statuses", %{
2530 current_user: current_user,
2535 |> assign(:user, current_user)
2536 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2537 |> json_response(:ok)
2539 assert Enum.empty?(response)
2542 test "returns 404 error when specified user is not exist", %{conn: conn} do
2543 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2545 assert json_response(conn, 404) == %{"error" => "Record not found"}
2548 test "returns 403 error when user has hidden own favorites", %{
2550 current_user: current_user
2552 user = insert(:user, %{info: %{hide_favorites: true}})
2553 activity = insert(:note_activity)
2554 CommonAPI.favorite(activity.id, user)
2558 |> assign(:user, current_user)
2559 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2561 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2564 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2565 user = insert(:user)
2566 activity = insert(:note_activity)
2567 CommonAPI.favorite(activity.id, user)
2571 |> assign(:user, current_user)
2572 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2574 assert user.info.hide_favorites
2575 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2579 test "get instance information", %{conn: conn} do
2580 conn = get(conn, "/api/v1/instance")
2581 assert result = json_response(conn, 200)
2583 email = Config.get([:instance, :email])
2584 # Note: not checking for "max_toot_chars" since it's optional
2590 "email" => from_config_email,
2592 "streaming_api" => _
2597 "registrations" => _,
2601 assert email == from_config_email
2604 test "get instance stats", %{conn: conn} do
2605 user = insert(:user, %{local: true})
2607 user2 = insert(:user, %{local: true})
2608 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2610 insert(:user, %{local: false, nickname: "u@peer1.com"})
2611 insert(:user, %{local: false, nickname: "u@peer2.com"})
2613 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2615 # Stats should count users with missing or nil `info.deactivated` value
2619 |> User.get_cached_by_id()
2620 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
2622 Pleroma.Stats.force_update()
2624 conn = get(conn, "/api/v1/instance")
2626 assert result = json_response(conn, 200)
2628 stats = result["stats"]
2631 assert stats["user_count"] == 1
2632 assert stats["status_count"] == 1
2633 assert stats["domain_count"] == 2
2636 test "get peers", %{conn: conn} do
2637 insert(:user, %{local: false, nickname: "u@peer1.com"})
2638 insert(:user, %{local: false, nickname: "u@peer2.com"})
2640 Pleroma.Stats.force_update()
2642 conn = get(conn, "/api/v1/instance/peers")
2644 assert result = json_response(conn, 200)
2646 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2649 test "put settings", %{conn: conn} do
2650 user = insert(:user)
2654 |> assign(:user, user)
2655 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2657 assert _result = json_response(conn, 200)
2659 user = User.get_cached_by_ap_id(user.ap_id)
2660 assert user.info.settings == %{"programming" => "socks"}
2663 describe "pinned statuses" do
2665 user = insert(:user)
2666 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2668 [user: user, activity: activity]
2671 clear_config([:instance, :max_pinned_statuses]) do
2672 Config.put([:instance, :max_pinned_statuses], 1)
2675 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2676 {:ok, _} = CommonAPI.pin(activity.id, user)
2680 |> assign(:user, user)
2681 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2682 |> json_response(200)
2684 id_str = to_string(activity.id)
2686 assert [%{"id" => ^id_str, "pinned" => true}] = result
2689 test "pin status", %{conn: conn, user: user, activity: activity} do
2690 id_str = to_string(activity.id)
2692 assert %{"id" => ^id_str, "pinned" => true} =
2694 |> assign(:user, user)
2695 |> post("/api/v1/statuses/#{activity.id}/pin")
2696 |> json_response(200)
2698 assert [%{"id" => ^id_str, "pinned" => true}] =
2700 |> assign(:user, user)
2701 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2702 |> json_response(200)
2705 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2706 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2710 |> assign(:user, user)
2711 |> post("/api/v1/statuses/#{dm.id}/pin")
2713 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2716 test "unpin status", %{conn: conn, user: user, activity: activity} do
2717 {:ok, _} = CommonAPI.pin(activity.id, user)
2719 id_str = to_string(activity.id)
2720 user = refresh_record(user)
2722 assert %{"id" => ^id_str, "pinned" => false} =
2724 |> assign(:user, user)
2725 |> post("/api/v1/statuses/#{activity.id}/unpin")
2726 |> json_response(200)
2730 |> assign(:user, user)
2731 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2732 |> json_response(200)
2735 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2738 |> assign(:user, user)
2739 |> post("/api/v1/statuses/1/unpin")
2741 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2744 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2745 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2747 id_str_one = to_string(activity_one.id)
2749 assert %{"id" => ^id_str_one, "pinned" => true} =
2751 |> assign(:user, user)
2752 |> post("/api/v1/statuses/#{id_str_one}/pin")
2753 |> json_response(200)
2755 user = refresh_record(user)
2757 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2759 |> assign(:user, user)
2760 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2761 |> json_response(400)
2767 Config.put([:rich_media, :enabled], true)
2769 user = insert(:user)
2773 test "returns rich-media card", %{conn: conn, user: user} do
2774 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2777 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2778 "provider_name" => "example.com",
2779 "provider_url" => "https://example.com",
2780 "title" => "The Rock",
2782 "url" => "https://example.com/ogp",
2784 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2787 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2788 "title" => "The Rock",
2789 "type" => "video.movie",
2790 "url" => "https://example.com/ogp",
2792 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2799 |> get("/api/v1/statuses/#{activity.id}/card")
2800 |> json_response(200)
2802 assert response == card_data
2804 # works with private posts
2806 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2810 |> assign(:user, user)
2811 |> get("/api/v1/statuses/#{activity.id}/card")
2812 |> json_response(200)
2814 assert response_two == card_data
2817 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2819 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2823 |> get("/api/v1/statuses/#{activity.id}/card")
2824 |> json_response(:ok)
2826 assert response == %{
2828 "title" => "Pleroma",
2829 "description" => "",
2831 "provider_name" => "example.com",
2832 "provider_url" => "https://example.com",
2833 "url" => "https://example.com/ogp-missing-data",
2836 "title" => "Pleroma",
2837 "type" => "website",
2838 "url" => "https://example.com/ogp-missing-data"
2846 user = insert(:user)
2847 for_user = insert(:user)
2850 CommonAPI.post(user, %{
2851 "status" => "heweoo?"
2855 CommonAPI.post(user, %{
2856 "status" => "heweoo!"
2861 |> assign(:user, for_user)
2862 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2864 assert json_response(response1, 200)["bookmarked"] == true
2868 |> assign(:user, for_user)
2869 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2871 assert json_response(response2, 200)["bookmarked"] == true
2875 |> assign(:user, for_user)
2876 |> get("/api/v1/bookmarks")
2878 assert [json_response(response2, 200), json_response(response1, 200)] ==
2879 json_response(bookmarks, 200)
2883 |> assign(:user, for_user)
2884 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2886 assert json_response(response1, 200)["bookmarked"] == false
2890 |> assign(:user, for_user)
2891 |> get("/api/v1/bookmarks")
2893 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2896 describe "conversation muting" do
2898 post_user = insert(:user)
2899 user = insert(:user)
2901 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2903 [user: user, activity: activity]
2906 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2907 id_str = to_string(activity.id)
2909 assert %{"id" => ^id_str, "muted" => true} =
2911 |> assign(:user, user)
2912 |> post("/api/v1/statuses/#{activity.id}/mute")
2913 |> json_response(200)
2916 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2917 {:ok, _} = CommonAPI.add_mute(user, activity)
2921 |> assign(:user, user)
2922 |> post("/api/v1/statuses/#{activity.id}/mute")
2924 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2927 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2928 {:ok, _} = CommonAPI.add_mute(user, activity)
2930 id_str = to_string(activity.id)
2931 user = refresh_record(user)
2933 assert %{"id" => ^id_str, "muted" => false} =
2935 |> assign(:user, user)
2936 |> post("/api/v1/statuses/#{activity.id}/unmute")
2937 |> json_response(200)
2941 describe "reports" do
2943 reporter = insert(:user)
2944 target_user = insert(:user)
2946 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2948 [reporter: reporter, target_user: target_user, activity: activity]
2951 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2952 assert %{"action_taken" => false, "id" => _} =
2954 |> assign(:user, reporter)
2955 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2956 |> json_response(200)
2959 test "submit a report with statuses and comment", %{
2962 target_user: target_user,
2965 assert %{"action_taken" => false, "id" => _} =
2967 |> assign(:user, reporter)
2968 |> post("/api/v1/reports", %{
2969 "account_id" => target_user.id,
2970 "status_ids" => [activity.id],
2971 "comment" => "bad status!",
2972 "forward" => "false"
2974 |> json_response(200)
2977 test "account_id is required", %{
2982 assert %{"error" => "Valid `account_id` required"} =
2984 |> assign(:user, reporter)
2985 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2986 |> json_response(400)
2989 test "comment must be up to the size specified in the config", %{
2992 target_user: target_user
2994 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2995 comment = String.pad_trailing("a", max_size + 1, "a")
2997 error = %{"error" => "Comment must be up to #{max_size} characters"}
3001 |> assign(:user, reporter)
3002 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3003 |> json_response(400)
3006 test "returns error when account is not exist", %{
3013 |> assign(:user, reporter)
3014 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3016 assert json_response(conn, 400) == %{"error" => "Account not found"}
3020 describe "link headers" do
3021 test "preserves parameters in link headers", %{conn: conn} do
3022 user = insert(:user)
3023 other_user = insert(:user)
3026 CommonAPI.post(other_user, %{
3027 "status" => "hi @#{user.nickname}",
3028 "visibility" => "public"
3032 CommonAPI.post(other_user, %{
3033 "status" => "hi @#{user.nickname}",
3034 "visibility" => "public"
3037 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3038 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3042 |> assign(:user, user)
3043 |> get("/api/v1/notifications", %{media_only: true})
3045 assert [link_header] = get_resp_header(conn, "link")
3046 assert link_header =~ ~r/media_only=true/
3047 assert link_header =~ ~r/min_id=#{notification2.id}/
3048 assert link_header =~ ~r/max_id=#{notification1.id}/
3052 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3053 # Need to set an old-style integer ID to reproduce the problem
3054 # (these are no longer assigned to new accounts but were preserved
3055 # for existing accounts during the migration to flakeIDs)
3056 user_one = insert(:user, %{id: 1212})
3057 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3061 |> get("/api/v1/accounts/#{user_one.id}")
3065 |> get("/api/v1/accounts/#{user_two.nickname}")
3069 |> get("/api/v1/accounts/#{user_two.id}")
3071 acc_one = json_response(resp_one, 200)
3072 acc_two = json_response(resp_two, 200)
3073 acc_three = json_response(resp_three, 200)
3074 refute acc_one == acc_two
3075 assert acc_two == acc_three
3078 describe "custom emoji" do
3079 test "with tags", %{conn: conn} do
3082 |> get("/api/v1/custom_emojis")
3083 |> json_response(200)
3085 assert Map.has_key?(emoji, "shortcode")
3086 assert Map.has_key?(emoji, "static_url")
3087 assert Map.has_key?(emoji, "tags")
3088 assert is_list(emoji["tags"])
3089 assert Map.has_key?(emoji, "category")
3090 assert Map.has_key?(emoji, "url")
3091 assert Map.has_key?(emoji, "visible_in_picker")
3095 describe "index/2 redirections" do
3096 setup %{conn: conn} do
3100 signing_salt: "cooldude"
3105 |> Plug.Session.call(Plug.Session.init(session_opts))
3108 test_path = "/web/statuses/test"
3109 %{conn: conn, path: test_path}
3112 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3113 conn = get(conn, path)
3115 assert conn.status == 302
3116 assert redirected_to(conn) == "/web/login"
3119 test "redirects not logged-in users to the login page on private instances", %{
3123 Config.put([:instance, :public], false)
3125 conn = get(conn, path)
3127 assert conn.status == 302
3128 assert redirected_to(conn) == "/web/login"
3131 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3132 token = insert(:oauth_token)
3136 |> assign(:user, token.user)
3137 |> put_session(:oauth_token, token.token)
3140 assert conn.status == 200
3143 test "saves referer path to session", %{conn: conn, path: path} do
3144 conn = get(conn, path)
3145 return_to = Plug.Conn.get_session(conn, :return_to)
3147 assert return_to == path
3150 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3151 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3152 auth = insert(:oauth_authorization, app: app)
3156 |> put_session(:return_to, path)
3157 |> get("/web/login", %{code: auth.token})
3159 assert conn.status == 302
3160 assert redirected_to(conn) == path
3163 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3164 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3165 auth = insert(:oauth_authorization, app: app)
3167 conn = get(conn, "/web/login", %{code: auth.token})
3169 assert conn.status == 302
3170 assert redirected_to(conn) == "/web/getting-started"
3174 describe "scheduled activities" do
3175 test "creates a scheduled activity", %{conn: conn} do
3176 user = insert(:user)
3177 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3181 |> assign(:user, user)
3182 |> post("/api/v1/statuses", %{
3183 "status" => "scheduled",
3184 "scheduled_at" => scheduled_at
3187 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3188 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3189 assert [] == Repo.all(Activity)
3192 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3193 user = insert(:user)
3194 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3196 file = %Plug.Upload{
3197 content_type: "image/jpg",
3198 path: Path.absname("test/fixtures/image.jpg"),
3199 filename: "an_image.jpg"
3202 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3206 |> assign(:user, user)
3207 |> post("/api/v1/statuses", %{
3208 "media_ids" => [to_string(upload.id)],
3209 "status" => "scheduled",
3210 "scheduled_at" => scheduled_at
3213 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3214 assert %{"type" => "image"} = media_attachment
3217 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3219 user = insert(:user)
3222 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3226 |> assign(:user, user)
3227 |> post("/api/v1/statuses", %{
3228 "status" => "not scheduled",
3229 "scheduled_at" => scheduled_at
3232 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3233 assert [] == Repo.all(ScheduledActivity)
3236 test "returns error when daily user limit is exceeded", %{conn: conn} do
3237 user = insert(:user)
3240 NaiveDateTime.utc_now()
3241 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3242 |> NaiveDateTime.to_iso8601()
3244 attrs = %{params: %{}, scheduled_at: today}
3245 {:ok, _} = ScheduledActivity.create(user, attrs)
3246 {:ok, _} = ScheduledActivity.create(user, attrs)
3250 |> assign(:user, user)
3251 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3253 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3256 test "returns error when total user limit is exceeded", %{conn: conn} do
3257 user = insert(:user)
3260 NaiveDateTime.utc_now()
3261 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3262 |> NaiveDateTime.to_iso8601()
3265 NaiveDateTime.utc_now()
3266 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3267 |> NaiveDateTime.to_iso8601()
3269 attrs = %{params: %{}, scheduled_at: today}
3270 {:ok, _} = ScheduledActivity.create(user, attrs)
3271 {:ok, _} = ScheduledActivity.create(user, attrs)
3272 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3276 |> assign(:user, user)
3277 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3279 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3282 test "shows scheduled activities", %{conn: conn} do
3283 user = insert(:user)
3284 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3285 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3286 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3287 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3291 |> assign(:user, user)
3296 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3298 result = json_response(conn_res, 200)
3299 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3304 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3306 result = json_response(conn_res, 200)
3307 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3312 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3314 result = json_response(conn_res, 200)
3315 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3318 test "shows a scheduled activity", %{conn: conn} do
3319 user = insert(:user)
3320 scheduled_activity = insert(:scheduled_activity, user: user)
3324 |> assign(:user, user)
3325 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3327 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3328 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3332 |> assign(:user, user)
3333 |> get("/api/v1/scheduled_statuses/404")
3335 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3338 test "updates a scheduled activity", %{conn: conn} do
3339 user = insert(:user)
3340 scheduled_activity = insert(:scheduled_activity, user: user)
3343 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3347 |> assign(:user, user)
3348 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3349 scheduled_at: new_scheduled_at
3352 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3353 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3357 |> assign(:user, user)
3358 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3360 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3363 test "deletes a scheduled activity", %{conn: conn} do
3364 user = insert(:user)
3365 scheduled_activity = insert(:scheduled_activity, user: user)
3369 |> assign(:user, user)
3370 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3372 assert %{} = json_response(res_conn, 200)
3373 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3377 |> assign(:user, user)
3378 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3380 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3384 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3385 user1 = insert(:user)
3386 user2 = insert(:user)
3387 user3 = insert(:user)
3389 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3391 # Reply to status from another user
3394 |> assign(:user, user2)
3395 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3397 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3399 activity = Activity.get_by_id_with_object(id)
3401 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3402 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3404 # Reblog from the third user
3407 |> assign(:user, user3)
3408 |> post("/api/v1/statuses/#{activity.id}/reblog")
3410 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3411 json_response(conn2, 200)
3413 assert to_string(activity.id) == id
3415 # Getting third user status
3418 |> assign(:user, user3)
3419 |> get("api/v1/timelines/home")
3421 [reblogged_activity] = json_response(conn3, 200)
3423 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3425 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3426 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3429 describe "create account by app" do
3430 test "Account registration via Application", %{conn: conn} do
3433 |> post("/api/v1/apps", %{
3434 client_name: "client_name",
3435 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3436 scopes: "read, write, follow"
3440 "client_id" => client_id,
3441 "client_secret" => client_secret,
3443 "name" => "client_name",
3444 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3447 } = json_response(conn, 200)
3451 |> post("/oauth/token", %{
3452 grant_type: "client_credentials",
3453 client_id: client_id,
3454 client_secret: client_secret
3457 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3458 json_response(conn, 200)
3461 token_from_db = Repo.get_by(Token, token: token)
3462 assert token_from_db
3464 assert scope == "read write follow"
3468 |> put_req_header("authorization", "Bearer " <> token)
3469 |> post("/api/v1/accounts", %{
3471 email: "lain@example.org",
3472 password: "PlzDontHackLain",
3477 "access_token" => token,
3478 "created_at" => _created_at,
3480 "token_type" => "Bearer"
3481 } = json_response(conn, 200)
3483 token_from_db = Repo.get_by(Token, token: token)
3484 assert token_from_db
3485 token_from_db = Repo.preload(token_from_db, :user)
3486 assert token_from_db.user
3488 assert token_from_db.user.info.confirmation_pending
3491 test "rate limit", %{conn: conn} do
3492 app_token = insert(:oauth_token, user: nil)
3495 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3496 |> Map.put(:remote_ip, {15, 15, 15, 15})
3501 |> post("/api/v1/accounts", %{
3502 username: "#{i}lain",
3503 email: "#{i}lain@example.org",
3504 password: "PlzDontHackLain",
3509 "access_token" => token,
3510 "created_at" => _created_at,
3512 "token_type" => "Bearer"
3513 } = json_response(conn, 200)
3515 token_from_db = Repo.get_by(Token, token: token)
3516 assert token_from_db
3517 token_from_db = Repo.preload(token_from_db, :user)
3518 assert token_from_db.user
3520 assert token_from_db.user.info.confirmation_pending
3525 |> post("/api/v1/accounts", %{
3527 email: "6lain@example.org",
3528 password: "PlzDontHackLain",
3532 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3536 describe "GET /api/v1/polls/:id" do
3537 test "returns poll entity for object id", %{conn: conn} do
3538 user = insert(:user)
3541 CommonAPI.post(user, %{
3542 "status" => "Pleroma does",
3543 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3546 object = Object.normalize(activity)
3550 |> assign(:user, user)
3551 |> get("/api/v1/polls/#{object.id}")
3553 response = json_response(conn, 200)
3554 id = to_string(object.id)
3555 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3558 test "does not expose polls for private statuses", %{conn: conn} do
3559 user = insert(:user)
3560 other_user = insert(:user)
3563 CommonAPI.post(user, %{
3564 "status" => "Pleroma does",
3565 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3566 "visibility" => "private"
3569 object = Object.normalize(activity)
3573 |> assign(:user, other_user)
3574 |> get("/api/v1/polls/#{object.id}")
3576 assert json_response(conn, 404)
3580 describe "POST /api/v1/polls/:id/votes" do
3581 test "votes are added to the poll", %{conn: conn} do
3582 user = insert(:user)
3583 other_user = insert(:user)
3586 CommonAPI.post(user, %{
3587 "status" => "A very delicious sandwich",
3589 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3595 object = Object.normalize(activity)
3599 |> assign(:user, other_user)
3600 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3602 assert json_response(conn, 200)
3603 object = Object.get_by_id(object.id)
3605 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3610 test "author can't vote", %{conn: conn} do
3611 user = insert(:user)
3614 CommonAPI.post(user, %{
3615 "status" => "Am I cute?",
3616 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3619 object = Object.normalize(activity)
3622 |> assign(:user, user)
3623 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3624 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3626 object = Object.get_by_id(object.id)
3628 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3631 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3632 user = insert(:user)
3633 other_user = insert(:user)
3636 CommonAPI.post(user, %{
3637 "status" => "The glass is",
3638 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3641 object = Object.normalize(activity)
3644 |> assign(:user, other_user)
3645 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3646 |> json_response(422) == %{"error" => "Too many choices"}
3648 object = Object.get_by_id(object.id)
3650 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3655 test "does not allow choice index to be greater than options count", %{conn: conn} do
3656 user = insert(:user)
3657 other_user = insert(:user)
3660 CommonAPI.post(user, %{
3661 "status" => "Am I cute?",
3662 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3665 object = Object.normalize(activity)
3669 |> assign(:user, other_user)
3670 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3672 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3675 test "returns 404 error when object is not exist", %{conn: conn} do
3676 user = insert(:user)
3680 |> assign(:user, user)
3681 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3683 assert json_response(conn, 404) == %{"error" => "Record not found"}
3686 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3687 user = insert(:user)
3688 other_user = insert(:user)
3691 CommonAPI.post(user, %{
3692 "status" => "Am I cute?",
3693 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3694 "visibility" => "private"
3697 object = Object.normalize(activity)
3701 |> assign(:user, other_user)
3702 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3704 assert json_response(conn, 404) == %{"error" => "Record not found"}
3708 describe "GET /api/v1/statuses/:id/favourited_by" do
3710 user = insert(:user)
3711 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3715 |> assign(:user, user)
3717 [conn: conn, activity: activity, user: user]
3720 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3721 other_user = insert(:user)
3722 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3726 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3727 |> json_response(:ok)
3729 [%{"id" => id}] = response
3731 assert id == other_user.id
3734 test "returns empty array when status has not been favorited yet", %{
3740 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3741 |> json_response(:ok)
3743 assert Enum.empty?(response)
3746 test "does not return users who have favorited the status but are blocked", %{
3747 conn: %{assigns: %{user: user}} = conn,
3750 other_user = insert(:user)
3751 {:ok, user} = User.block(user, other_user)
3753 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3757 |> assign(:user, user)
3758 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3759 |> json_response(:ok)
3761 assert Enum.empty?(response)
3764 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3765 other_user = insert(:user)
3766 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3770 |> assign(:user, nil)
3771 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3772 |> json_response(:ok)
3774 [%{"id" => id}] = response
3775 assert id == other_user.id
3778 test "requires authentification for private posts", %{conn: conn, user: user} do
3779 other_user = insert(:user)
3782 CommonAPI.post(user, %{
3783 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3784 "visibility" => "direct"
3787 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3790 |> assign(:user, nil)
3791 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3792 |> json_response(404)
3796 |> assign(:user, other_user)
3797 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3798 |> json_response(200)
3800 [%{"id" => id}] = response
3801 assert id == other_user.id
3805 describe "GET /api/v1/statuses/:id/reblogged_by" do
3807 user = insert(:user)
3808 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3812 |> assign(:user, user)
3814 [conn: conn, activity: activity, user: user]
3817 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3818 other_user = insert(:user)
3819 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3823 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3824 |> json_response(:ok)
3826 [%{"id" => id}] = response
3828 assert id == other_user.id
3831 test "returns empty array when status has not been reblogged yet", %{
3837 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3838 |> json_response(:ok)
3840 assert Enum.empty?(response)
3843 test "does not return users who have reblogged the status but are blocked", %{
3844 conn: %{assigns: %{user: user}} = conn,
3847 other_user = insert(:user)
3848 {:ok, user} = User.block(user, other_user)
3850 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3854 |> assign(:user, user)
3855 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3856 |> json_response(:ok)
3858 assert Enum.empty?(response)
3861 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3862 other_user = insert(:user)
3863 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3867 |> assign(:user, nil)
3868 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3869 |> json_response(:ok)
3871 [%{"id" => id}] = response
3872 assert id == other_user.id
3875 test "requires authentification for private posts", %{conn: conn, user: user} do
3876 other_user = insert(:user)
3879 CommonAPI.post(user, %{
3880 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3881 "visibility" => "direct"
3885 |> assign(:user, nil)
3886 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3887 |> json_response(404)
3891 |> assign(:user, other_user)
3892 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3893 |> json_response(200)
3895 assert [] == response
3899 describe "POST /auth/password, with valid parameters" do
3900 setup %{conn: conn} do
3901 user = insert(:user)
3902 conn = post(conn, "/auth/password?email=#{user.email}")
3903 %{conn: conn, user: user}
3906 test "it returns 204", %{conn: conn} do
3907 assert json_response(conn, :no_content)
3910 test "it creates a PasswordResetToken record for user", %{user: user} do
3911 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3915 test "it sends an email to user", %{user: user} do
3916 ObanHelpers.perform_all()
3917 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3919 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3920 notify_email = Config.get([:instance, :notify_email])
3921 instance_name = Config.get([:instance, :name])
3924 from: {instance_name, notify_email},
3925 to: {user.name, user.email},
3926 html_body: email.html_body
3931 describe "POST /auth/password, with invalid parameters" do
3933 user = insert(:user)
3937 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3938 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3939 assert conn.status == 404
3940 assert conn.resp_body == ""
3943 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3944 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3945 conn = post(conn, "/auth/password?email=#{user.email}")
3946 assert conn.status == 400
3947 assert conn.resp_body == ""
3951 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3955 |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
3958 assert user.info.confirmation_pending
3963 clear_config([:instance, :account_activation_required]) do
3964 Config.put([:instance, :account_activation_required], true)
3967 test "resend account confirmation email", %{conn: conn, user: user} do
3969 |> assign(:user, user)
3970 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3971 |> json_response(:no_content)
3973 ObanHelpers.perform_all()
3975 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3976 notify_email = Config.get([:instance, :notify_email])
3977 instance_name = Config.get([:instance, :name])
3980 from: {instance_name, notify_email},
3981 to: {user.name, user.email},
3982 html_body: email.html_body
3987 describe "GET /api/v1/suggestions" do
3989 user = insert(:user)
3990 other_user = insert(:user)
3991 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3992 url500 = "http://test500?#{host}&#{user.nickname}"
3993 url200 = "http://test200?#{host}&#{user.nickname}"
3996 %{method: :get, url: ^url500} ->
3997 %Tesla.Env{status: 500, body: "bad request"}
3999 %{method: :get, url: ^url200} ->
4003 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4005 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4009 [user: user, other_user: other_user]
4012 clear_config(:suggestions)
4014 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4015 Config.put([:suggestions, :enabled], false)
4019 |> assign(:user, user)
4020 |> get("/api/v1/suggestions")
4021 |> json_response(200)
4026 test "returns error", %{conn: conn, user: user} do
4027 Config.put([:suggestions, :enabled], true)
4028 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4030 assert capture_log(fn ->
4033 |> assign(:user, user)
4034 |> get("/api/v1/suggestions")
4035 |> json_response(500)
4037 assert res == "Something went wrong"
4038 end) =~ "Could not retrieve suggestions"
4041 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4042 Config.put([:suggestions, :enabled], true)
4043 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4047 |> assign(:user, user)
4048 |> get("/api/v1/suggestions")
4049 |> json_response(200)
4054 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4055 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4059 "acct" => other_user.ap_id,
4060 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4061 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4062 "id" => other_user.id