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 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1019 }\">@<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 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1041 }\">@<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
2616 user = User.get_cached_by_id(user.id)
2617 info_change = Changeset.change(user.info, %{deactivated: nil})
2621 |> Changeset.change()
2622 |> Changeset.put_embed(:info, info_change)
2623 |> User.update_and_set_cache()
2625 Pleroma.Stats.force_update()
2627 conn = get(conn, "/api/v1/instance")
2629 assert result = json_response(conn, 200)
2631 stats = result["stats"]
2634 assert stats["user_count"] == 1
2635 assert stats["status_count"] == 1
2636 assert stats["domain_count"] == 2
2639 test "get peers", %{conn: conn} do
2640 insert(:user, %{local: false, nickname: "u@peer1.com"})
2641 insert(:user, %{local: false, nickname: "u@peer2.com"})
2643 Pleroma.Stats.force_update()
2645 conn = get(conn, "/api/v1/instance/peers")
2647 assert result = json_response(conn, 200)
2649 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2652 test "put settings", %{conn: conn} do
2653 user = insert(:user)
2657 |> assign(:user, user)
2658 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2660 assert _result = json_response(conn, 200)
2662 user = User.get_cached_by_ap_id(user.ap_id)
2663 assert user.info.settings == %{"programming" => "socks"}
2666 describe "pinned statuses" do
2668 user = insert(:user)
2669 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2671 [user: user, activity: activity]
2674 clear_config([:instance, :max_pinned_statuses]) do
2675 Config.put([:instance, :max_pinned_statuses], 1)
2678 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2679 {:ok, _} = CommonAPI.pin(activity.id, user)
2683 |> assign(:user, user)
2684 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2685 |> json_response(200)
2687 id_str = to_string(activity.id)
2689 assert [%{"id" => ^id_str, "pinned" => true}] = result
2692 test "pin status", %{conn: conn, user: user, activity: activity} do
2693 id_str = to_string(activity.id)
2695 assert %{"id" => ^id_str, "pinned" => true} =
2697 |> assign(:user, user)
2698 |> post("/api/v1/statuses/#{activity.id}/pin")
2699 |> json_response(200)
2701 assert [%{"id" => ^id_str, "pinned" => true}] =
2703 |> assign(:user, user)
2704 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2705 |> json_response(200)
2708 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2709 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2713 |> assign(:user, user)
2714 |> post("/api/v1/statuses/#{dm.id}/pin")
2716 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2719 test "unpin status", %{conn: conn, user: user, activity: activity} do
2720 {:ok, _} = CommonAPI.pin(activity.id, user)
2722 id_str = to_string(activity.id)
2723 user = refresh_record(user)
2725 assert %{"id" => ^id_str, "pinned" => false} =
2727 |> assign(:user, user)
2728 |> post("/api/v1/statuses/#{activity.id}/unpin")
2729 |> json_response(200)
2733 |> assign(:user, user)
2734 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2735 |> json_response(200)
2738 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2741 |> assign(:user, user)
2742 |> post("/api/v1/statuses/1/unpin")
2744 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2747 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2748 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2750 id_str_one = to_string(activity_one.id)
2752 assert %{"id" => ^id_str_one, "pinned" => true} =
2754 |> assign(:user, user)
2755 |> post("/api/v1/statuses/#{id_str_one}/pin")
2756 |> json_response(200)
2758 user = refresh_record(user)
2760 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2762 |> assign(:user, user)
2763 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2764 |> json_response(400)
2770 Config.put([:rich_media, :enabled], true)
2772 user = insert(:user)
2776 test "returns rich-media card", %{conn: conn, user: user} do
2777 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2780 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2781 "provider_name" => "example.com",
2782 "provider_url" => "https://example.com",
2783 "title" => "The Rock",
2785 "url" => "https://example.com/ogp",
2787 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2790 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2791 "title" => "The Rock",
2792 "type" => "video.movie",
2793 "url" => "https://example.com/ogp",
2795 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2802 |> get("/api/v1/statuses/#{activity.id}/card")
2803 |> json_response(200)
2805 assert response == card_data
2807 # works with private posts
2809 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2813 |> assign(:user, user)
2814 |> get("/api/v1/statuses/#{activity.id}/card")
2815 |> json_response(200)
2817 assert response_two == card_data
2820 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2822 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2826 |> get("/api/v1/statuses/#{activity.id}/card")
2827 |> json_response(:ok)
2829 assert response == %{
2831 "title" => "Pleroma",
2832 "description" => "",
2834 "provider_name" => "example.com",
2835 "provider_url" => "https://example.com",
2836 "url" => "https://example.com/ogp-missing-data",
2839 "title" => "Pleroma",
2840 "type" => "website",
2841 "url" => "https://example.com/ogp-missing-data"
2849 user = insert(:user)
2850 for_user = insert(:user)
2853 CommonAPI.post(user, %{
2854 "status" => "heweoo?"
2858 CommonAPI.post(user, %{
2859 "status" => "heweoo!"
2864 |> assign(:user, for_user)
2865 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2867 assert json_response(response1, 200)["bookmarked"] == true
2871 |> assign(:user, for_user)
2872 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2874 assert json_response(response2, 200)["bookmarked"] == true
2878 |> assign(:user, for_user)
2879 |> get("/api/v1/bookmarks")
2881 assert [json_response(response2, 200), json_response(response1, 200)] ==
2882 json_response(bookmarks, 200)
2886 |> assign(:user, for_user)
2887 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2889 assert json_response(response1, 200)["bookmarked"] == false
2893 |> assign(:user, for_user)
2894 |> get("/api/v1/bookmarks")
2896 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2899 describe "conversation muting" do
2901 post_user = insert(:user)
2902 user = insert(:user)
2904 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2906 [user: user, activity: activity]
2909 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2910 id_str = to_string(activity.id)
2912 assert %{"id" => ^id_str, "muted" => true} =
2914 |> assign(:user, user)
2915 |> post("/api/v1/statuses/#{activity.id}/mute")
2916 |> json_response(200)
2919 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2920 {:ok, _} = CommonAPI.add_mute(user, activity)
2924 |> assign(:user, user)
2925 |> post("/api/v1/statuses/#{activity.id}/mute")
2927 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2930 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2931 {:ok, _} = CommonAPI.add_mute(user, activity)
2933 id_str = to_string(activity.id)
2934 user = refresh_record(user)
2936 assert %{"id" => ^id_str, "muted" => false} =
2938 |> assign(:user, user)
2939 |> post("/api/v1/statuses/#{activity.id}/unmute")
2940 |> json_response(200)
2944 describe "reports" do
2946 reporter = insert(:user)
2947 target_user = insert(:user)
2949 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2951 [reporter: reporter, target_user: target_user, activity: activity]
2954 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2955 assert %{"action_taken" => false, "id" => _} =
2957 |> assign(:user, reporter)
2958 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2959 |> json_response(200)
2962 test "submit a report with statuses and comment", %{
2965 target_user: target_user,
2968 assert %{"action_taken" => false, "id" => _} =
2970 |> assign(:user, reporter)
2971 |> post("/api/v1/reports", %{
2972 "account_id" => target_user.id,
2973 "status_ids" => [activity.id],
2974 "comment" => "bad status!",
2975 "forward" => "false"
2977 |> json_response(200)
2980 test "account_id is required", %{
2985 assert %{"error" => "Valid `account_id` required"} =
2987 |> assign(:user, reporter)
2988 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2989 |> json_response(400)
2992 test "comment must be up to the size specified in the config", %{
2995 target_user: target_user
2997 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2998 comment = String.pad_trailing("a", max_size + 1, "a")
3000 error = %{"error" => "Comment must be up to #{max_size} characters"}
3004 |> assign(:user, reporter)
3005 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3006 |> json_response(400)
3009 test "returns error when account is not exist", %{
3016 |> assign(:user, reporter)
3017 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3019 assert json_response(conn, 400) == %{"error" => "Account not found"}
3023 describe "link headers" do
3024 test "preserves parameters in link headers", %{conn: conn} do
3025 user = insert(:user)
3026 other_user = insert(:user)
3029 CommonAPI.post(other_user, %{
3030 "status" => "hi @#{user.nickname}",
3031 "visibility" => "public"
3035 CommonAPI.post(other_user, %{
3036 "status" => "hi @#{user.nickname}",
3037 "visibility" => "public"
3040 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3041 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3045 |> assign(:user, user)
3046 |> get("/api/v1/notifications", %{media_only: true})
3048 assert [link_header] = get_resp_header(conn, "link")
3049 assert link_header =~ ~r/media_only=true/
3050 assert link_header =~ ~r/min_id=#{notification2.id}/
3051 assert link_header =~ ~r/max_id=#{notification1.id}/
3055 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3056 # Need to set an old-style integer ID to reproduce the problem
3057 # (these are no longer assigned to new accounts but were preserved
3058 # for existing accounts during the migration to flakeIDs)
3059 user_one = insert(:user, %{id: 1212})
3060 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3064 |> get("/api/v1/accounts/#{user_one.id}")
3068 |> get("/api/v1/accounts/#{user_two.nickname}")
3072 |> get("/api/v1/accounts/#{user_two.id}")
3074 acc_one = json_response(resp_one, 200)
3075 acc_two = json_response(resp_two, 200)
3076 acc_three = json_response(resp_three, 200)
3077 refute acc_one == acc_two
3078 assert acc_two == acc_three
3081 describe "custom emoji" do
3082 test "with tags", %{conn: conn} do
3085 |> get("/api/v1/custom_emojis")
3086 |> json_response(200)
3088 assert Map.has_key?(emoji, "shortcode")
3089 assert Map.has_key?(emoji, "static_url")
3090 assert Map.has_key?(emoji, "tags")
3091 assert is_list(emoji["tags"])
3092 assert Map.has_key?(emoji, "category")
3093 assert Map.has_key?(emoji, "url")
3094 assert Map.has_key?(emoji, "visible_in_picker")
3098 describe "index/2 redirections" do
3099 setup %{conn: conn} do
3103 signing_salt: "cooldude"
3108 |> Plug.Session.call(Plug.Session.init(session_opts))
3111 test_path = "/web/statuses/test"
3112 %{conn: conn, path: test_path}
3115 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3116 conn = get(conn, path)
3118 assert conn.status == 302
3119 assert redirected_to(conn) == "/web/login"
3122 test "redirects not logged-in users to the login page on private instances", %{
3126 Config.put([:instance, :public], false)
3128 conn = get(conn, path)
3130 assert conn.status == 302
3131 assert redirected_to(conn) == "/web/login"
3134 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3135 token = insert(:oauth_token)
3139 |> assign(:user, token.user)
3140 |> put_session(:oauth_token, token.token)
3143 assert conn.status == 200
3146 test "saves referer path to session", %{conn: conn, path: path} do
3147 conn = get(conn, path)
3148 return_to = Plug.Conn.get_session(conn, :return_to)
3150 assert return_to == path
3153 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3154 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3155 auth = insert(:oauth_authorization, app: app)
3159 |> put_session(:return_to, path)
3160 |> get("/web/login", %{code: auth.token})
3162 assert conn.status == 302
3163 assert redirected_to(conn) == path
3166 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3167 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3168 auth = insert(:oauth_authorization, app: app)
3170 conn = get(conn, "/web/login", %{code: auth.token})
3172 assert conn.status == 302
3173 assert redirected_to(conn) == "/web/getting-started"
3177 describe "scheduled activities" do
3178 test "creates a scheduled activity", %{conn: conn} do
3179 user = insert(:user)
3180 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3184 |> assign(:user, user)
3185 |> post("/api/v1/statuses", %{
3186 "status" => "scheduled",
3187 "scheduled_at" => scheduled_at
3190 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3191 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3192 assert [] == Repo.all(Activity)
3195 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3196 user = insert(:user)
3197 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3199 file = %Plug.Upload{
3200 content_type: "image/jpg",
3201 path: Path.absname("test/fixtures/image.jpg"),
3202 filename: "an_image.jpg"
3205 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3209 |> assign(:user, user)
3210 |> post("/api/v1/statuses", %{
3211 "media_ids" => [to_string(upload.id)],
3212 "status" => "scheduled",
3213 "scheduled_at" => scheduled_at
3216 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3217 assert %{"type" => "image"} = media_attachment
3220 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3222 user = insert(:user)
3225 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3229 |> assign(:user, user)
3230 |> post("/api/v1/statuses", %{
3231 "status" => "not scheduled",
3232 "scheduled_at" => scheduled_at
3235 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3236 assert [] == Repo.all(ScheduledActivity)
3239 test "returns error when daily user limit is exceeded", %{conn: conn} do
3240 user = insert(:user)
3243 NaiveDateTime.utc_now()
3244 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3245 |> NaiveDateTime.to_iso8601()
3247 attrs = %{params: %{}, scheduled_at: today}
3248 {:ok, _} = ScheduledActivity.create(user, attrs)
3249 {:ok, _} = ScheduledActivity.create(user, attrs)
3253 |> assign(:user, user)
3254 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3256 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3259 test "returns error when total user limit is exceeded", %{conn: conn} do
3260 user = insert(:user)
3263 NaiveDateTime.utc_now()
3264 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3265 |> NaiveDateTime.to_iso8601()
3268 NaiveDateTime.utc_now()
3269 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3270 |> NaiveDateTime.to_iso8601()
3272 attrs = %{params: %{}, scheduled_at: today}
3273 {:ok, _} = ScheduledActivity.create(user, attrs)
3274 {:ok, _} = ScheduledActivity.create(user, attrs)
3275 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3279 |> assign(:user, user)
3280 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3282 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3285 test "shows scheduled activities", %{conn: conn} do
3286 user = insert(:user)
3287 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3288 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3289 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3290 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3294 |> assign(:user, user)
3299 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3301 result = json_response(conn_res, 200)
3302 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3307 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3309 result = json_response(conn_res, 200)
3310 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3315 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3317 result = json_response(conn_res, 200)
3318 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3321 test "shows a scheduled activity", %{conn: conn} do
3322 user = insert(:user)
3323 scheduled_activity = insert(:scheduled_activity, user: user)
3327 |> assign(:user, user)
3328 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3330 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3331 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3335 |> assign(:user, user)
3336 |> get("/api/v1/scheduled_statuses/404")
3338 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3341 test "updates a scheduled activity", %{conn: conn} do
3342 user = insert(:user)
3343 scheduled_activity = insert(:scheduled_activity, user: user)
3346 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3350 |> assign(:user, user)
3351 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3352 scheduled_at: new_scheduled_at
3355 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3356 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3360 |> assign(:user, user)
3361 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3363 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3366 test "deletes a scheduled activity", %{conn: conn} do
3367 user = insert(:user)
3368 scheduled_activity = insert(:scheduled_activity, user: user)
3372 |> assign(:user, user)
3373 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3375 assert %{} = json_response(res_conn, 200)
3376 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3380 |> assign(:user, user)
3381 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3383 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3387 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3388 user1 = insert(:user)
3389 user2 = insert(:user)
3390 user3 = insert(:user)
3392 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3394 # Reply to status from another user
3397 |> assign(:user, user2)
3398 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3400 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3402 activity = Activity.get_by_id_with_object(id)
3404 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3405 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3407 # Reblog from the third user
3410 |> assign(:user, user3)
3411 |> post("/api/v1/statuses/#{activity.id}/reblog")
3413 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3414 json_response(conn2, 200)
3416 assert to_string(activity.id) == id
3418 # Getting third user status
3421 |> assign(:user, user3)
3422 |> get("api/v1/timelines/home")
3424 [reblogged_activity] = json_response(conn3, 200)
3426 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3428 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3429 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3432 describe "create account by app" do
3433 test "Account registration via Application", %{conn: conn} do
3436 |> post("/api/v1/apps", %{
3437 client_name: "client_name",
3438 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3439 scopes: "read, write, follow"
3443 "client_id" => client_id,
3444 "client_secret" => client_secret,
3446 "name" => "client_name",
3447 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3450 } = json_response(conn, 200)
3454 |> post("/oauth/token", %{
3455 grant_type: "client_credentials",
3456 client_id: client_id,
3457 client_secret: client_secret
3460 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3461 json_response(conn, 200)
3464 token_from_db = Repo.get_by(Token, token: token)
3465 assert token_from_db
3467 assert scope == "read write follow"
3471 |> put_req_header("authorization", "Bearer " <> token)
3472 |> post("/api/v1/accounts", %{
3474 email: "lain@example.org",
3475 password: "PlzDontHackLain",
3480 "access_token" => token,
3481 "created_at" => _created_at,
3483 "token_type" => "Bearer"
3484 } = json_response(conn, 200)
3486 token_from_db = Repo.get_by(Token, token: token)
3487 assert token_from_db
3488 token_from_db = Repo.preload(token_from_db, :user)
3489 assert token_from_db.user
3491 assert token_from_db.user.info.confirmation_pending
3494 test "rate limit", %{conn: conn} do
3495 app_token = insert(:oauth_token, user: nil)
3498 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3499 |> Map.put(:remote_ip, {15, 15, 15, 15})
3504 |> post("/api/v1/accounts", %{
3505 username: "#{i}lain",
3506 email: "#{i}lain@example.org",
3507 password: "PlzDontHackLain",
3512 "access_token" => token,
3513 "created_at" => _created_at,
3515 "token_type" => "Bearer"
3516 } = json_response(conn, 200)
3518 token_from_db = Repo.get_by(Token, token: token)
3519 assert token_from_db
3520 token_from_db = Repo.preload(token_from_db, :user)
3521 assert token_from_db.user
3523 assert token_from_db.user.info.confirmation_pending
3528 |> post("/api/v1/accounts", %{
3530 email: "6lain@example.org",
3531 password: "PlzDontHackLain",
3535 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3539 describe "GET /api/v1/polls/:id" do
3540 test "returns poll entity for object id", %{conn: conn} do
3541 user = insert(:user)
3544 CommonAPI.post(user, %{
3545 "status" => "Pleroma does",
3546 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3549 object = Object.normalize(activity)
3553 |> assign(:user, user)
3554 |> get("/api/v1/polls/#{object.id}")
3556 response = json_response(conn, 200)
3557 id = to_string(object.id)
3558 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3561 test "does not expose polls for private statuses", %{conn: conn} do
3562 user = insert(:user)
3563 other_user = insert(:user)
3566 CommonAPI.post(user, %{
3567 "status" => "Pleroma does",
3568 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3569 "visibility" => "private"
3572 object = Object.normalize(activity)
3576 |> assign(:user, other_user)
3577 |> get("/api/v1/polls/#{object.id}")
3579 assert json_response(conn, 404)
3583 describe "POST /api/v1/polls/:id/votes" do
3584 test "votes are added to the poll", %{conn: conn} do
3585 user = insert(:user)
3586 other_user = insert(:user)
3589 CommonAPI.post(user, %{
3590 "status" => "A very delicious sandwich",
3592 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3598 object = Object.normalize(activity)
3602 |> assign(:user, other_user)
3603 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3605 assert json_response(conn, 200)
3606 object = Object.get_by_id(object.id)
3608 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3613 test "author can't vote", %{conn: conn} do
3614 user = insert(:user)
3617 CommonAPI.post(user, %{
3618 "status" => "Am I cute?",
3619 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3622 object = Object.normalize(activity)
3625 |> assign(:user, user)
3626 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3627 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3629 object = Object.get_by_id(object.id)
3631 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3634 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3635 user = insert(:user)
3636 other_user = insert(:user)
3639 CommonAPI.post(user, %{
3640 "status" => "The glass is",
3641 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3644 object = Object.normalize(activity)
3647 |> assign(:user, other_user)
3648 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3649 |> json_response(422) == %{"error" => "Too many choices"}
3651 object = Object.get_by_id(object.id)
3653 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3658 test "does not allow choice index to be greater than options count", %{conn: conn} do
3659 user = insert(:user)
3660 other_user = insert(:user)
3663 CommonAPI.post(user, %{
3664 "status" => "Am I cute?",
3665 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3668 object = Object.normalize(activity)
3672 |> assign(:user, other_user)
3673 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3675 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3678 test "returns 404 error when object is not exist", %{conn: conn} do
3679 user = insert(:user)
3683 |> assign(:user, user)
3684 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3686 assert json_response(conn, 404) == %{"error" => "Record not found"}
3689 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3690 user = insert(:user)
3691 other_user = insert(:user)
3694 CommonAPI.post(user, %{
3695 "status" => "Am I cute?",
3696 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3697 "visibility" => "private"
3700 object = Object.normalize(activity)
3704 |> assign(:user, other_user)
3705 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3707 assert json_response(conn, 404) == %{"error" => "Record not found"}
3711 describe "GET /api/v1/statuses/:id/favourited_by" do
3713 user = insert(:user)
3714 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3718 |> assign(:user, user)
3720 [conn: conn, activity: activity, user: user]
3723 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3724 other_user = insert(:user)
3725 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3729 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3730 |> json_response(:ok)
3732 [%{"id" => id}] = response
3734 assert id == other_user.id
3737 test "returns empty array when status has not been favorited yet", %{
3743 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3744 |> json_response(:ok)
3746 assert Enum.empty?(response)
3749 test "does not return users who have favorited the status but are blocked", %{
3750 conn: %{assigns: %{user: user}} = conn,
3753 other_user = insert(:user)
3754 {:ok, user} = User.block(user, other_user)
3756 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3760 |> assign(:user, user)
3761 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3762 |> json_response(:ok)
3764 assert Enum.empty?(response)
3767 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3768 other_user = insert(:user)
3769 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3773 |> assign(:user, nil)
3774 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3775 |> json_response(:ok)
3777 [%{"id" => id}] = response
3778 assert id == other_user.id
3781 test "requires authentification for private posts", %{conn: conn, user: user} do
3782 other_user = insert(:user)
3785 CommonAPI.post(user, %{
3786 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3787 "visibility" => "direct"
3790 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3793 |> assign(:user, nil)
3794 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3795 |> json_response(404)
3799 |> assign(:user, other_user)
3800 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3801 |> json_response(200)
3803 [%{"id" => id}] = response
3804 assert id == other_user.id
3808 describe "GET /api/v1/statuses/:id/reblogged_by" do
3810 user = insert(:user)
3811 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3815 |> assign(:user, user)
3817 [conn: conn, activity: activity, user: user]
3820 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3821 other_user = insert(:user)
3822 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3826 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3827 |> json_response(:ok)
3829 [%{"id" => id}] = response
3831 assert id == other_user.id
3834 test "returns empty array when status has not been reblogged yet", %{
3840 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3841 |> json_response(:ok)
3843 assert Enum.empty?(response)
3846 test "does not return users who have reblogged the status but are blocked", %{
3847 conn: %{assigns: %{user: user}} = conn,
3850 other_user = insert(:user)
3851 {:ok, user} = User.block(user, other_user)
3853 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3857 |> assign(:user, user)
3858 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3859 |> json_response(:ok)
3861 assert Enum.empty?(response)
3864 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3865 other_user = insert(:user)
3866 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3870 |> assign(:user, nil)
3871 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3872 |> json_response(:ok)
3874 [%{"id" => id}] = response
3875 assert id == other_user.id
3878 test "requires authentification for private posts", %{conn: conn, user: user} do
3879 other_user = insert(:user)
3882 CommonAPI.post(user, %{
3883 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3884 "visibility" => "direct"
3888 |> assign(:user, nil)
3889 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3890 |> json_response(404)
3894 |> assign(:user, other_user)
3895 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3896 |> json_response(200)
3898 assert [] == response
3902 describe "POST /auth/password, with valid parameters" do
3903 setup %{conn: conn} do
3904 user = insert(:user)
3905 conn = post(conn, "/auth/password?email=#{user.email}")
3906 %{conn: conn, user: user}
3909 test "it returns 204", %{conn: conn} do
3910 assert json_response(conn, :no_content)
3913 test "it creates a PasswordResetToken record for user", %{user: user} do
3914 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3918 test "it sends an email to user", %{user: user} do
3919 ObanHelpers.perform_all()
3920 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3922 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3923 notify_email = Config.get([:instance, :notify_email])
3924 instance_name = Config.get([:instance, :name])
3927 from: {instance_name, notify_email},
3928 to: {user.name, user.email},
3929 html_body: email.html_body
3934 describe "POST /auth/password, with invalid parameters" do
3936 user = insert(:user)
3940 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3941 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3942 assert conn.status == 404
3943 assert conn.resp_body == ""
3946 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3947 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3948 conn = post(conn, "/auth/password?email=#{user.email}")
3949 assert conn.status == 400
3950 assert conn.resp_body == ""
3954 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3956 user = insert(:user)
3957 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3961 |> Changeset.change()
3962 |> Changeset.put_embed(:info, info_change)
3965 assert user.info.confirmation_pending
3970 clear_config([:instance, :account_activation_required]) do
3971 Config.put([:instance, :account_activation_required], true)
3974 test "resend account confirmation email", %{conn: conn, user: user} do
3976 |> assign(:user, user)
3977 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3978 |> json_response(:no_content)
3980 ObanHelpers.perform_all()
3982 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3983 notify_email = Config.get([:instance, :notify_email])
3984 instance_name = Config.get([:instance, :name])
3987 from: {instance_name, notify_email},
3988 to: {user.name, user.email},
3989 html_body: email.html_body
3994 describe "GET /api/v1/suggestions" do
3996 user = insert(:user)
3997 other_user = insert(:user)
3998 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3999 url500 = "http://test500?#{host}&#{user.nickname}"
4000 url200 = "http://test200?#{host}&#{user.nickname}"
4003 %{method: :get, url: ^url500} ->
4004 %Tesla.Env{status: 500, body: "bad request"}
4006 %{method: :get, url: ^url200} ->
4010 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4012 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4016 [user: user, other_user: other_user]
4019 clear_config(:suggestions)
4021 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4022 Config.put([:suggestions, :enabled], false)
4026 |> assign(:user, user)
4027 |> get("/api/v1/suggestions")
4028 |> json_response(200)
4033 test "returns error", %{conn: conn, user: user} do
4034 Config.put([:suggestions, :enabled], true)
4035 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4037 assert capture_log(fn ->
4040 |> assign(:user, user)
4041 |> get("/api/v1/suggestions")
4042 |> json_response(500)
4044 assert res == "Something went wrong"
4045 end) =~ "Could not retrieve suggestions"
4048 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4049 Config.put([:suggestions, :enabled], true)
4050 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4054 |> assign(:user, user)
4055 |> get("/api/v1/suggestions")
4056 |> json_response(200)
4061 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4062 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4066 "acct" => other_user.ap_id,
4067 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4068 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4069 "id" => other_user.id