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 "reblogging" do
1003 test "reblogs and returns the reblogged status", %{conn: conn} do
1004 activity = insert(:note_activity)
1005 user = insert(:user)
1009 |> assign(:user, user)
1010 |> post("/api/v1/statuses/#{activity.id}/reblog")
1013 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1015 } = json_response(conn, 200)
1017 assert to_string(activity.id) == id
1020 test "reblogged status for another user", %{conn: conn} do
1021 activity = insert(:note_activity)
1022 user1 = insert(:user)
1023 user2 = insert(:user)
1024 user3 = insert(:user)
1025 CommonAPI.favorite(activity.id, user2)
1026 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1027 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1028 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1032 |> assign(:user, user3)
1033 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1036 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1037 "reblogged" => false,
1038 "favourited" => false,
1039 "bookmarked" => false
1040 } = json_response(conn_res, 200)
1044 |> assign(:user, user2)
1045 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1048 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1049 "reblogged" => true,
1050 "favourited" => true,
1051 "bookmarked" => true
1052 } = json_response(conn_res, 200)
1054 assert to_string(activity.id) == id
1057 test "returns 400 error when activity is not exist", %{conn: conn} do
1058 user = insert(:user)
1062 |> assign(:user, user)
1063 |> post("/api/v1/statuses/foo/reblog")
1065 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1069 describe "unreblogging" do
1070 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1071 activity = insert(:note_activity)
1072 user = insert(:user)
1074 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1078 |> assign(:user, user)
1079 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1081 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1083 assert to_string(activity.id) == id
1086 test "returns 400 error when activity is not exist", %{conn: conn} do
1087 user = insert(:user)
1091 |> assign(:user, user)
1092 |> post("/api/v1/statuses/foo/unreblog")
1094 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1098 describe "favoriting" do
1099 test "favs a status and returns it", %{conn: conn} do
1100 activity = insert(:note_activity)
1101 user = insert(:user)
1105 |> assign(:user, user)
1106 |> post("/api/v1/statuses/#{activity.id}/favourite")
1108 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1109 json_response(conn, 200)
1111 assert to_string(activity.id) == id
1114 test "returns 400 error for a wrong id", %{conn: conn} do
1115 user = insert(:user)
1119 |> assign(:user, user)
1120 |> post("/api/v1/statuses/1/favourite")
1122 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1126 describe "unfavoriting" do
1127 test "unfavorites a status and returns it", %{conn: conn} do
1128 activity = insert(:note_activity)
1129 user = insert(:user)
1131 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1135 |> assign(:user, user)
1136 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1138 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1139 json_response(conn, 200)
1141 assert to_string(activity.id) == id
1144 test "returns 400 error for a wrong id", %{conn: conn} do
1145 user = insert(:user)
1149 |> assign(:user, user)
1150 |> post("/api/v1/statuses/1/unfavourite")
1152 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1156 describe "user timelines" do
1157 test "gets a users statuses", %{conn: conn} do
1158 user_one = insert(:user)
1159 user_two = insert(:user)
1160 user_three = insert(:user)
1162 {:ok, user_three} = User.follow(user_three, user_one)
1164 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1166 {:ok, direct_activity} =
1167 CommonAPI.post(user_one, %{
1168 "status" => "Hi, @#{user_two.nickname}.",
1169 "visibility" => "direct"
1172 {:ok, private_activity} =
1173 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1177 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1179 assert [%{"id" => id}] = json_response(resp, 200)
1180 assert id == to_string(activity.id)
1184 |> assign(:user, user_two)
1185 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1187 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1188 assert id_one == to_string(direct_activity.id)
1189 assert id_two == to_string(activity.id)
1193 |> assign(:user, user_three)
1194 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1196 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1197 assert id_one == to_string(private_activity.id)
1198 assert id_two == to_string(activity.id)
1201 test "unimplemented pinned statuses feature", %{conn: conn} do
1202 note = insert(:note_activity)
1203 user = User.get_cached_by_ap_id(note.data["actor"])
1207 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1209 assert json_response(conn, 200) == []
1212 test "gets an users media", %{conn: conn} do
1213 note = insert(:note_activity)
1214 user = User.get_cached_by_ap_id(note.data["actor"])
1216 file = %Plug.Upload{
1217 content_type: "image/jpg",
1218 path: Path.absname("test/fixtures/image.jpg"),
1219 filename: "an_image.jpg"
1222 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1224 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1228 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1230 assert [%{"id" => id}] = json_response(conn, 200)
1231 assert id == to_string(image_post.id)
1235 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1237 assert [%{"id" => id}] = json_response(conn, 200)
1238 assert id == to_string(image_post.id)
1241 test "gets a user's statuses without reblogs", %{conn: conn} do
1242 user = insert(:user)
1243 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1244 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1248 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1250 assert [%{"id" => id}] = json_response(conn, 200)
1251 assert id == to_string(post.id)
1255 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1257 assert [%{"id" => id}] = json_response(conn, 200)
1258 assert id == to_string(post.id)
1261 test "filters user's statuses by a hashtag", %{conn: conn} do
1262 user = insert(:user)
1263 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1264 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1268 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1270 assert [%{"id" => id}] = json_response(conn, 200)
1271 assert id == to_string(post.id)
1275 describe "user relationships" do
1276 test "returns the relationships for the current user", %{conn: conn} do
1277 user = insert(:user)
1278 other_user = insert(:user)
1279 {:ok, user} = User.follow(user, other_user)
1283 |> assign(:user, user)
1284 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1286 assert [relationship] = json_response(conn, 200)
1288 assert to_string(other_user.id) == relationship["id"]
1292 describe "media upload" do
1294 user = insert(:user)
1298 |> assign(:user, user)
1300 image = %Plug.Upload{
1301 content_type: "image/jpg",
1302 path: Path.absname("test/fixtures/image.jpg"),
1303 filename: "an_image.jpg"
1306 [conn: conn, image: image]
1309 clear_config([:media_proxy])
1310 clear_config([Pleroma.Upload])
1312 test "returns uploaded image", %{conn: conn, image: image} do
1313 desc = "Description of the image"
1317 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1318 |> json_response(:ok)
1320 assert media["type"] == "image"
1321 assert media["description"] == desc
1324 object = Repo.get(Object, media["id"])
1325 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1329 describe "locked accounts" do
1330 test "/api/v1/follow_requests works" do
1331 user = insert(:user, %{info: %User.Info{locked: true}})
1332 other_user = insert(:user)
1334 {:ok, _activity} = ActivityPub.follow(other_user, user)
1336 user = User.get_cached_by_id(user.id)
1337 other_user = User.get_cached_by_id(other_user.id)
1339 assert User.following?(other_user, user) == false
1343 |> assign(:user, user)
1344 |> get("/api/v1/follow_requests")
1346 assert [relationship] = json_response(conn, 200)
1347 assert to_string(other_user.id) == relationship["id"]
1350 test "/api/v1/follow_requests/:id/authorize works" do
1351 user = insert(:user, %{info: %User.Info{locked: true}})
1352 other_user = insert(:user)
1354 {:ok, _activity} = ActivityPub.follow(other_user, user)
1356 user = User.get_cached_by_id(user.id)
1357 other_user = User.get_cached_by_id(other_user.id)
1359 assert User.following?(other_user, user) == false
1363 |> assign(:user, user)
1364 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1366 assert relationship = json_response(conn, 200)
1367 assert to_string(other_user.id) == relationship["id"]
1369 user = User.get_cached_by_id(user.id)
1370 other_user = User.get_cached_by_id(other_user.id)
1372 assert User.following?(other_user, user) == true
1375 test "verify_credentials", %{conn: conn} do
1376 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1380 |> assign(:user, user)
1381 |> get("/api/v1/accounts/verify_credentials")
1383 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1384 assert id == to_string(user.id)
1387 test "/api/v1/follow_requests/:id/reject works" do
1388 user = insert(:user, %{info: %User.Info{locked: true}})
1389 other_user = insert(:user)
1391 {:ok, _activity} = ActivityPub.follow(other_user, user)
1393 user = User.get_cached_by_id(user.id)
1397 |> assign(:user, user)
1398 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1400 assert relationship = json_response(conn, 200)
1401 assert to_string(other_user.id) == relationship["id"]
1403 user = User.get_cached_by_id(user.id)
1404 other_user = User.get_cached_by_id(other_user.id)
1406 assert User.following?(other_user, user) == false
1410 describe "account fetching" do
1411 test "works by id" do
1412 user = insert(:user)
1416 |> get("/api/v1/accounts/#{user.id}")
1418 assert %{"id" => id} = json_response(conn, 200)
1419 assert id == to_string(user.id)
1423 |> get("/api/v1/accounts/-1")
1425 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1428 test "works by nickname" do
1429 user = insert(:user)
1433 |> get("/api/v1/accounts/#{user.nickname}")
1435 assert %{"id" => id} = json_response(conn, 200)
1436 assert id == user.id
1439 test "works by nickname for remote users" do
1440 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1441 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1442 user = insert(:user, nickname: "user@example.com", local: false)
1446 |> get("/api/v1/accounts/#{user.nickname}")
1448 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1449 assert %{"id" => id} = json_response(conn, 200)
1450 assert id == user.id
1453 test "respects limit_to_local_content == :all for remote user nicknames" do
1454 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1455 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1457 user = insert(:user, nickname: "user@example.com", local: false)
1461 |> get("/api/v1/accounts/#{user.nickname}")
1463 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1464 assert json_response(conn, 404)
1467 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1468 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1469 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1471 user = insert(:user, nickname: "user@example.com", local: false)
1472 reading_user = insert(:user)
1476 |> get("/api/v1/accounts/#{user.nickname}")
1478 assert json_response(conn, 404)
1482 |> assign(:user, reading_user)
1483 |> get("/api/v1/accounts/#{user.nickname}")
1485 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1486 assert %{"id" => id} = json_response(conn, 200)
1487 assert id == user.id
1491 test "mascot upload", %{conn: conn} do
1492 user = insert(:user)
1494 non_image_file = %Plug.Upload{
1495 content_type: "audio/mpeg",
1496 path: Path.absname("test/fixtures/sound.mp3"),
1497 filename: "sound.mp3"
1502 |> assign(:user, user)
1503 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1505 assert json_response(conn, 415)
1507 file = %Plug.Upload{
1508 content_type: "image/jpg",
1509 path: Path.absname("test/fixtures/image.jpg"),
1510 filename: "an_image.jpg"
1515 |> assign(:user, user)
1516 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1518 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1521 test "mascot retrieving", %{conn: conn} do
1522 user = insert(:user)
1523 # When user hasn't set a mascot, we should just get pleroma tan back
1526 |> assign(:user, user)
1527 |> get("/api/v1/pleroma/mascot")
1529 assert %{"url" => url} = json_response(conn, 200)
1530 assert url =~ "pleroma-fox-tan-smol"
1532 # When a user sets their mascot, we should get that back
1533 file = %Plug.Upload{
1534 content_type: "image/jpg",
1535 path: Path.absname("test/fixtures/image.jpg"),
1536 filename: "an_image.jpg"
1541 |> assign(:user, user)
1542 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1544 assert json_response(conn, 200)
1546 user = User.get_cached_by_id(user.id)
1550 |> assign(:user, user)
1551 |> get("/api/v1/pleroma/mascot")
1553 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1554 assert url =~ "an_image"
1557 test "hashtag timeline", %{conn: conn} do
1558 following = insert(:user)
1561 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1563 {:ok, [_activity]} =
1564 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1568 |> get("/api/v1/timelines/tag/2hu")
1570 assert [%{"id" => id}] = json_response(nconn, 200)
1572 assert id == to_string(activity.id)
1574 # works for different capitalization too
1577 |> get("/api/v1/timelines/tag/2HU")
1579 assert [%{"id" => id}] = json_response(nconn, 200)
1581 assert id == to_string(activity.id)
1585 test "multi-hashtag timeline", %{conn: conn} do
1586 user = insert(:user)
1588 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1589 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1590 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1594 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1596 [status_none, status_test1, status_test] = json_response(any_test, 200)
1598 assert to_string(activity_test.id) == status_test["id"]
1599 assert to_string(activity_test1.id) == status_test1["id"]
1600 assert to_string(activity_none.id) == status_none["id"]
1604 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1606 assert [status_test1] == json_response(restricted_test, 200)
1608 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1610 assert [status_none] == json_response(all_test, 200)
1613 test "getting followers", %{conn: conn} do
1614 user = insert(:user)
1615 other_user = insert(:user)
1616 {:ok, user} = User.follow(user, other_user)
1620 |> get("/api/v1/accounts/#{other_user.id}/followers")
1622 assert [%{"id" => id}] = json_response(conn, 200)
1623 assert id == to_string(user.id)
1626 test "getting followers, hide_followers", %{conn: conn} do
1627 user = insert(:user)
1628 other_user = insert(:user, %{info: %{hide_followers: true}})
1629 {:ok, _user} = User.follow(user, other_user)
1633 |> get("/api/v1/accounts/#{other_user.id}/followers")
1635 assert [] == json_response(conn, 200)
1638 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1639 user = insert(:user)
1640 other_user = insert(:user, %{info: %{hide_followers: true}})
1641 {:ok, _user} = User.follow(user, other_user)
1645 |> assign(:user, other_user)
1646 |> get("/api/v1/accounts/#{other_user.id}/followers")
1648 refute [] == json_response(conn, 200)
1651 test "getting followers, pagination", %{conn: conn} do
1652 user = insert(:user)
1653 follower1 = insert(:user)
1654 follower2 = insert(:user)
1655 follower3 = insert(:user)
1656 {:ok, _} = User.follow(follower1, user)
1657 {:ok, _} = User.follow(follower2, user)
1658 {:ok, _} = User.follow(follower3, user)
1662 |> assign(:user, user)
1666 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1668 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1669 assert id3 == follower3.id
1670 assert id2 == follower2.id
1674 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1676 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1677 assert id2 == follower2.id
1678 assert id1 == follower1.id
1682 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1684 assert [%{"id" => id2}] = json_response(res_conn, 200)
1685 assert id2 == follower2.id
1687 assert [link_header] = get_resp_header(res_conn, "link")
1688 assert link_header =~ ~r/min_id=#{follower2.id}/
1689 assert link_header =~ ~r/max_id=#{follower2.id}/
1692 test "getting following", %{conn: conn} do
1693 user = insert(:user)
1694 other_user = insert(:user)
1695 {:ok, user} = User.follow(user, other_user)
1699 |> get("/api/v1/accounts/#{user.id}/following")
1701 assert [%{"id" => id}] = json_response(conn, 200)
1702 assert id == to_string(other_user.id)
1705 test "getting following, hide_follows", %{conn: conn} do
1706 user = insert(:user, %{info: %{hide_follows: true}})
1707 other_user = insert(:user)
1708 {:ok, user} = User.follow(user, other_user)
1712 |> get("/api/v1/accounts/#{user.id}/following")
1714 assert [] == json_response(conn, 200)
1717 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1718 user = insert(:user, %{info: %{hide_follows: true}})
1719 other_user = insert(:user)
1720 {:ok, user} = User.follow(user, other_user)
1724 |> assign(:user, user)
1725 |> get("/api/v1/accounts/#{user.id}/following")
1727 refute [] == json_response(conn, 200)
1730 test "getting following, pagination", %{conn: conn} do
1731 user = insert(:user)
1732 following1 = insert(:user)
1733 following2 = insert(:user)
1734 following3 = insert(:user)
1735 {:ok, _} = User.follow(user, following1)
1736 {:ok, _} = User.follow(user, following2)
1737 {:ok, _} = User.follow(user, following3)
1741 |> assign(:user, user)
1745 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1747 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1748 assert id3 == following3.id
1749 assert id2 == following2.id
1753 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1755 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1756 assert id2 == following2.id
1757 assert id1 == following1.id
1761 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1763 assert [%{"id" => id2}] = json_response(res_conn, 200)
1764 assert id2 == following2.id
1766 assert [link_header] = get_resp_header(res_conn, "link")
1767 assert link_header =~ ~r/min_id=#{following2.id}/
1768 assert link_header =~ ~r/max_id=#{following2.id}/
1771 test "following / unfollowing a user", %{conn: conn} do
1772 user = insert(:user)
1773 other_user = insert(:user)
1777 |> assign(:user, user)
1778 |> post("/api/v1/accounts/#{other_user.id}/follow")
1780 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1782 user = User.get_cached_by_id(user.id)
1786 |> assign(:user, user)
1787 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1789 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1791 user = User.get_cached_by_id(user.id)
1795 |> assign(:user, user)
1796 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1798 assert %{"id" => id} = json_response(conn, 200)
1799 assert id == to_string(other_user.id)
1802 test "following without reblogs" do
1803 follower = insert(:user)
1804 followed = insert(:user)
1805 other_user = insert(:user)
1809 |> assign(:user, follower)
1810 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1812 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1814 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1815 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1819 |> assign(:user, User.get_cached_by_id(follower.id))
1820 |> get("/api/v1/timelines/home")
1822 assert [] == json_response(conn, 200)
1826 |> assign(:user, follower)
1827 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1829 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1833 |> assign(:user, User.get_cached_by_id(follower.id))
1834 |> get("/api/v1/timelines/home")
1836 expected_activity_id = reblog.id
1837 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1840 test "following / unfollowing errors" do
1841 user = insert(:user)
1845 |> assign(:user, user)
1848 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1849 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1852 user = User.get_cached_by_id(user.id)
1853 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1854 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1856 # self follow via uri
1857 user = User.get_cached_by_id(user.id)
1858 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1859 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1861 # follow non existing user
1862 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1863 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1865 # follow non existing user via uri
1866 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1867 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1869 # unfollow non existing user
1870 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1871 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1874 describe "mute/unmute" do
1875 test "with notifications", %{conn: conn} do
1876 user = insert(:user)
1877 other_user = insert(:user)
1881 |> assign(:user, user)
1882 |> post("/api/v1/accounts/#{other_user.id}/mute")
1884 response = json_response(conn, 200)
1886 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
1887 user = User.get_cached_by_id(user.id)
1891 |> assign(:user, user)
1892 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1894 response = json_response(conn, 200)
1895 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
1898 test "without notifications", %{conn: conn} do
1899 user = insert(:user)
1900 other_user = insert(:user)
1904 |> assign(:user, user)
1905 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
1907 response = json_response(conn, 200)
1909 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
1910 user = User.get_cached_by_id(user.id)
1914 |> assign(:user, user)
1915 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1917 response = json_response(conn, 200)
1918 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
1922 test "subscribing / unsubscribing to a user", %{conn: conn} do
1923 user = insert(:user)
1924 subscription_target = insert(:user)
1928 |> assign(:user, user)
1929 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1931 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1935 |> assign(:user, user)
1936 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1938 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1941 test "getting a list of mutes", %{conn: conn} do
1942 user = insert(:user)
1943 other_user = insert(:user)
1945 {:ok, user} = User.mute(user, other_user)
1949 |> assign(:user, user)
1950 |> get("/api/v1/mutes")
1952 other_user_id = to_string(other_user.id)
1953 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1956 test "blocking / unblocking a user", %{conn: conn} do
1957 user = insert(:user)
1958 other_user = insert(:user)
1962 |> assign(:user, user)
1963 |> post("/api/v1/accounts/#{other_user.id}/block")
1965 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1967 user = User.get_cached_by_id(user.id)
1971 |> assign(:user, user)
1972 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1974 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1977 test "getting a list of blocks", %{conn: conn} do
1978 user = insert(:user)
1979 other_user = insert(:user)
1981 {:ok, user} = User.block(user, other_user)
1985 |> assign(:user, user)
1986 |> get("/api/v1/blocks")
1988 other_user_id = to_string(other_user.id)
1989 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1992 test "blocking / unblocking a domain", %{conn: conn} do
1993 user = insert(:user)
1994 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1998 |> assign(:user, user)
1999 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2001 assert %{} = json_response(conn, 200)
2002 user = User.get_cached_by_ap_id(user.ap_id)
2003 assert User.blocks?(user, other_user)
2007 |> assign(:user, user)
2008 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2010 assert %{} = json_response(conn, 200)
2011 user = User.get_cached_by_ap_id(user.ap_id)
2012 refute User.blocks?(user, other_user)
2015 test "getting a list of domain blocks", %{conn: conn} do
2016 user = insert(:user)
2018 {:ok, user} = User.block_domain(user, "bad.site")
2019 {:ok, user} = User.block_domain(user, "even.worse.site")
2023 |> assign(:user, user)
2024 |> get("/api/v1/domain_blocks")
2026 domain_blocks = json_response(conn, 200)
2028 assert "bad.site" in domain_blocks
2029 assert "even.worse.site" in domain_blocks
2032 test "unimplemented follow_requests, blocks, domain blocks" do
2033 user = insert(:user)
2035 ["blocks", "domain_blocks", "follow_requests"]
2036 |> Enum.each(fn endpoint ->
2039 |> assign(:user, user)
2040 |> get("/api/v1/#{endpoint}")
2042 assert [] = json_response(conn, 200)
2046 test "returns the favorites of a user", %{conn: conn} do
2047 user = insert(:user)
2048 other_user = insert(:user)
2050 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2051 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2053 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2057 |> assign(:user, user)
2058 |> get("/api/v1/favourites")
2060 assert [status] = json_response(first_conn, 200)
2061 assert status["id"] == to_string(activity.id)
2063 assert [{"link", _link_header}] =
2064 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2066 # Honours query params
2067 {:ok, second_activity} =
2068 CommonAPI.post(other_user, %{
2070 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2073 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2075 last_like = status["id"]
2079 |> assign(:user, user)
2080 |> get("/api/v1/favourites?since_id=#{last_like}")
2082 assert [second_status] = json_response(second_conn, 200)
2083 assert second_status["id"] == to_string(second_activity.id)
2087 |> assign(:user, user)
2088 |> get("/api/v1/favourites?limit=0")
2090 assert [] = json_response(third_conn, 200)
2093 describe "getting favorites timeline of specified user" do
2095 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2096 [current_user: current_user, user: user]
2099 test "returns list of statuses favorited by specified user", %{
2101 current_user: current_user,
2104 [activity | _] = insert_pair(:note_activity)
2105 CommonAPI.favorite(activity.id, user)
2109 |> assign(:user, current_user)
2110 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2111 |> json_response(:ok)
2115 assert length(response) == 1
2116 assert like["id"] == activity.id
2119 test "returns favorites for specified user_id when user is not logged in", %{
2123 activity = insert(:note_activity)
2124 CommonAPI.favorite(activity.id, user)
2128 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2129 |> json_response(:ok)
2131 assert length(response) == 1
2134 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2136 current_user: current_user,
2140 CommonAPI.post(current_user, %{
2141 "status" => "Hi @#{user.nickname}!",
2142 "visibility" => "direct"
2145 CommonAPI.favorite(direct.id, user)
2149 |> assign(:user, current_user)
2150 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2151 |> json_response(:ok)
2153 assert length(response) == 1
2155 anonymous_response =
2157 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2158 |> json_response(:ok)
2160 assert Enum.empty?(anonymous_response)
2163 test "does not return others' favorited DM when user is not one of recipients", %{
2165 current_user: current_user,
2168 user_two = insert(:user)
2171 CommonAPI.post(user_two, %{
2172 "status" => "Hi @#{user.nickname}!",
2173 "visibility" => "direct"
2176 CommonAPI.favorite(direct.id, user)
2180 |> assign(:user, current_user)
2181 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2182 |> json_response(:ok)
2184 assert Enum.empty?(response)
2187 test "paginates favorites using since_id and max_id", %{
2189 current_user: current_user,
2192 activities = insert_list(10, :note_activity)
2194 Enum.each(activities, fn activity ->
2195 CommonAPI.favorite(activity.id, user)
2198 third_activity = Enum.at(activities, 2)
2199 seventh_activity = Enum.at(activities, 6)
2203 |> assign(:user, current_user)
2204 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2205 since_id: third_activity.id,
2206 max_id: seventh_activity.id
2208 |> json_response(:ok)
2210 assert length(response) == 3
2211 refute third_activity in response
2212 refute seventh_activity in response
2215 test "limits favorites using limit parameter", %{
2217 current_user: current_user,
2221 |> insert_list(:note_activity)
2222 |> Enum.each(fn activity ->
2223 CommonAPI.favorite(activity.id, user)
2228 |> assign(:user, current_user)
2229 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2230 |> json_response(:ok)
2232 assert length(response) == 3
2235 test "returns empty response when user does not have any favorited statuses", %{
2237 current_user: current_user,
2242 |> assign(:user, current_user)
2243 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2244 |> json_response(:ok)
2246 assert Enum.empty?(response)
2249 test "returns 404 error when specified user is not exist", %{conn: conn} do
2250 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2252 assert json_response(conn, 404) == %{"error" => "Record not found"}
2255 test "returns 403 error when user has hidden own favorites", %{
2257 current_user: current_user
2259 user = insert(:user, %{info: %{hide_favorites: true}})
2260 activity = insert(:note_activity)
2261 CommonAPI.favorite(activity.id, user)
2265 |> assign(:user, current_user)
2266 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2268 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2271 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2272 user = insert(:user)
2273 activity = insert(:note_activity)
2274 CommonAPI.favorite(activity.id, user)
2278 |> assign(:user, current_user)
2279 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2281 assert user.info.hide_favorites
2282 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2286 test "get instance information", %{conn: conn} do
2287 conn = get(conn, "/api/v1/instance")
2288 assert result = json_response(conn, 200)
2290 email = Config.get([:instance, :email])
2291 # Note: not checking for "max_toot_chars" since it's optional
2297 "email" => from_config_email,
2299 "streaming_api" => _
2304 "registrations" => _,
2308 assert email == from_config_email
2311 test "get instance stats", %{conn: conn} do
2312 user = insert(:user, %{local: true})
2314 user2 = insert(:user, %{local: true})
2315 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2317 insert(:user, %{local: false, nickname: "u@peer1.com"})
2318 insert(:user, %{local: false, nickname: "u@peer2.com"})
2320 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2322 # Stats should count users with missing or nil `info.deactivated` value
2326 |> User.get_cached_by_id()
2327 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
2329 Pleroma.Stats.force_update()
2331 conn = get(conn, "/api/v1/instance")
2333 assert result = json_response(conn, 200)
2335 stats = result["stats"]
2338 assert stats["user_count"] == 1
2339 assert stats["status_count"] == 1
2340 assert stats["domain_count"] == 2
2343 test "get peers", %{conn: conn} do
2344 insert(:user, %{local: false, nickname: "u@peer1.com"})
2345 insert(:user, %{local: false, nickname: "u@peer2.com"})
2347 Pleroma.Stats.force_update()
2349 conn = get(conn, "/api/v1/instance/peers")
2351 assert result = json_response(conn, 200)
2353 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2356 test "put settings", %{conn: conn} do
2357 user = insert(:user)
2361 |> assign(:user, user)
2362 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2364 assert _result = json_response(conn, 200)
2366 user = User.get_cached_by_ap_id(user.ap_id)
2367 assert user.info.settings == %{"programming" => "socks"}
2370 describe "pinned statuses" do
2372 user = insert(:user)
2373 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2375 [user: user, activity: activity]
2378 clear_config([:instance, :max_pinned_statuses]) do
2379 Config.put([:instance, :max_pinned_statuses], 1)
2382 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2383 {:ok, _} = CommonAPI.pin(activity.id, user)
2387 |> assign(:user, user)
2388 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2389 |> json_response(200)
2391 id_str = to_string(activity.id)
2393 assert [%{"id" => ^id_str, "pinned" => true}] = result
2396 test "pin status", %{conn: conn, user: user, activity: activity} do
2397 id_str = to_string(activity.id)
2399 assert %{"id" => ^id_str, "pinned" => true} =
2401 |> assign(:user, user)
2402 |> post("/api/v1/statuses/#{activity.id}/pin")
2403 |> json_response(200)
2405 assert [%{"id" => ^id_str, "pinned" => true}] =
2407 |> assign(:user, user)
2408 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2409 |> json_response(200)
2412 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2413 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2417 |> assign(:user, user)
2418 |> post("/api/v1/statuses/#{dm.id}/pin")
2420 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2423 test "unpin status", %{conn: conn, user: user, activity: activity} do
2424 {:ok, _} = CommonAPI.pin(activity.id, user)
2426 id_str = to_string(activity.id)
2427 user = refresh_record(user)
2429 assert %{"id" => ^id_str, "pinned" => false} =
2431 |> assign(:user, user)
2432 |> post("/api/v1/statuses/#{activity.id}/unpin")
2433 |> json_response(200)
2437 |> assign(:user, user)
2438 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2439 |> json_response(200)
2442 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2445 |> assign(:user, user)
2446 |> post("/api/v1/statuses/1/unpin")
2448 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2451 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2452 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2454 id_str_one = to_string(activity_one.id)
2456 assert %{"id" => ^id_str_one, "pinned" => true} =
2458 |> assign(:user, user)
2459 |> post("/api/v1/statuses/#{id_str_one}/pin")
2460 |> json_response(200)
2462 user = refresh_record(user)
2464 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2466 |> assign(:user, user)
2467 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2468 |> json_response(400)
2474 Config.put([:rich_media, :enabled], true)
2476 user = insert(:user)
2480 test "returns rich-media card", %{conn: conn, user: user} do
2481 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2484 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2485 "provider_name" => "example.com",
2486 "provider_url" => "https://example.com",
2487 "title" => "The Rock",
2489 "url" => "https://example.com/ogp",
2491 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2494 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2495 "title" => "The Rock",
2496 "type" => "video.movie",
2497 "url" => "https://example.com/ogp",
2499 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2506 |> get("/api/v1/statuses/#{activity.id}/card")
2507 |> json_response(200)
2509 assert response == card_data
2511 # works with private posts
2513 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2517 |> assign(:user, user)
2518 |> get("/api/v1/statuses/#{activity.id}/card")
2519 |> json_response(200)
2521 assert response_two == card_data
2524 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2526 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2530 |> get("/api/v1/statuses/#{activity.id}/card")
2531 |> json_response(:ok)
2533 assert response == %{
2535 "title" => "Pleroma",
2536 "description" => "",
2538 "provider_name" => "example.com",
2539 "provider_url" => "https://example.com",
2540 "url" => "https://example.com/ogp-missing-data",
2543 "title" => "Pleroma",
2544 "type" => "website",
2545 "url" => "https://example.com/ogp-missing-data"
2553 user = insert(:user)
2554 for_user = insert(:user)
2557 CommonAPI.post(user, %{
2558 "status" => "heweoo?"
2562 CommonAPI.post(user, %{
2563 "status" => "heweoo!"
2568 |> assign(:user, for_user)
2569 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2571 assert json_response(response1, 200)["bookmarked"] == true
2575 |> assign(:user, for_user)
2576 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2578 assert json_response(response2, 200)["bookmarked"] == true
2582 |> assign(:user, for_user)
2583 |> get("/api/v1/bookmarks")
2585 assert [json_response(response2, 200), json_response(response1, 200)] ==
2586 json_response(bookmarks, 200)
2590 |> assign(:user, for_user)
2591 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2593 assert json_response(response1, 200)["bookmarked"] == false
2597 |> assign(:user, for_user)
2598 |> get("/api/v1/bookmarks")
2600 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2603 describe "conversation muting" do
2605 post_user = insert(:user)
2606 user = insert(:user)
2608 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2610 [user: user, activity: activity]
2613 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2614 id_str = to_string(activity.id)
2616 assert %{"id" => ^id_str, "muted" => true} =
2618 |> assign(:user, user)
2619 |> post("/api/v1/statuses/#{activity.id}/mute")
2620 |> json_response(200)
2623 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2624 {:ok, _} = CommonAPI.add_mute(user, activity)
2628 |> assign(:user, user)
2629 |> post("/api/v1/statuses/#{activity.id}/mute")
2631 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2634 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2635 {:ok, _} = CommonAPI.add_mute(user, activity)
2637 id_str = to_string(activity.id)
2638 user = refresh_record(user)
2640 assert %{"id" => ^id_str, "muted" => false} =
2642 |> assign(:user, user)
2643 |> post("/api/v1/statuses/#{activity.id}/unmute")
2644 |> json_response(200)
2648 describe "reports" do
2650 reporter = insert(:user)
2651 target_user = insert(:user)
2653 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2655 [reporter: reporter, target_user: target_user, activity: activity]
2658 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2659 assert %{"action_taken" => false, "id" => _} =
2661 |> assign(:user, reporter)
2662 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2663 |> json_response(200)
2666 test "submit a report with statuses and comment", %{
2669 target_user: target_user,
2672 assert %{"action_taken" => false, "id" => _} =
2674 |> assign(:user, reporter)
2675 |> post("/api/v1/reports", %{
2676 "account_id" => target_user.id,
2677 "status_ids" => [activity.id],
2678 "comment" => "bad status!",
2679 "forward" => "false"
2681 |> json_response(200)
2684 test "account_id is required", %{
2689 assert %{"error" => "Valid `account_id` required"} =
2691 |> assign(:user, reporter)
2692 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2693 |> json_response(400)
2696 test "comment must be up to the size specified in the config", %{
2699 target_user: target_user
2701 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2702 comment = String.pad_trailing("a", max_size + 1, "a")
2704 error = %{"error" => "Comment must be up to #{max_size} characters"}
2708 |> assign(:user, reporter)
2709 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2710 |> json_response(400)
2713 test "returns error when account is not exist", %{
2720 |> assign(:user, reporter)
2721 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
2723 assert json_response(conn, 400) == %{"error" => "Account not found"}
2727 describe "link headers" do
2728 test "preserves parameters in link headers", %{conn: conn} do
2729 user = insert(:user)
2730 other_user = insert(:user)
2733 CommonAPI.post(other_user, %{
2734 "status" => "hi @#{user.nickname}",
2735 "visibility" => "public"
2739 CommonAPI.post(other_user, %{
2740 "status" => "hi @#{user.nickname}",
2741 "visibility" => "public"
2744 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2745 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2749 |> assign(:user, user)
2750 |> get("/api/v1/notifications", %{media_only: true})
2752 assert [link_header] = get_resp_header(conn, "link")
2753 assert link_header =~ ~r/media_only=true/
2754 assert link_header =~ ~r/min_id=#{notification2.id}/
2755 assert link_header =~ ~r/max_id=#{notification1.id}/
2759 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2760 # Need to set an old-style integer ID to reproduce the problem
2761 # (these are no longer assigned to new accounts but were preserved
2762 # for existing accounts during the migration to flakeIDs)
2763 user_one = insert(:user, %{id: 1212})
2764 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2768 |> get("/api/v1/accounts/#{user_one.id}")
2772 |> get("/api/v1/accounts/#{user_two.nickname}")
2776 |> get("/api/v1/accounts/#{user_two.id}")
2778 acc_one = json_response(resp_one, 200)
2779 acc_two = json_response(resp_two, 200)
2780 acc_three = json_response(resp_three, 200)
2781 refute acc_one == acc_two
2782 assert acc_two == acc_three
2785 describe "custom emoji" do
2786 test "with tags", %{conn: conn} do
2789 |> get("/api/v1/custom_emojis")
2790 |> json_response(200)
2792 assert Map.has_key?(emoji, "shortcode")
2793 assert Map.has_key?(emoji, "static_url")
2794 assert Map.has_key?(emoji, "tags")
2795 assert is_list(emoji["tags"])
2796 assert Map.has_key?(emoji, "category")
2797 assert Map.has_key?(emoji, "url")
2798 assert Map.has_key?(emoji, "visible_in_picker")
2802 describe "index/2 redirections" do
2803 setup %{conn: conn} do
2807 signing_salt: "cooldude"
2812 |> Plug.Session.call(Plug.Session.init(session_opts))
2815 test_path = "/web/statuses/test"
2816 %{conn: conn, path: test_path}
2819 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2820 conn = get(conn, path)
2822 assert conn.status == 302
2823 assert redirected_to(conn) == "/web/login"
2826 test "redirects not logged-in users to the login page on private instances", %{
2830 Config.put([:instance, :public], false)
2832 conn = get(conn, path)
2834 assert conn.status == 302
2835 assert redirected_to(conn) == "/web/login"
2838 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2839 token = insert(:oauth_token)
2843 |> assign(:user, token.user)
2844 |> put_session(:oauth_token, token.token)
2847 assert conn.status == 200
2850 test "saves referer path to session", %{conn: conn, path: path} do
2851 conn = get(conn, path)
2852 return_to = Plug.Conn.get_session(conn, :return_to)
2854 assert return_to == path
2857 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2858 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2859 auth = insert(:oauth_authorization, app: app)
2863 |> put_session(:return_to, path)
2864 |> get("/web/login", %{code: auth.token})
2866 assert conn.status == 302
2867 assert redirected_to(conn) == path
2870 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2871 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2872 auth = insert(:oauth_authorization, app: app)
2874 conn = get(conn, "/web/login", %{code: auth.token})
2876 assert conn.status == 302
2877 assert redirected_to(conn) == "/web/getting-started"
2881 describe "scheduled activities" do
2882 test "creates a scheduled activity", %{conn: conn} do
2883 user = insert(:user)
2884 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2888 |> assign(:user, user)
2889 |> post("/api/v1/statuses", %{
2890 "status" => "scheduled",
2891 "scheduled_at" => scheduled_at
2894 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2895 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2896 assert [] == Repo.all(Activity)
2899 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2900 user = insert(:user)
2901 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2903 file = %Plug.Upload{
2904 content_type: "image/jpg",
2905 path: Path.absname("test/fixtures/image.jpg"),
2906 filename: "an_image.jpg"
2909 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
2913 |> assign(:user, user)
2914 |> post("/api/v1/statuses", %{
2915 "media_ids" => [to_string(upload.id)],
2916 "status" => "scheduled",
2917 "scheduled_at" => scheduled_at
2920 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
2921 assert %{"type" => "image"} = media_attachment
2924 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
2926 user = insert(:user)
2929 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
2933 |> assign(:user, user)
2934 |> post("/api/v1/statuses", %{
2935 "status" => "not scheduled",
2936 "scheduled_at" => scheduled_at
2939 assert %{"content" => "not scheduled"} = json_response(conn, 200)
2940 assert [] == Repo.all(ScheduledActivity)
2943 test "returns error when daily user limit is exceeded", %{conn: conn} do
2944 user = insert(:user)
2947 NaiveDateTime.utc_now()
2948 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2949 |> NaiveDateTime.to_iso8601()
2951 attrs = %{params: %{}, scheduled_at: today}
2952 {:ok, _} = ScheduledActivity.create(user, attrs)
2953 {:ok, _} = ScheduledActivity.create(user, attrs)
2957 |> assign(:user, user)
2958 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
2960 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
2963 test "returns error when total user limit is exceeded", %{conn: conn} do
2964 user = insert(:user)
2967 NaiveDateTime.utc_now()
2968 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2969 |> NaiveDateTime.to_iso8601()
2972 NaiveDateTime.utc_now()
2973 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
2974 |> NaiveDateTime.to_iso8601()
2976 attrs = %{params: %{}, scheduled_at: today}
2977 {:ok, _} = ScheduledActivity.create(user, attrs)
2978 {:ok, _} = ScheduledActivity.create(user, attrs)
2979 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
2983 |> assign(:user, user)
2984 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
2986 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
2989 test "shows scheduled activities", %{conn: conn} do
2990 user = insert(:user)
2991 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
2992 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
2993 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
2994 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
2998 |> assign(:user, user)
3003 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3005 result = json_response(conn_res, 200)
3006 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3011 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3013 result = json_response(conn_res, 200)
3014 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3019 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3021 result = json_response(conn_res, 200)
3022 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3025 test "shows a scheduled activity", %{conn: conn} do
3026 user = insert(:user)
3027 scheduled_activity = insert(:scheduled_activity, user: user)
3031 |> assign(:user, user)
3032 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3034 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3035 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3039 |> assign(:user, user)
3040 |> get("/api/v1/scheduled_statuses/404")
3042 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3045 test "updates a scheduled activity", %{conn: conn} do
3046 user = insert(:user)
3047 scheduled_activity = insert(:scheduled_activity, user: user)
3050 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3054 |> assign(:user, user)
3055 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3056 scheduled_at: new_scheduled_at
3059 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3060 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3064 |> assign(:user, user)
3065 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3067 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3070 test "deletes a scheduled activity", %{conn: conn} do
3071 user = insert(:user)
3072 scheduled_activity = insert(:scheduled_activity, user: user)
3076 |> assign(:user, user)
3077 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3079 assert %{} = json_response(res_conn, 200)
3080 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3084 |> assign(:user, user)
3085 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3087 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3091 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3092 user1 = insert(:user)
3093 user2 = insert(:user)
3094 user3 = insert(:user)
3096 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3098 # Reply to status from another user
3101 |> assign(:user, user2)
3102 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3104 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3106 activity = Activity.get_by_id_with_object(id)
3108 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3109 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3111 # Reblog from the third user
3114 |> assign(:user, user3)
3115 |> post("/api/v1/statuses/#{activity.id}/reblog")
3117 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3118 json_response(conn2, 200)
3120 assert to_string(activity.id) == id
3122 # Getting third user status
3125 |> assign(:user, user3)
3126 |> get("api/v1/timelines/home")
3128 [reblogged_activity] = json_response(conn3, 200)
3130 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3132 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3133 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3136 describe "create account by app" do
3137 test "Account registration via Application", %{conn: conn} do
3140 |> post("/api/v1/apps", %{
3141 client_name: "client_name",
3142 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3143 scopes: "read, write, follow"
3147 "client_id" => client_id,
3148 "client_secret" => client_secret,
3150 "name" => "client_name",
3151 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3154 } = json_response(conn, 200)
3158 |> post("/oauth/token", %{
3159 grant_type: "client_credentials",
3160 client_id: client_id,
3161 client_secret: client_secret
3164 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3165 json_response(conn, 200)
3168 token_from_db = Repo.get_by(Token, token: token)
3169 assert token_from_db
3171 assert scope == "read write follow"
3175 |> put_req_header("authorization", "Bearer " <> token)
3176 |> post("/api/v1/accounts", %{
3178 email: "lain@example.org",
3179 password: "PlzDontHackLain",
3184 "access_token" => token,
3185 "created_at" => _created_at,
3187 "token_type" => "Bearer"
3188 } = json_response(conn, 200)
3190 token_from_db = Repo.get_by(Token, token: token)
3191 assert token_from_db
3192 token_from_db = Repo.preload(token_from_db, :user)
3193 assert token_from_db.user
3195 assert token_from_db.user.info.confirmation_pending
3198 test "rate limit", %{conn: conn} do
3199 app_token = insert(:oauth_token, user: nil)
3202 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3203 |> Map.put(:remote_ip, {15, 15, 15, 15})
3208 |> post("/api/v1/accounts", %{
3209 username: "#{i}lain",
3210 email: "#{i}lain@example.org",
3211 password: "PlzDontHackLain",
3216 "access_token" => token,
3217 "created_at" => _created_at,
3219 "token_type" => "Bearer"
3220 } = json_response(conn, 200)
3222 token_from_db = Repo.get_by(Token, token: token)
3223 assert token_from_db
3224 token_from_db = Repo.preload(token_from_db, :user)
3225 assert token_from_db.user
3227 assert token_from_db.user.info.confirmation_pending
3232 |> post("/api/v1/accounts", %{
3234 email: "6lain@example.org",
3235 password: "PlzDontHackLain",
3239 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3243 describe "GET /api/v1/polls/:id" do
3244 test "returns poll entity for object id", %{conn: conn} do
3245 user = insert(:user)
3248 CommonAPI.post(user, %{
3249 "status" => "Pleroma does",
3250 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3253 object = Object.normalize(activity)
3257 |> assign(:user, user)
3258 |> get("/api/v1/polls/#{object.id}")
3260 response = json_response(conn, 200)
3261 id = to_string(object.id)
3262 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3265 test "does not expose polls for private statuses", %{conn: conn} do
3266 user = insert(:user)
3267 other_user = insert(:user)
3270 CommonAPI.post(user, %{
3271 "status" => "Pleroma does",
3272 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3273 "visibility" => "private"
3276 object = Object.normalize(activity)
3280 |> assign(:user, other_user)
3281 |> get("/api/v1/polls/#{object.id}")
3283 assert json_response(conn, 404)
3287 describe "POST /api/v1/polls/:id/votes" do
3288 test "votes are added to the poll", %{conn: conn} do
3289 user = insert(:user)
3290 other_user = insert(:user)
3293 CommonAPI.post(user, %{
3294 "status" => "A very delicious sandwich",
3296 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3302 object = Object.normalize(activity)
3306 |> assign(:user, other_user)
3307 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3309 assert json_response(conn, 200)
3310 object = Object.get_by_id(object.id)
3312 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3317 test "author can't vote", %{conn: conn} do
3318 user = insert(:user)
3321 CommonAPI.post(user, %{
3322 "status" => "Am I cute?",
3323 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3326 object = Object.normalize(activity)
3329 |> assign(:user, user)
3330 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3331 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3333 object = Object.get_by_id(object.id)
3335 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3338 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3339 user = insert(:user)
3340 other_user = insert(:user)
3343 CommonAPI.post(user, %{
3344 "status" => "The glass is",
3345 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3348 object = Object.normalize(activity)
3351 |> assign(:user, other_user)
3352 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3353 |> json_response(422) == %{"error" => "Too many choices"}
3355 object = Object.get_by_id(object.id)
3357 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3362 test "does not allow choice index to be greater than options count", %{conn: conn} do
3363 user = insert(:user)
3364 other_user = insert(:user)
3367 CommonAPI.post(user, %{
3368 "status" => "Am I cute?",
3369 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3372 object = Object.normalize(activity)
3376 |> assign(:user, other_user)
3377 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3379 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3382 test "returns 404 error when object is not exist", %{conn: conn} do
3383 user = insert(:user)
3387 |> assign(:user, user)
3388 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3390 assert json_response(conn, 404) == %{"error" => "Record not found"}
3393 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3394 user = insert(:user)
3395 other_user = insert(:user)
3398 CommonAPI.post(user, %{
3399 "status" => "Am I cute?",
3400 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3401 "visibility" => "private"
3404 object = Object.normalize(activity)
3408 |> assign(:user, other_user)
3409 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3411 assert json_response(conn, 404) == %{"error" => "Record not found"}
3415 describe "GET /api/v1/statuses/:id/favourited_by" do
3417 user = insert(:user)
3418 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3422 |> assign(:user, user)
3424 [conn: conn, activity: activity, user: user]
3427 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3428 other_user = insert(:user)
3429 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3433 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3434 |> json_response(:ok)
3436 [%{"id" => id}] = response
3438 assert id == other_user.id
3441 test "returns empty array when status has not been favorited yet", %{
3447 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3448 |> json_response(:ok)
3450 assert Enum.empty?(response)
3453 test "does not return users who have favorited the status but are blocked", %{
3454 conn: %{assigns: %{user: user}} = conn,
3457 other_user = insert(:user)
3458 {:ok, user} = User.block(user, other_user)
3460 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3464 |> assign(:user, user)
3465 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3466 |> json_response(:ok)
3468 assert Enum.empty?(response)
3471 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3472 other_user = insert(:user)
3473 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3477 |> assign(:user, nil)
3478 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3479 |> json_response(:ok)
3481 [%{"id" => id}] = response
3482 assert id == other_user.id
3485 test "requires authentification for private posts", %{conn: conn, user: user} do
3486 other_user = insert(:user)
3489 CommonAPI.post(user, %{
3490 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3491 "visibility" => "direct"
3494 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3497 |> assign(:user, nil)
3498 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3499 |> json_response(404)
3503 |> assign(:user, other_user)
3504 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3505 |> json_response(200)
3507 [%{"id" => id}] = response
3508 assert id == other_user.id
3512 describe "GET /api/v1/statuses/:id/reblogged_by" do
3514 user = insert(:user)
3515 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3519 |> assign(:user, user)
3521 [conn: conn, activity: activity, user: user]
3524 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3525 other_user = insert(:user)
3526 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3530 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3531 |> json_response(:ok)
3533 [%{"id" => id}] = response
3535 assert id == other_user.id
3538 test "returns empty array when status has not been reblogged yet", %{
3544 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3545 |> json_response(:ok)
3547 assert Enum.empty?(response)
3550 test "does not return users who have reblogged the status but are blocked", %{
3551 conn: %{assigns: %{user: user}} = conn,
3554 other_user = insert(:user)
3555 {:ok, user} = User.block(user, other_user)
3557 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3561 |> assign(:user, user)
3562 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3563 |> json_response(:ok)
3565 assert Enum.empty?(response)
3568 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3569 other_user = insert(:user)
3570 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3574 |> assign(:user, nil)
3575 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3576 |> json_response(:ok)
3578 [%{"id" => id}] = response
3579 assert id == other_user.id
3582 test "requires authentification for private posts", %{conn: conn, user: user} do
3583 other_user = insert(:user)
3586 CommonAPI.post(user, %{
3587 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3588 "visibility" => "direct"
3592 |> assign(:user, nil)
3593 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3594 |> json_response(404)
3598 |> assign(:user, other_user)
3599 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3600 |> json_response(200)
3602 assert [] == response
3606 describe "POST /auth/password, with valid parameters" do
3607 setup %{conn: conn} do
3608 user = insert(:user)
3609 conn = post(conn, "/auth/password?email=#{user.email}")
3610 %{conn: conn, user: user}
3613 test "it returns 204", %{conn: conn} do
3614 assert json_response(conn, :no_content)
3617 test "it creates a PasswordResetToken record for user", %{user: user} do
3618 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3622 test "it sends an email to user", %{user: user} do
3623 ObanHelpers.perform_all()
3624 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3626 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3627 notify_email = Config.get([:instance, :notify_email])
3628 instance_name = Config.get([:instance, :name])
3631 from: {instance_name, notify_email},
3632 to: {user.name, user.email},
3633 html_body: email.html_body
3638 describe "POST /auth/password, with invalid parameters" do
3640 user = insert(:user)
3644 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3645 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3646 assert conn.status == 404
3647 assert conn.resp_body == ""
3650 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3651 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3652 conn = post(conn, "/auth/password?email=#{user.email}")
3653 assert conn.status == 400
3654 assert conn.resp_body == ""
3658 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3662 |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
3665 assert user.info.confirmation_pending
3670 clear_config([:instance, :account_activation_required]) do
3671 Config.put([:instance, :account_activation_required], true)
3674 test "resend account confirmation email", %{conn: conn, user: user} do
3676 |> assign(:user, user)
3677 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3678 |> json_response(:no_content)
3680 ObanHelpers.perform_all()
3682 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3683 notify_email = Config.get([:instance, :notify_email])
3684 instance_name = Config.get([:instance, :name])
3687 from: {instance_name, notify_email},
3688 to: {user.name, user.email},
3689 html_body: email.html_body
3694 describe "GET /api/v1/suggestions" do
3696 user = insert(:user)
3697 other_user = insert(:user)
3698 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3699 url500 = "http://test500?#{host}&#{user.nickname}"
3700 url200 = "http://test200?#{host}&#{user.nickname}"
3703 %{method: :get, url: ^url500} ->
3704 %Tesla.Env{status: 500, body: "bad request"}
3706 %{method: :get, url: ^url200} ->
3710 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3712 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3716 [user: user, other_user: other_user]
3719 clear_config(:suggestions)
3721 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3722 Config.put([:suggestions, :enabled], false)
3726 |> assign(:user, user)
3727 |> get("/api/v1/suggestions")
3728 |> json_response(200)
3733 test "returns error", %{conn: conn, user: user} do
3734 Config.put([:suggestions, :enabled], true)
3735 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
3737 assert capture_log(fn ->
3740 |> assign(:user, user)
3741 |> get("/api/v1/suggestions")
3742 |> json_response(500)
3744 assert res == "Something went wrong"
3745 end) =~ "Could not retrieve suggestions"
3748 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
3749 Config.put([:suggestions, :enabled], true)
3750 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
3754 |> assign(:user, user)
3755 |> get("/api/v1/suggestions")
3756 |> json_response(200)
3761 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
3762 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
3766 "acct" => other_user.ap_id,
3767 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
3768 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
3769 "id" => other_user.id