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 alias Pleroma.Web.TwitterAPI.TwitterAPI
26 import Pleroma.Factory
27 import ExUnit.CaptureLog
29 import Swoosh.TestAssertions
31 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
34 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
38 clear_config([:instance, :public])
39 clear_config([:rich_media, :enabled])
41 test "the home timeline", %{conn: conn} do
43 following = insert(:user)
45 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
49 |> assign(:user, user)
50 |> get("/api/v1/timelines/home")
52 assert Enum.empty?(json_response(conn, 200))
54 {:ok, user} = User.follow(user, following)
58 |> assign(:user, user)
59 |> get("/api/v1/timelines/home")
61 assert [%{"content" => "test"}] = json_response(conn, 200)
64 test "the public timeline", %{conn: conn} do
65 following = insert(:user)
68 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
71 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
75 |> get("/api/v1/timelines/public", %{"local" => "False"})
77 assert length(json_response(conn, 200)) == 2
81 |> get("/api/v1/timelines/public", %{"local" => "True"})
83 assert [%{"content" => "test"}] = json_response(conn, 200)
87 |> get("/api/v1/timelines/public", %{"local" => "1"})
89 assert [%{"content" => "test"}] = json_response(conn, 200)
93 test "the public timeline when public is set to false", %{conn: conn} do
94 Config.put([:instance, :public], false)
97 |> get("/api/v1/timelines/public", %{"local" => "False"})
98 |> json_response(403) == %{"error" => "This resource requires authentication."}
101 describe "posting statuses" do
107 |> assign(:user, user)
112 test "posting a status", %{conn: conn} do
113 idempotency_key = "Pikachu rocks!"
117 |> put_req_header("idempotency-key", idempotency_key)
118 |> post("/api/v1/statuses", %{
120 "spoiler_text" => "2hu",
121 "sensitive" => "false"
124 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
126 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
128 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
129 json_response(conn_one, 200)
131 assert Activity.get_by_id(id)
135 |> put_req_header("idempotency-key", idempotency_key)
136 |> post("/api/v1/statuses", %{
138 "spoiler_text" => "2hu",
139 "sensitive" => "false"
142 assert %{"id" => second_id} = json_response(conn_two, 200)
143 assert id == second_id
147 |> post("/api/v1/statuses", %{
149 "spoiler_text" => "2hu",
150 "sensitive" => "false"
153 assert %{"id" => third_id} = json_response(conn_three, 200)
154 refute id == third_id
156 # An activity that will expire:
158 expires_in = 120 * 60
162 |> post("api/v1/statuses", %{
163 "status" => "oolong",
164 "expires_in" => expires_in
167 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
168 assert activity = Activity.get_by_id(fourth_id)
169 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
171 estimated_expires_at =
172 NaiveDateTime.utc_now()
173 |> NaiveDateTime.add(expires_in)
174 |> NaiveDateTime.truncate(:second)
176 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
177 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
179 assert fourth_response["pleroma"]["expires_at"] ==
180 NaiveDateTime.to_iso8601(expiration.scheduled_at)
183 test "replying to a status", %{conn: conn} do
185 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
189 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
191 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
193 activity = Activity.get_by_id(id)
195 assert activity.data["context"] == replied_to.data["context"]
196 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
199 test "replying to a direct message with visibility other than direct", %{conn: conn} do
201 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
203 Enum.each(["public", "private", "unlisted"], fn visibility ->
206 |> post("/api/v1/statuses", %{
207 "status" => "@#{user.nickname} hey",
208 "in_reply_to_id" => replied_to.id,
209 "visibility" => visibility
212 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
216 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
219 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
221 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
222 assert Activity.get_by_id(id)
225 test "posting a sensitive status", %{conn: conn} do
228 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
230 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
231 assert Activity.get_by_id(id)
234 test "posting a fake status", %{conn: conn} do
237 |> post("/api/v1/statuses", %{
239 "\"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"
242 real_status = json_response(real_conn, 200)
245 assert Object.get_by_ap_id(real_status["uri"])
249 |> Map.put("id", nil)
250 |> Map.put("url", nil)
251 |> Map.put("uri", nil)
252 |> Map.put("created_at", nil)
253 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
257 |> post("/api/v1/statuses", %{
259 "\"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",
263 fake_status = json_response(fake_conn, 200)
266 refute Object.get_by_ap_id(fake_status["uri"])
270 |> Map.put("id", nil)
271 |> Map.put("url", nil)
272 |> Map.put("uri", nil)
273 |> Map.put("created_at", nil)
274 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
276 assert real_status == fake_status
279 test "posting a status with OGP link preview", %{conn: conn} do
280 Config.put([:rich_media, :enabled], true)
284 |> post("/api/v1/statuses", %{
285 "status" => "https://example.com/ogp"
288 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
289 assert Activity.get_by_id(id)
292 test "posting a direct status", %{conn: conn} do
293 user2 = insert(:user)
294 content = "direct cofe @#{user2.nickname}"
298 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
300 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
301 assert activity = Activity.get_by_id(id)
302 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
303 assert activity.data["to"] == [user2.ap_id]
304 assert activity.data["cc"] == []
308 describe "posting polls" do
309 test "posting a poll", %{conn: conn} do
311 time = NaiveDateTime.utc_now()
315 |> assign(:user, user)
316 |> post("/api/v1/statuses", %{
317 "status" => "Who is the #bestgrill?",
318 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
321 response = json_response(conn, 200)
323 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
324 title in ["Rei", "Asuka", "Misato"]
327 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
328 refute response["poll"]["expred"]
331 test "option limit is enforced", %{conn: conn} do
333 limit = Config.get([:instance, :poll_limits, :max_options])
337 |> assign(:user, user)
338 |> post("/api/v1/statuses", %{
340 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
343 %{"error" => error} = json_response(conn, 422)
344 assert error == "Poll can't contain more than #{limit} options"
347 test "option character limit is enforced", %{conn: conn} do
349 limit = Config.get([:instance, :poll_limits, :max_option_chars])
353 |> assign(:user, user)
354 |> post("/api/v1/statuses", %{
357 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
362 %{"error" => error} = json_response(conn, 422)
363 assert error == "Poll options cannot be longer than #{limit} characters each"
366 test "minimal date limit is enforced", %{conn: conn} do
368 limit = Config.get([:instance, :poll_limits, :min_expiration])
372 |> assign(:user, user)
373 |> post("/api/v1/statuses", %{
374 "status" => "imagine arbitrary limits",
376 "options" => ["this post was made by pleroma gang"],
377 "expires_in" => limit - 1
381 %{"error" => error} = json_response(conn, 422)
382 assert error == "Expiration date is too soon"
385 test "maximum date limit is enforced", %{conn: conn} do
387 limit = Config.get([:instance, :poll_limits, :max_expiration])
391 |> assign(:user, user)
392 |> post("/api/v1/statuses", %{
393 "status" => "imagine arbitrary limits",
395 "options" => ["this post was made by pleroma gang"],
396 "expires_in" => limit + 1
400 %{"error" => error} = json_response(conn, 422)
401 assert error == "Expiration date is too far in the future"
405 test "direct timeline", %{conn: conn} do
406 user_one = insert(:user)
407 user_two = insert(:user)
409 {:ok, user_two} = User.follow(user_two, user_one)
412 CommonAPI.post(user_one, %{
413 "status" => "Hi @#{user_two.nickname}!",
414 "visibility" => "direct"
417 {:ok, _follower_only} =
418 CommonAPI.post(user_one, %{
419 "status" => "Hi @#{user_two.nickname}!",
420 "visibility" => "private"
423 # Only direct should be visible here
426 |> assign(:user, user_two)
427 |> get("api/v1/timelines/direct")
429 [status] = json_response(res_conn, 200)
431 assert %{"visibility" => "direct"} = status
432 assert status["url"] != direct.data["id"]
434 # User should be able to see their own direct message
437 |> assign(:user, user_one)
438 |> get("api/v1/timelines/direct")
440 [status] = json_response(res_conn, 200)
442 assert %{"visibility" => "direct"} = status
444 # Both should be visible here
447 |> assign(:user, user_two)
448 |> get("api/v1/timelines/home")
450 [_s1, _s2] = json_response(res_conn, 200)
453 Enum.each(1..20, fn _ ->
455 CommonAPI.post(user_one, %{
456 "status" => "Hi @#{user_two.nickname}!",
457 "visibility" => "direct"
463 |> assign(:user, user_two)
464 |> get("api/v1/timelines/direct")
466 statuses = json_response(res_conn, 200)
467 assert length(statuses) == 20
471 |> assign(:user, user_two)
472 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
474 [status] = json_response(res_conn, 200)
476 assert status["url"] != direct.data["id"]
479 test "Conversations", %{conn: conn} do
480 user_one = insert(:user)
481 user_two = insert(:user)
482 user_three = insert(:user)
484 {:ok, user_two} = User.follow(user_two, user_one)
487 CommonAPI.post(user_one, %{
488 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
489 "visibility" => "direct"
492 {:ok, _follower_only} =
493 CommonAPI.post(user_one, %{
494 "status" => "Hi @#{user_two.nickname}!",
495 "visibility" => "private"
500 |> assign(:user, user_one)
501 |> get("/api/v1/conversations")
503 assert response = json_response(res_conn, 200)
508 "accounts" => res_accounts,
509 "last_status" => res_last_status,
514 account_ids = Enum.map(res_accounts, & &1["id"])
515 assert length(res_accounts) == 2
516 assert user_two.id in account_ids
517 assert user_three.id in account_ids
518 assert is_binary(res_id)
519 assert unread == true
520 assert res_last_status["id"] == direct.id
522 # Apparently undocumented API endpoint
525 |> assign(:user, user_one)
526 |> post("/api/v1/conversations/#{res_id}/read")
528 assert response = json_response(res_conn, 200)
529 assert length(response["accounts"]) == 2
530 assert response["last_status"]["id"] == direct.id
531 assert response["unread"] == false
533 # (vanilla) Mastodon frontend behaviour
536 |> assign(:user, user_one)
537 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
539 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
542 test "doesn't include DMs from blocked users", %{conn: conn} do
543 blocker = insert(:user)
544 blocked = insert(:user)
546 {:ok, blocker} = User.block(blocker, blocked)
548 {:ok, _blocked_direct} =
549 CommonAPI.post(blocked, %{
550 "status" => "Hi @#{blocker.nickname}!",
551 "visibility" => "direct"
555 CommonAPI.post(user, %{
556 "status" => "Hi @#{blocker.nickname}!",
557 "visibility" => "direct"
562 |> assign(:user, user)
563 |> get("api/v1/timelines/direct")
565 [status] = json_response(res_conn, 200)
566 assert status["id"] == direct.id
569 test "verify_credentials", %{conn: conn} do
574 |> assign(:user, user)
575 |> get("/api/v1/accounts/verify_credentials")
577 response = json_response(conn, 200)
579 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
580 assert response["pleroma"]["chat_token"]
581 assert id == to_string(user.id)
584 test "verify_credentials default scope unlisted", %{conn: conn} do
585 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
589 |> assign(:user, user)
590 |> get("/api/v1/accounts/verify_credentials")
592 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
593 assert id == to_string(user.id)
596 test "apps/verify_credentials", %{conn: conn} do
597 token = insert(:oauth_token)
601 |> assign(:user, token.user)
602 |> assign(:token, token)
603 |> get("/api/v1/apps/verify_credentials")
605 app = Repo.preload(token, :app).app
608 "name" => app.client_name,
609 "website" => app.website,
610 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
613 assert expected == json_response(conn, 200)
616 test "user avatar can be set", %{conn: conn} do
618 avatar_image = File.read!("test/fixtures/avatar_data_uri")
622 |> assign(:user, user)
623 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
625 user = refresh_record(user)
639 assert %{"url" => _} = json_response(conn, 200)
642 test "user avatar can be reset", %{conn: conn} do
647 |> assign(:user, user)
648 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
650 user = User.get_cached_by_id(user.id)
652 assert user.avatar == nil
654 assert %{"url" => nil} = json_response(conn, 200)
657 test "can set profile banner", %{conn: conn} do
662 |> assign(:user, user)
663 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
665 user = refresh_record(user)
666 assert user.info.banner["type"] == "Image"
668 assert %{"url" => _} = json_response(conn, 200)
671 test "can reset profile banner", %{conn: conn} do
676 |> assign(:user, user)
677 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
679 user = refresh_record(user)
680 assert user.info.banner == %{}
682 assert %{"url" => nil} = json_response(conn, 200)
685 test "background image can be set", %{conn: conn} do
690 |> assign(:user, user)
691 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
693 user = refresh_record(user)
694 assert user.info.background["type"] == "Image"
695 assert %{"url" => _} = json_response(conn, 200)
698 test "background image can be reset", %{conn: conn} do
703 |> assign(:user, user)
704 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
706 user = refresh_record(user)
707 assert user.info.background == %{}
708 assert %{"url" => nil} = json_response(conn, 200)
711 test "creates an oauth app", %{conn: conn} do
713 app_attrs = build(:oauth_app)
717 |> assign(:user, user)
718 |> post("/api/v1/apps", %{
719 client_name: app_attrs.client_name,
720 redirect_uris: app_attrs.redirect_uris
723 [app] = Repo.all(App)
726 "name" => app.client_name,
727 "website" => app.website,
728 "client_id" => app.client_id,
729 "client_secret" => app.client_secret,
730 "id" => app.id |> to_string(),
731 "redirect_uri" => app.redirect_uris,
732 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
735 assert expected == json_response(conn, 200)
738 test "get a status", %{conn: conn} do
739 activity = insert(:note_activity)
743 |> get("/api/v1/statuses/#{activity.id}")
745 assert %{"id" => id} = json_response(conn, 200)
746 assert id == to_string(activity.id)
749 describe "deleting a status" do
750 test "when you created it", %{conn: conn} do
751 activity = insert(:note_activity)
752 author = User.get_cached_by_ap_id(activity.data["actor"])
756 |> assign(:user, author)
757 |> delete("/api/v1/statuses/#{activity.id}")
759 assert %{} = json_response(conn, 200)
761 refute Activity.get_by_id(activity.id)
764 test "when you didn't create it", %{conn: conn} do
765 activity = insert(:note_activity)
770 |> assign(:user, user)
771 |> delete("/api/v1/statuses/#{activity.id}")
773 assert %{"error" => _} = json_response(conn, 403)
775 assert Activity.get_by_id(activity.id) == activity
778 test "when you're an admin or moderator", %{conn: conn} do
779 activity1 = insert(:note_activity)
780 activity2 = insert(:note_activity)
781 admin = insert(:user, info: %{is_admin: true})
782 moderator = insert(:user, info: %{is_moderator: true})
786 |> assign(:user, admin)
787 |> delete("/api/v1/statuses/#{activity1.id}")
789 assert %{} = json_response(res_conn, 200)
793 |> assign(:user, moderator)
794 |> delete("/api/v1/statuses/#{activity2.id}")
796 assert %{} = json_response(res_conn, 200)
798 refute Activity.get_by_id(activity1.id)
799 refute Activity.get_by_id(activity2.id)
803 describe "filters" do
804 test "creating a filter", %{conn: conn} do
807 filter = %Pleroma.Filter{
814 |> assign(:user, user)
815 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
817 assert response = json_response(conn, 200)
818 assert response["phrase"] == filter.phrase
819 assert response["context"] == filter.context
820 assert response["irreversible"] == false
821 assert response["id"] != nil
822 assert response["id"] != ""
825 test "fetching a list of filters", %{conn: conn} do
828 query_one = %Pleroma.Filter{
835 query_two = %Pleroma.Filter{
842 {:ok, filter_one} = Pleroma.Filter.create(query_one)
843 {:ok, filter_two} = Pleroma.Filter.create(query_two)
847 |> assign(:user, user)
848 |> get("/api/v1/filters")
849 |> json_response(200)
855 filters: [filter_two, filter_one]
859 test "get a filter", %{conn: conn} do
862 query = %Pleroma.Filter{
869 {:ok, filter} = Pleroma.Filter.create(query)
873 |> assign(:user, user)
874 |> get("/api/v1/filters/#{filter.filter_id}")
876 assert _response = json_response(conn, 200)
879 test "update a filter", %{conn: conn} do
882 query = %Pleroma.Filter{
889 {:ok, _filter} = Pleroma.Filter.create(query)
891 new = %Pleroma.Filter{
898 |> assign(:user, user)
899 |> put("/api/v1/filters/#{query.filter_id}", %{
904 assert response = json_response(conn, 200)
905 assert response["phrase"] == new.phrase
906 assert response["context"] == new.context
909 test "delete a filter", %{conn: conn} do
912 query = %Pleroma.Filter{
919 {:ok, filter} = Pleroma.Filter.create(query)
923 |> assign(:user, user)
924 |> delete("/api/v1/filters/#{filter.filter_id}")
926 assert response = json_response(conn, 200)
927 assert response == %{}
931 describe "list timelines" do
932 test "list timeline", %{conn: conn} do
934 other_user = insert(:user)
935 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
936 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
937 {:ok, list} = Pleroma.List.create("name", user)
938 {:ok, list} = Pleroma.List.follow(list, other_user)
942 |> assign(:user, user)
943 |> get("/api/v1/timelines/list/#{list.id}")
945 assert [%{"id" => id}] = json_response(conn, 200)
947 assert id == to_string(activity_two.id)
950 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
952 other_user = insert(:user)
953 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
955 {:ok, _activity_two} =
956 CommonAPI.post(other_user, %{
957 "status" => "Marisa is cute.",
958 "visibility" => "private"
961 {:ok, list} = Pleroma.List.create("name", user)
962 {:ok, list} = Pleroma.List.follow(list, other_user)
966 |> assign(:user, user)
967 |> get("/api/v1/timelines/list/#{list.id}")
969 assert [%{"id" => id}] = json_response(conn, 200)
971 assert id == to_string(activity_one.id)
975 describe "notifications" do
976 test "list of notifications", %{conn: conn} do
978 other_user = insert(:user)
980 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
982 {:ok, [_notification]} = Notification.create_notifications(activity)
986 |> assign(:user, user)
987 |> get("/api/v1/notifications")
990 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
992 }\">@<span>#{user.nickname}</span></a></span>"
994 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
995 assert response == expected_response
998 test "getting a single notification", %{conn: conn} do
1000 other_user = insert(:user)
1002 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1004 {:ok, [notification]} = Notification.create_notifications(activity)
1008 |> assign(:user, user)
1009 |> get("/api/v1/notifications/#{notification.id}")
1012 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1014 }\">@<span>#{user.nickname}</span></a></span>"
1016 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1017 assert response == expected_response
1020 test "dismissing a single notification", %{conn: conn} do
1021 user = insert(:user)
1022 other_user = insert(:user)
1024 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1026 {:ok, [notification]} = Notification.create_notifications(activity)
1030 |> assign(:user, user)
1031 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1033 assert %{} = json_response(conn, 200)
1036 test "clearing all notifications", %{conn: conn} do
1037 user = insert(:user)
1038 other_user = insert(:user)
1040 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1042 {:ok, [_notification]} = Notification.create_notifications(activity)
1046 |> assign(:user, user)
1047 |> post("/api/v1/notifications/clear")
1049 assert %{} = json_response(conn, 200)
1053 |> assign(:user, user)
1054 |> get("/api/v1/notifications")
1056 assert all = json_response(conn, 200)
1060 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1061 user = insert(:user)
1062 other_user = insert(:user)
1064 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1065 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1066 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1067 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1069 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1070 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1071 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1072 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1076 |> assign(:user, user)
1081 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1083 result = json_response(conn_res, 200)
1084 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1089 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1091 result = json_response(conn_res, 200)
1092 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1097 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1099 result = json_response(conn_res, 200)
1100 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1103 test "filters notifications using exclude_types", %{conn: conn} do
1104 user = insert(:user)
1105 other_user = insert(:user)
1107 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1108 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1109 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1110 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1111 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1113 mention_notification_id =
1114 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1116 favorite_notification_id =
1117 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1119 reblog_notification_id =
1120 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1122 follow_notification_id =
1123 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1127 |> assign(:user, user)
1130 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1132 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1135 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1137 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1140 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1142 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1145 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1147 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1150 test "destroy multiple", %{conn: conn} do
1151 user = insert(:user)
1152 other_user = insert(:user)
1154 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1155 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1156 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1157 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1159 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1160 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1161 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1162 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1166 |> assign(:user, user)
1170 |> get("/api/v1/notifications")
1172 result = json_response(conn_res, 200)
1173 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1177 |> assign(:user, other_user)
1181 |> get("/api/v1/notifications")
1183 result = json_response(conn_res, 200)
1184 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1188 |> delete("/api/v1/notifications/destroy_multiple", %{
1189 "ids" => [notification1_id, notification2_id]
1192 assert json_response(conn_destroy, 200) == %{}
1196 |> get("/api/v1/notifications")
1198 result = json_response(conn_res, 200)
1199 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1202 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1203 user = insert(:user)
1204 user2 = insert(:user)
1206 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1207 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1209 conn = assign(conn, :user, user)
1211 conn = get(conn, "/api/v1/notifications")
1213 assert length(json_response(conn, 200)) == 1
1215 {:ok, user} = User.mute(user, user2)
1217 conn = assign(build_conn(), :user, user)
1218 conn = get(conn, "/api/v1/notifications")
1220 assert json_response(conn, 200) == []
1223 test "see notifications after muting user without notifications", %{conn: conn} do
1224 user = insert(:user)
1225 user2 = insert(:user)
1227 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1228 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1230 conn = assign(conn, :user, user)
1232 conn = get(conn, "/api/v1/notifications")
1234 assert length(json_response(conn, 200)) == 1
1236 {:ok, user} = User.mute(user, user2, false)
1238 conn = assign(build_conn(), :user, user)
1239 conn = get(conn, "/api/v1/notifications")
1241 assert length(json_response(conn, 200)) == 1
1244 test "see notifications after muting user with notifications and with_muted parameter", %{
1247 user = insert(:user)
1248 user2 = insert(:user)
1250 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1251 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1253 conn = assign(conn, :user, user)
1255 conn = get(conn, "/api/v1/notifications")
1257 assert length(json_response(conn, 200)) == 1
1259 {:ok, user} = User.mute(user, user2)
1261 conn = assign(build_conn(), :user, user)
1262 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1264 assert length(json_response(conn, 200)) == 1
1268 describe "reblogging" do
1269 test "reblogs and returns the reblogged status", %{conn: conn} do
1270 activity = insert(:note_activity)
1271 user = insert(:user)
1275 |> assign(:user, user)
1276 |> post("/api/v1/statuses/#{activity.id}/reblog")
1279 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1281 } = json_response(conn, 200)
1283 assert to_string(activity.id) == id
1286 test "reblogged status for another user", %{conn: conn} do
1287 activity = insert(:note_activity)
1288 user1 = insert(:user)
1289 user2 = insert(:user)
1290 user3 = insert(:user)
1291 CommonAPI.favorite(activity.id, user2)
1292 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1293 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1294 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1298 |> assign(:user, user3)
1299 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1302 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1303 "reblogged" => false,
1304 "favourited" => false,
1305 "bookmarked" => false
1306 } = json_response(conn_res, 200)
1310 |> assign(:user, user2)
1311 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1314 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1315 "reblogged" => true,
1316 "favourited" => true,
1317 "bookmarked" => true
1318 } = json_response(conn_res, 200)
1320 assert to_string(activity.id) == id
1323 test "returns 400 error when activity is not exist", %{conn: conn} do
1324 user = insert(:user)
1328 |> assign(:user, user)
1329 |> post("/api/v1/statuses/foo/reblog")
1331 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1335 describe "unreblogging" do
1336 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1337 activity = insert(:note_activity)
1338 user = insert(:user)
1340 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1344 |> assign(:user, user)
1345 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1347 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1349 assert to_string(activity.id) == id
1352 test "returns 400 error when activity is not exist", %{conn: conn} do
1353 user = insert(:user)
1357 |> assign(:user, user)
1358 |> post("/api/v1/statuses/foo/unreblog")
1360 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1364 describe "favoriting" do
1365 test "favs a status and returns it", %{conn: conn} do
1366 activity = insert(:note_activity)
1367 user = insert(:user)
1371 |> assign(:user, user)
1372 |> post("/api/v1/statuses/#{activity.id}/favourite")
1374 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1375 json_response(conn, 200)
1377 assert to_string(activity.id) == id
1380 test "returns 400 error for a wrong id", %{conn: conn} do
1381 user = insert(:user)
1385 |> assign(:user, user)
1386 |> post("/api/v1/statuses/1/favourite")
1388 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1392 describe "unfavoriting" do
1393 test "unfavorites a status and returns it", %{conn: conn} do
1394 activity = insert(:note_activity)
1395 user = insert(:user)
1397 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1401 |> assign(:user, user)
1402 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1404 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1405 json_response(conn, 200)
1407 assert to_string(activity.id) == id
1410 test "returns 400 error for a wrong id", %{conn: conn} do
1411 user = insert(:user)
1415 |> assign(:user, user)
1416 |> post("/api/v1/statuses/1/unfavourite")
1418 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1422 describe "user timelines" do
1423 test "gets a users statuses", %{conn: conn} do
1424 user_one = insert(:user)
1425 user_two = insert(:user)
1426 user_three = insert(:user)
1428 {:ok, user_three} = User.follow(user_three, user_one)
1430 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1432 {:ok, direct_activity} =
1433 CommonAPI.post(user_one, %{
1434 "status" => "Hi, @#{user_two.nickname}.",
1435 "visibility" => "direct"
1438 {:ok, private_activity} =
1439 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1443 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1445 assert [%{"id" => id}] = json_response(resp, 200)
1446 assert id == to_string(activity.id)
1450 |> assign(:user, user_two)
1451 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1453 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1454 assert id_one == to_string(direct_activity.id)
1455 assert id_two == to_string(activity.id)
1459 |> assign(:user, user_three)
1460 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1462 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1463 assert id_one == to_string(private_activity.id)
1464 assert id_two == to_string(activity.id)
1467 test "unimplemented pinned statuses feature", %{conn: conn} do
1468 note = insert(:note_activity)
1469 user = User.get_cached_by_ap_id(note.data["actor"])
1473 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1475 assert json_response(conn, 200) == []
1478 test "gets an users media", %{conn: conn} do
1479 note = insert(:note_activity)
1480 user = User.get_cached_by_ap_id(note.data["actor"])
1482 file = %Plug.Upload{
1483 content_type: "image/jpg",
1484 path: Path.absname("test/fixtures/image.jpg"),
1485 filename: "an_image.jpg"
1489 TwitterAPI.upload(file, user, "json")
1493 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1497 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1499 assert [%{"id" => id}] = json_response(conn, 200)
1500 assert id == to_string(image_post.id)
1504 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1506 assert [%{"id" => id}] = json_response(conn, 200)
1507 assert id == to_string(image_post.id)
1510 test "gets a user's statuses without reblogs", %{conn: conn} do
1511 user = insert(:user)
1512 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1513 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1517 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1519 assert [%{"id" => id}] = json_response(conn, 200)
1520 assert id == to_string(post.id)
1524 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1526 assert [%{"id" => id}] = json_response(conn, 200)
1527 assert id == to_string(post.id)
1530 test "filters user's statuses by a hashtag", %{conn: conn} do
1531 user = insert(:user)
1532 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1533 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1537 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1539 assert [%{"id" => id}] = json_response(conn, 200)
1540 assert id == to_string(post.id)
1544 describe "user relationships" do
1545 test "returns the relationships for the current user", %{conn: conn} do
1546 user = insert(:user)
1547 other_user = insert(:user)
1548 {:ok, user} = User.follow(user, other_user)
1552 |> assign(:user, user)
1553 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1555 assert [relationship] = json_response(conn, 200)
1557 assert to_string(other_user.id) == relationship["id"]
1561 describe "media upload" do
1563 user = insert(:user)
1567 |> assign(:user, user)
1569 image = %Plug.Upload{
1570 content_type: "image/jpg",
1571 path: Path.absname("test/fixtures/image.jpg"),
1572 filename: "an_image.jpg"
1575 [conn: conn, image: image]
1578 clear_config([:media_proxy])
1579 clear_config([Pleroma.Upload])
1581 test "returns uploaded image", %{conn: conn, image: image} do
1582 desc = "Description of the image"
1586 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1587 |> json_response(:ok)
1589 assert media["type"] == "image"
1590 assert media["description"] == desc
1593 object = Repo.get(Object, media["id"])
1594 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1598 describe "locked accounts" do
1599 test "/api/v1/follow_requests works" do
1600 user = insert(:user, %{info: %User.Info{locked: true}})
1601 other_user = insert(:user)
1603 {:ok, _activity} = ActivityPub.follow(other_user, user)
1605 user = User.get_cached_by_id(user.id)
1606 other_user = User.get_cached_by_id(other_user.id)
1608 assert User.following?(other_user, user) == false
1612 |> assign(:user, user)
1613 |> get("/api/v1/follow_requests")
1615 assert [relationship] = json_response(conn, 200)
1616 assert to_string(other_user.id) == relationship["id"]
1619 test "/api/v1/follow_requests/:id/authorize works" do
1620 user = insert(:user, %{info: %User.Info{locked: true}})
1621 other_user = insert(:user)
1623 {:ok, _activity} = ActivityPub.follow(other_user, user)
1625 user = User.get_cached_by_id(user.id)
1626 other_user = User.get_cached_by_id(other_user.id)
1628 assert User.following?(other_user, user) == false
1632 |> assign(:user, user)
1633 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1635 assert relationship = json_response(conn, 200)
1636 assert to_string(other_user.id) == relationship["id"]
1638 user = User.get_cached_by_id(user.id)
1639 other_user = User.get_cached_by_id(other_user.id)
1641 assert User.following?(other_user, user) == true
1644 test "verify_credentials", %{conn: conn} do
1645 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1649 |> assign(:user, user)
1650 |> get("/api/v1/accounts/verify_credentials")
1652 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1653 assert id == to_string(user.id)
1656 test "/api/v1/follow_requests/:id/reject works" do
1657 user = insert(:user, %{info: %User.Info{locked: true}})
1658 other_user = insert(:user)
1660 {:ok, _activity} = ActivityPub.follow(other_user, user)
1662 user = User.get_cached_by_id(user.id)
1666 |> assign(:user, user)
1667 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1669 assert relationship = json_response(conn, 200)
1670 assert to_string(other_user.id) == relationship["id"]
1672 user = User.get_cached_by_id(user.id)
1673 other_user = User.get_cached_by_id(other_user.id)
1675 assert User.following?(other_user, user) == false
1679 test "account fetching", %{conn: conn} do
1680 user = insert(:user)
1684 |> get("/api/v1/accounts/#{user.id}")
1686 assert %{"id" => id} = json_response(conn, 200)
1687 assert id == to_string(user.id)
1691 |> get("/api/v1/accounts/-1")
1693 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1696 test "account fetching also works nickname", %{conn: conn} do
1697 user = insert(:user)
1701 |> get("/api/v1/accounts/#{user.nickname}")
1703 assert %{"id" => id} = json_response(conn, 200)
1704 assert id == user.id
1707 test "mascot upload", %{conn: conn} do
1708 user = insert(:user)
1710 non_image_file = %Plug.Upload{
1711 content_type: "audio/mpeg",
1712 path: Path.absname("test/fixtures/sound.mp3"),
1713 filename: "sound.mp3"
1718 |> assign(:user, user)
1719 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1721 assert json_response(conn, 415)
1723 file = %Plug.Upload{
1724 content_type: "image/jpg",
1725 path: Path.absname("test/fixtures/image.jpg"),
1726 filename: "an_image.jpg"
1731 |> assign(:user, user)
1732 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1734 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1737 test "mascot retrieving", %{conn: conn} do
1738 user = insert(:user)
1739 # When user hasn't set a mascot, we should just get pleroma tan back
1742 |> assign(:user, user)
1743 |> get("/api/v1/pleroma/mascot")
1745 assert %{"url" => url} = json_response(conn, 200)
1746 assert url =~ "pleroma-fox-tan-smol"
1748 # When a user sets their mascot, we should get that back
1749 file = %Plug.Upload{
1750 content_type: "image/jpg",
1751 path: Path.absname("test/fixtures/image.jpg"),
1752 filename: "an_image.jpg"
1757 |> assign(:user, user)
1758 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1760 assert json_response(conn, 200)
1762 user = User.get_cached_by_id(user.id)
1766 |> assign(:user, user)
1767 |> get("/api/v1/pleroma/mascot")
1769 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1770 assert url =~ "an_image"
1773 test "hashtag timeline", %{conn: conn} do
1774 following = insert(:user)
1777 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1779 {:ok, [_activity]} =
1780 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1784 |> get("/api/v1/timelines/tag/2hu")
1786 assert [%{"id" => id}] = json_response(nconn, 200)
1788 assert id == to_string(activity.id)
1790 # works for different capitalization too
1793 |> get("/api/v1/timelines/tag/2HU")
1795 assert [%{"id" => id}] = json_response(nconn, 200)
1797 assert id == to_string(activity.id)
1801 test "multi-hashtag timeline", %{conn: conn} do
1802 user = insert(:user)
1804 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1805 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1806 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1810 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1812 [status_none, status_test1, status_test] = json_response(any_test, 200)
1814 assert to_string(activity_test.id) == status_test["id"]
1815 assert to_string(activity_test1.id) == status_test1["id"]
1816 assert to_string(activity_none.id) == status_none["id"]
1820 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1822 assert [status_test1] == json_response(restricted_test, 200)
1824 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1826 assert [status_none] == json_response(all_test, 200)
1829 test "getting followers", %{conn: conn} do
1830 user = insert(:user)
1831 other_user = insert(:user)
1832 {:ok, user} = User.follow(user, other_user)
1836 |> get("/api/v1/accounts/#{other_user.id}/followers")
1838 assert [%{"id" => id}] = json_response(conn, 200)
1839 assert id == to_string(user.id)
1842 test "getting followers, hide_followers", %{conn: conn} do
1843 user = insert(:user)
1844 other_user = insert(:user, %{info: %{hide_followers: true}})
1845 {:ok, _user} = User.follow(user, other_user)
1849 |> get("/api/v1/accounts/#{other_user.id}/followers")
1851 assert [] == json_response(conn, 200)
1854 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1855 user = insert(:user)
1856 other_user = insert(:user, %{info: %{hide_followers: true}})
1857 {:ok, _user} = User.follow(user, other_user)
1861 |> assign(:user, other_user)
1862 |> get("/api/v1/accounts/#{other_user.id}/followers")
1864 refute [] == json_response(conn, 200)
1867 test "getting followers, pagination", %{conn: conn} do
1868 user = insert(:user)
1869 follower1 = insert(:user)
1870 follower2 = insert(:user)
1871 follower3 = insert(:user)
1872 {:ok, _} = User.follow(follower1, user)
1873 {:ok, _} = User.follow(follower2, user)
1874 {:ok, _} = User.follow(follower3, user)
1878 |> assign(:user, user)
1882 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1884 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1885 assert id3 == follower3.id
1886 assert id2 == follower2.id
1890 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1892 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1893 assert id2 == follower2.id
1894 assert id1 == follower1.id
1898 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1900 assert [%{"id" => id2}] = json_response(res_conn, 200)
1901 assert id2 == follower2.id
1903 assert [link_header] = get_resp_header(res_conn, "link")
1904 assert link_header =~ ~r/min_id=#{follower2.id}/
1905 assert link_header =~ ~r/max_id=#{follower2.id}/
1908 test "getting following", %{conn: conn} do
1909 user = insert(:user)
1910 other_user = insert(:user)
1911 {:ok, user} = User.follow(user, other_user)
1915 |> get("/api/v1/accounts/#{user.id}/following")
1917 assert [%{"id" => id}] = json_response(conn, 200)
1918 assert id == to_string(other_user.id)
1921 test "getting following, hide_follows", %{conn: conn} do
1922 user = insert(:user, %{info: %{hide_follows: true}})
1923 other_user = insert(:user)
1924 {:ok, user} = User.follow(user, other_user)
1928 |> get("/api/v1/accounts/#{user.id}/following")
1930 assert [] == json_response(conn, 200)
1933 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1934 user = insert(:user, %{info: %{hide_follows: true}})
1935 other_user = insert(:user)
1936 {:ok, user} = User.follow(user, other_user)
1940 |> assign(:user, user)
1941 |> get("/api/v1/accounts/#{user.id}/following")
1943 refute [] == json_response(conn, 200)
1946 test "getting following, pagination", %{conn: conn} do
1947 user = insert(:user)
1948 following1 = insert(:user)
1949 following2 = insert(:user)
1950 following3 = insert(:user)
1951 {:ok, _} = User.follow(user, following1)
1952 {:ok, _} = User.follow(user, following2)
1953 {:ok, _} = User.follow(user, following3)
1957 |> assign(:user, user)
1961 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1963 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1964 assert id3 == following3.id
1965 assert id2 == following2.id
1969 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1971 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1972 assert id2 == following2.id
1973 assert id1 == following1.id
1977 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1979 assert [%{"id" => id2}] = json_response(res_conn, 200)
1980 assert id2 == following2.id
1982 assert [link_header] = get_resp_header(res_conn, "link")
1983 assert link_header =~ ~r/min_id=#{following2.id}/
1984 assert link_header =~ ~r/max_id=#{following2.id}/
1987 test "following / unfollowing a user", %{conn: conn} do
1988 user = insert(:user)
1989 other_user = insert(:user)
1993 |> assign(:user, user)
1994 |> post("/api/v1/accounts/#{other_user.id}/follow")
1996 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1998 user = User.get_cached_by_id(user.id)
2002 |> assign(:user, user)
2003 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2005 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2007 user = User.get_cached_by_id(user.id)
2011 |> assign(:user, user)
2012 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2014 assert %{"id" => id} = json_response(conn, 200)
2015 assert id == to_string(other_user.id)
2018 test "following without reblogs" do
2019 follower = insert(:user)
2020 followed = insert(:user)
2021 other_user = insert(:user)
2025 |> assign(:user, follower)
2026 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2028 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2030 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2031 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2035 |> assign(:user, User.get_cached_by_id(follower.id))
2036 |> get("/api/v1/timelines/home")
2038 assert [] == json_response(conn, 200)
2042 |> assign(:user, follower)
2043 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2045 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2049 |> assign(:user, User.get_cached_by_id(follower.id))
2050 |> get("/api/v1/timelines/home")
2052 expected_activity_id = reblog.id
2053 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2056 test "following / unfollowing errors" do
2057 user = insert(:user)
2061 |> assign(:user, user)
2064 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2065 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2068 user = User.get_cached_by_id(user.id)
2069 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2070 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2072 # self follow via uri
2073 user = User.get_cached_by_id(user.id)
2074 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2075 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2077 # follow non existing user
2078 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2079 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2081 # follow non existing user via uri
2082 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2083 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2085 # unfollow non existing user
2086 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2087 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2090 describe "mute/unmute" do
2091 test "with notifications", %{conn: conn} do
2092 user = insert(:user)
2093 other_user = insert(:user)
2097 |> assign(:user, user)
2098 |> post("/api/v1/accounts/#{other_user.id}/mute")
2100 response = json_response(conn, 200)
2102 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2103 user = User.get_cached_by_id(user.id)
2107 |> assign(:user, user)
2108 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2110 response = json_response(conn, 200)
2111 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2114 test "without notifications", %{conn: conn} do
2115 user = insert(:user)
2116 other_user = insert(:user)
2120 |> assign(:user, user)
2121 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2123 response = json_response(conn, 200)
2125 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2126 user = User.get_cached_by_id(user.id)
2130 |> assign(:user, user)
2131 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2133 response = json_response(conn, 200)
2134 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2138 test "subscribing / unsubscribing to a user", %{conn: conn} do
2139 user = insert(:user)
2140 subscription_target = insert(:user)
2144 |> assign(:user, user)
2145 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2147 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2151 |> assign(:user, user)
2152 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2154 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2157 test "getting a list of mutes", %{conn: conn} do
2158 user = insert(:user)
2159 other_user = insert(:user)
2161 {:ok, user} = User.mute(user, other_user)
2165 |> assign(:user, user)
2166 |> get("/api/v1/mutes")
2168 other_user_id = to_string(other_user.id)
2169 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2172 test "blocking / unblocking a user", %{conn: conn} do
2173 user = insert(:user)
2174 other_user = insert(:user)
2178 |> assign(:user, user)
2179 |> post("/api/v1/accounts/#{other_user.id}/block")
2181 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2183 user = User.get_cached_by_id(user.id)
2187 |> assign(:user, user)
2188 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2190 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2193 test "getting a list of blocks", %{conn: conn} do
2194 user = insert(:user)
2195 other_user = insert(:user)
2197 {:ok, user} = User.block(user, other_user)
2201 |> assign(:user, user)
2202 |> get("/api/v1/blocks")
2204 other_user_id = to_string(other_user.id)
2205 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2208 test "blocking / unblocking a domain", %{conn: conn} do
2209 user = insert(:user)
2210 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2214 |> assign(:user, user)
2215 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2217 assert %{} = json_response(conn, 200)
2218 user = User.get_cached_by_ap_id(user.ap_id)
2219 assert User.blocks?(user, other_user)
2223 |> assign(:user, user)
2224 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2226 assert %{} = json_response(conn, 200)
2227 user = User.get_cached_by_ap_id(user.ap_id)
2228 refute User.blocks?(user, other_user)
2231 test "getting a list of domain blocks", %{conn: conn} do
2232 user = insert(:user)
2234 {:ok, user} = User.block_domain(user, "bad.site")
2235 {:ok, user} = User.block_domain(user, "even.worse.site")
2239 |> assign(:user, user)
2240 |> get("/api/v1/domain_blocks")
2242 domain_blocks = json_response(conn, 200)
2244 assert "bad.site" in domain_blocks
2245 assert "even.worse.site" in domain_blocks
2248 test "unimplemented follow_requests, blocks, domain blocks" do
2249 user = insert(:user)
2251 ["blocks", "domain_blocks", "follow_requests"]
2252 |> Enum.each(fn endpoint ->
2255 |> assign(:user, user)
2256 |> get("/api/v1/#{endpoint}")
2258 assert [] = json_response(conn, 200)
2262 test "returns the favorites of a user", %{conn: conn} do
2263 user = insert(:user)
2264 other_user = insert(:user)
2266 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2267 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2269 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2273 |> assign(:user, user)
2274 |> get("/api/v1/favourites")
2276 assert [status] = json_response(first_conn, 200)
2277 assert status["id"] == to_string(activity.id)
2279 assert [{"link", _link_header}] =
2280 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2282 # Honours query params
2283 {:ok, second_activity} =
2284 CommonAPI.post(other_user, %{
2286 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2289 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2291 last_like = status["id"]
2295 |> assign(:user, user)
2296 |> get("/api/v1/favourites?since_id=#{last_like}")
2298 assert [second_status] = json_response(second_conn, 200)
2299 assert second_status["id"] == to_string(second_activity.id)
2303 |> assign(:user, user)
2304 |> get("/api/v1/favourites?limit=0")
2306 assert [] = json_response(third_conn, 200)
2309 describe "getting favorites timeline of specified user" do
2311 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2312 [current_user: current_user, user: user]
2315 test "returns list of statuses favorited by specified user", %{
2317 current_user: current_user,
2320 [activity | _] = insert_pair(:note_activity)
2321 CommonAPI.favorite(activity.id, user)
2325 |> assign(:user, current_user)
2326 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2327 |> json_response(:ok)
2331 assert length(response) == 1
2332 assert like["id"] == activity.id
2335 test "returns favorites for specified user_id when user is not logged in", %{
2339 activity = insert(:note_activity)
2340 CommonAPI.favorite(activity.id, user)
2344 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2345 |> json_response(:ok)
2347 assert length(response) == 1
2350 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2352 current_user: current_user,
2356 CommonAPI.post(current_user, %{
2357 "status" => "Hi @#{user.nickname}!",
2358 "visibility" => "direct"
2361 CommonAPI.favorite(direct.id, user)
2365 |> assign(:user, current_user)
2366 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2367 |> json_response(:ok)
2369 assert length(response) == 1
2371 anonymous_response =
2373 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2374 |> json_response(:ok)
2376 assert Enum.empty?(anonymous_response)
2379 test "does not return others' favorited DM when user is not one of recipients", %{
2381 current_user: current_user,
2384 user_two = insert(:user)
2387 CommonAPI.post(user_two, %{
2388 "status" => "Hi @#{user.nickname}!",
2389 "visibility" => "direct"
2392 CommonAPI.favorite(direct.id, user)
2396 |> assign(:user, current_user)
2397 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2398 |> json_response(:ok)
2400 assert Enum.empty?(response)
2403 test "paginates favorites using since_id and max_id", %{
2405 current_user: current_user,
2408 activities = insert_list(10, :note_activity)
2410 Enum.each(activities, fn activity ->
2411 CommonAPI.favorite(activity.id, user)
2414 third_activity = Enum.at(activities, 2)
2415 seventh_activity = Enum.at(activities, 6)
2419 |> assign(:user, current_user)
2420 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2421 since_id: third_activity.id,
2422 max_id: seventh_activity.id
2424 |> json_response(:ok)
2426 assert length(response) == 3
2427 refute third_activity in response
2428 refute seventh_activity in response
2431 test "limits favorites using limit parameter", %{
2433 current_user: current_user,
2437 |> insert_list(:note_activity)
2438 |> Enum.each(fn activity ->
2439 CommonAPI.favorite(activity.id, user)
2444 |> assign(:user, current_user)
2445 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2446 |> json_response(:ok)
2448 assert length(response) == 3
2451 test "returns empty response when user does not have any favorited statuses", %{
2453 current_user: current_user,
2458 |> assign(:user, current_user)
2459 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2460 |> json_response(:ok)
2462 assert Enum.empty?(response)
2465 test "returns 404 error when specified user is not exist", %{conn: conn} do
2466 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2468 assert json_response(conn, 404) == %{"error" => "Record not found"}
2471 test "returns 403 error when user has hidden own favorites", %{
2473 current_user: current_user
2475 user = insert(:user, %{info: %{hide_favorites: true}})
2476 activity = insert(:note_activity)
2477 CommonAPI.favorite(activity.id, user)
2481 |> assign(:user, current_user)
2482 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2484 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2487 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2488 user = insert(:user)
2489 activity = insert(:note_activity)
2490 CommonAPI.favorite(activity.id, user)
2494 |> assign(:user, current_user)
2495 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2497 assert user.info.hide_favorites
2498 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2502 test "get instance information", %{conn: conn} do
2503 conn = get(conn, "/api/v1/instance")
2504 assert result = json_response(conn, 200)
2506 email = Config.get([:instance, :email])
2507 # Note: not checking for "max_toot_chars" since it's optional
2513 "email" => from_config_email,
2515 "streaming_api" => _
2520 "registrations" => _,
2524 assert email == from_config_email
2527 test "get instance stats", %{conn: conn} do
2528 user = insert(:user, %{local: true})
2530 user2 = insert(:user, %{local: true})
2531 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2533 insert(:user, %{local: false, nickname: "u@peer1.com"})
2534 insert(:user, %{local: false, nickname: "u@peer2.com"})
2536 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2538 # Stats should count users with missing or nil `info.deactivated` value
2539 user = User.get_cached_by_id(user.id)
2540 info_change = Changeset.change(user.info, %{deactivated: nil})
2544 |> Changeset.change()
2545 |> Changeset.put_embed(:info, info_change)
2546 |> User.update_and_set_cache()
2548 Pleroma.Stats.force_update()
2550 conn = get(conn, "/api/v1/instance")
2552 assert result = json_response(conn, 200)
2554 stats = result["stats"]
2557 assert stats["user_count"] == 1
2558 assert stats["status_count"] == 1
2559 assert stats["domain_count"] == 2
2562 test "get peers", %{conn: conn} do
2563 insert(:user, %{local: false, nickname: "u@peer1.com"})
2564 insert(:user, %{local: false, nickname: "u@peer2.com"})
2566 Pleroma.Stats.force_update()
2568 conn = get(conn, "/api/v1/instance/peers")
2570 assert result = json_response(conn, 200)
2572 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2575 test "put settings", %{conn: conn} do
2576 user = insert(:user)
2580 |> assign(:user, user)
2581 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2583 assert _result = json_response(conn, 200)
2585 user = User.get_cached_by_ap_id(user.ap_id)
2586 assert user.info.settings == %{"programming" => "socks"}
2589 describe "pinned statuses" do
2591 user = insert(:user)
2592 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2594 [user: user, activity: activity]
2597 clear_config([:instance, :max_pinned_statuses]) do
2598 Config.put([:instance, :max_pinned_statuses], 1)
2601 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2602 {:ok, _} = CommonAPI.pin(activity.id, user)
2606 |> assign(:user, user)
2607 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2608 |> json_response(200)
2610 id_str = to_string(activity.id)
2612 assert [%{"id" => ^id_str, "pinned" => true}] = result
2615 test "pin status", %{conn: conn, user: user, activity: activity} do
2616 id_str = to_string(activity.id)
2618 assert %{"id" => ^id_str, "pinned" => true} =
2620 |> assign(:user, user)
2621 |> post("/api/v1/statuses/#{activity.id}/pin")
2622 |> json_response(200)
2624 assert [%{"id" => ^id_str, "pinned" => true}] =
2626 |> assign(:user, user)
2627 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2628 |> json_response(200)
2631 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2632 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2636 |> assign(:user, user)
2637 |> post("/api/v1/statuses/#{dm.id}/pin")
2639 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2642 test "unpin status", %{conn: conn, user: user, activity: activity} do
2643 {:ok, _} = CommonAPI.pin(activity.id, user)
2645 id_str = to_string(activity.id)
2646 user = refresh_record(user)
2648 assert %{"id" => ^id_str, "pinned" => false} =
2650 |> assign(:user, user)
2651 |> post("/api/v1/statuses/#{activity.id}/unpin")
2652 |> json_response(200)
2656 |> assign(:user, user)
2657 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2658 |> json_response(200)
2661 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2664 |> assign(:user, user)
2665 |> post("/api/v1/statuses/1/unpin")
2667 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2670 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2671 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2673 id_str_one = to_string(activity_one.id)
2675 assert %{"id" => ^id_str_one, "pinned" => true} =
2677 |> assign(:user, user)
2678 |> post("/api/v1/statuses/#{id_str_one}/pin")
2679 |> json_response(200)
2681 user = refresh_record(user)
2683 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2685 |> assign(:user, user)
2686 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2687 |> json_response(400)
2693 Config.put([:rich_media, :enabled], true)
2695 user = insert(:user)
2699 test "returns rich-media card", %{conn: conn, user: user} do
2700 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2703 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2704 "provider_name" => "example.com",
2705 "provider_url" => "https://example.com",
2706 "title" => "The Rock",
2708 "url" => "https://example.com/ogp",
2710 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2713 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2714 "title" => "The Rock",
2715 "type" => "video.movie",
2716 "url" => "https://example.com/ogp",
2718 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2725 |> get("/api/v1/statuses/#{activity.id}/card")
2726 |> json_response(200)
2728 assert response == card_data
2730 # works with private posts
2732 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2736 |> assign(:user, user)
2737 |> get("/api/v1/statuses/#{activity.id}/card")
2738 |> json_response(200)
2740 assert response_two == card_data
2743 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2745 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2749 |> get("/api/v1/statuses/#{activity.id}/card")
2750 |> json_response(:ok)
2752 assert response == %{
2754 "title" => "Pleroma",
2755 "description" => "",
2757 "provider_name" => "example.com",
2758 "provider_url" => "https://example.com",
2759 "url" => "https://example.com/ogp-missing-data",
2762 "title" => "Pleroma",
2763 "type" => "website",
2764 "url" => "https://example.com/ogp-missing-data"
2772 user = insert(:user)
2773 for_user = insert(:user)
2776 CommonAPI.post(user, %{
2777 "status" => "heweoo?"
2781 CommonAPI.post(user, %{
2782 "status" => "heweoo!"
2787 |> assign(:user, for_user)
2788 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2790 assert json_response(response1, 200)["bookmarked"] == true
2794 |> assign(:user, for_user)
2795 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2797 assert json_response(response2, 200)["bookmarked"] == true
2801 |> assign(:user, for_user)
2802 |> get("/api/v1/bookmarks")
2804 assert [json_response(response2, 200), json_response(response1, 200)] ==
2805 json_response(bookmarks, 200)
2809 |> assign(:user, for_user)
2810 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2812 assert json_response(response1, 200)["bookmarked"] == false
2816 |> assign(:user, for_user)
2817 |> get("/api/v1/bookmarks")
2819 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2822 describe "conversation muting" do
2824 post_user = insert(:user)
2825 user = insert(:user)
2827 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2829 [user: user, activity: activity]
2832 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2833 id_str = to_string(activity.id)
2835 assert %{"id" => ^id_str, "muted" => true} =
2837 |> assign(:user, user)
2838 |> post("/api/v1/statuses/#{activity.id}/mute")
2839 |> json_response(200)
2842 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2843 {:ok, _} = CommonAPI.add_mute(user, activity)
2847 |> assign(:user, user)
2848 |> post("/api/v1/statuses/#{activity.id}/mute")
2850 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2853 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2854 {:ok, _} = CommonAPI.add_mute(user, activity)
2856 id_str = to_string(activity.id)
2857 user = refresh_record(user)
2859 assert %{"id" => ^id_str, "muted" => false} =
2861 |> assign(:user, user)
2862 |> post("/api/v1/statuses/#{activity.id}/unmute")
2863 |> json_response(200)
2867 describe "reports" do
2869 reporter = insert(:user)
2870 target_user = insert(:user)
2872 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2874 [reporter: reporter, target_user: target_user, activity: activity]
2877 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2878 assert %{"action_taken" => false, "id" => _} =
2880 |> assign(:user, reporter)
2881 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2882 |> json_response(200)
2885 test "submit a report with statuses and comment", %{
2888 target_user: target_user,
2891 assert %{"action_taken" => false, "id" => _} =
2893 |> assign(:user, reporter)
2894 |> post("/api/v1/reports", %{
2895 "account_id" => target_user.id,
2896 "status_ids" => [activity.id],
2897 "comment" => "bad status!",
2898 "forward" => "false"
2900 |> json_response(200)
2903 test "account_id is required", %{
2908 assert %{"error" => "Valid `account_id` required"} =
2910 |> assign(:user, reporter)
2911 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2912 |> json_response(400)
2915 test "comment must be up to the size specified in the config", %{
2918 target_user: target_user
2920 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2921 comment = String.pad_trailing("a", max_size + 1, "a")
2923 error = %{"error" => "Comment must be up to #{max_size} characters"}
2927 |> assign(:user, reporter)
2928 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2929 |> json_response(400)
2932 test "returns error when account is not exist", %{
2939 |> assign(:user, reporter)
2940 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
2942 assert json_response(conn, 400) == %{"error" => "Account not found"}
2946 describe "link headers" do
2947 test "preserves parameters in link headers", %{conn: conn} do
2948 user = insert(:user)
2949 other_user = insert(:user)
2952 CommonAPI.post(other_user, %{
2953 "status" => "hi @#{user.nickname}",
2954 "visibility" => "public"
2958 CommonAPI.post(other_user, %{
2959 "status" => "hi @#{user.nickname}",
2960 "visibility" => "public"
2963 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2964 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2968 |> assign(:user, user)
2969 |> get("/api/v1/notifications", %{media_only: true})
2971 assert [link_header] = get_resp_header(conn, "link")
2972 assert link_header =~ ~r/media_only=true/
2973 assert link_header =~ ~r/min_id=#{notification2.id}/
2974 assert link_header =~ ~r/max_id=#{notification1.id}/
2978 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2979 # Need to set an old-style integer ID to reproduce the problem
2980 # (these are no longer assigned to new accounts but were preserved
2981 # for existing accounts during the migration to flakeIDs)
2982 user_one = insert(:user, %{id: 1212})
2983 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2987 |> get("/api/v1/accounts/#{user_one.id}")
2991 |> get("/api/v1/accounts/#{user_two.nickname}")
2995 |> get("/api/v1/accounts/#{user_two.id}")
2997 acc_one = json_response(resp_one, 200)
2998 acc_two = json_response(resp_two, 200)
2999 acc_three = json_response(resp_three, 200)
3000 refute acc_one == acc_two
3001 assert acc_two == acc_three
3004 describe "custom emoji" do
3005 test "with tags", %{conn: conn} do
3008 |> get("/api/v1/custom_emojis")
3009 |> json_response(200)
3011 assert Map.has_key?(emoji, "shortcode")
3012 assert Map.has_key?(emoji, "static_url")
3013 assert Map.has_key?(emoji, "tags")
3014 assert is_list(emoji["tags"])
3015 assert Map.has_key?(emoji, "category")
3016 assert Map.has_key?(emoji, "url")
3017 assert Map.has_key?(emoji, "visible_in_picker")
3021 describe "index/2 redirections" do
3022 setup %{conn: conn} do
3026 signing_salt: "cooldude"
3031 |> Plug.Session.call(Plug.Session.init(session_opts))
3034 test_path = "/web/statuses/test"
3035 %{conn: conn, path: test_path}
3038 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3039 conn = get(conn, path)
3041 assert conn.status == 302
3042 assert redirected_to(conn) == "/web/login"
3045 test "redirects not logged-in users to the login page on private instances", %{
3049 Config.put([:instance, :public], false)
3051 conn = get(conn, path)
3053 assert conn.status == 302
3054 assert redirected_to(conn) == "/web/login"
3057 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3058 token = insert(:oauth_token)
3062 |> assign(:user, token.user)
3063 |> put_session(:oauth_token, token.token)
3066 assert conn.status == 200
3069 test "saves referer path to session", %{conn: conn, path: path} do
3070 conn = get(conn, path)
3071 return_to = Plug.Conn.get_session(conn, :return_to)
3073 assert return_to == path
3076 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3077 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3078 auth = insert(:oauth_authorization, app: app)
3082 |> put_session(:return_to, path)
3083 |> get("/web/login", %{code: auth.token})
3085 assert conn.status == 302
3086 assert redirected_to(conn) == path
3089 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3090 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3091 auth = insert(:oauth_authorization, app: app)
3093 conn = get(conn, "/web/login", %{code: auth.token})
3095 assert conn.status == 302
3096 assert redirected_to(conn) == "/web/getting-started"
3100 describe "scheduled activities" do
3101 test "creates a scheduled activity", %{conn: conn} do
3102 user = insert(:user)
3103 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3107 |> assign(:user, user)
3108 |> post("/api/v1/statuses", %{
3109 "status" => "scheduled",
3110 "scheduled_at" => scheduled_at
3113 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3114 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3115 assert [] == Repo.all(Activity)
3118 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3119 user = insert(:user)
3120 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3122 file = %Plug.Upload{
3123 content_type: "image/jpg",
3124 path: Path.absname("test/fixtures/image.jpg"),
3125 filename: "an_image.jpg"
3128 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3132 |> assign(:user, user)
3133 |> post("/api/v1/statuses", %{
3134 "media_ids" => [to_string(upload.id)],
3135 "status" => "scheduled",
3136 "scheduled_at" => scheduled_at
3139 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3140 assert %{"type" => "image"} = media_attachment
3143 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3145 user = insert(:user)
3148 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3152 |> assign(:user, user)
3153 |> post("/api/v1/statuses", %{
3154 "status" => "not scheduled",
3155 "scheduled_at" => scheduled_at
3158 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3159 assert [] == Repo.all(ScheduledActivity)
3162 test "returns error when daily user limit is exceeded", %{conn: conn} do
3163 user = insert(:user)
3166 NaiveDateTime.utc_now()
3167 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3168 |> NaiveDateTime.to_iso8601()
3170 attrs = %{params: %{}, scheduled_at: today}
3171 {:ok, _} = ScheduledActivity.create(user, attrs)
3172 {:ok, _} = ScheduledActivity.create(user, attrs)
3176 |> assign(:user, user)
3177 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3179 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3182 test "returns error when total user limit is exceeded", %{conn: conn} do
3183 user = insert(:user)
3186 NaiveDateTime.utc_now()
3187 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3188 |> NaiveDateTime.to_iso8601()
3191 NaiveDateTime.utc_now()
3192 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3193 |> NaiveDateTime.to_iso8601()
3195 attrs = %{params: %{}, scheduled_at: today}
3196 {:ok, _} = ScheduledActivity.create(user, attrs)
3197 {:ok, _} = ScheduledActivity.create(user, attrs)
3198 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3202 |> assign(:user, user)
3203 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3205 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3208 test "shows scheduled activities", %{conn: conn} do
3209 user = insert(:user)
3210 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3211 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3212 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3213 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3217 |> assign(:user, user)
3222 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3224 result = json_response(conn_res, 200)
3225 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3230 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3232 result = json_response(conn_res, 200)
3233 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3238 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3240 result = json_response(conn_res, 200)
3241 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3244 test "shows a scheduled activity", %{conn: conn} do
3245 user = insert(:user)
3246 scheduled_activity = insert(:scheduled_activity, user: user)
3250 |> assign(:user, user)
3251 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3253 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3254 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3258 |> assign(:user, user)
3259 |> get("/api/v1/scheduled_statuses/404")
3261 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3264 test "updates a scheduled activity", %{conn: conn} do
3265 user = insert(:user)
3266 scheduled_activity = insert(:scheduled_activity, user: user)
3269 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3273 |> assign(:user, user)
3274 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3275 scheduled_at: new_scheduled_at
3278 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3279 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3283 |> assign(:user, user)
3284 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3286 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3289 test "deletes a scheduled activity", %{conn: conn} do
3290 user = insert(:user)
3291 scheduled_activity = insert(:scheduled_activity, user: user)
3295 |> assign(:user, user)
3296 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3298 assert %{} = json_response(res_conn, 200)
3299 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3303 |> assign(:user, user)
3304 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3306 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3310 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3311 user1 = insert(:user)
3312 user2 = insert(:user)
3313 user3 = insert(:user)
3315 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3317 # Reply to status from another user
3320 |> assign(:user, user2)
3321 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3323 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3325 activity = Activity.get_by_id_with_object(id)
3327 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3328 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3330 # Reblog from the third user
3333 |> assign(:user, user3)
3334 |> post("/api/v1/statuses/#{activity.id}/reblog")
3336 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3337 json_response(conn2, 200)
3339 assert to_string(activity.id) == id
3341 # Getting third user status
3344 |> assign(:user, user3)
3345 |> get("api/v1/timelines/home")
3347 [reblogged_activity] = json_response(conn3, 200)
3349 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3351 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3352 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3355 describe "create account by app" do
3356 test "Account registration via Application", %{conn: conn} do
3359 |> post("/api/v1/apps", %{
3360 client_name: "client_name",
3361 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3362 scopes: "read, write, follow"
3366 "client_id" => client_id,
3367 "client_secret" => client_secret,
3369 "name" => "client_name",
3370 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3373 } = json_response(conn, 200)
3377 |> post("/oauth/token", %{
3378 grant_type: "client_credentials",
3379 client_id: client_id,
3380 client_secret: client_secret
3383 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3384 json_response(conn, 200)
3387 token_from_db = Repo.get_by(Token, token: token)
3388 assert token_from_db
3390 assert scope == "read write follow"
3394 |> put_req_header("authorization", "Bearer " <> token)
3395 |> post("/api/v1/accounts", %{
3397 email: "lain@example.org",
3398 password: "PlzDontHackLain",
3403 "access_token" => token,
3404 "created_at" => _created_at,
3406 "token_type" => "Bearer"
3407 } = json_response(conn, 200)
3409 token_from_db = Repo.get_by(Token, token: token)
3410 assert token_from_db
3411 token_from_db = Repo.preload(token_from_db, :user)
3412 assert token_from_db.user
3414 assert token_from_db.user.info.confirmation_pending
3417 test "rate limit", %{conn: conn} do
3418 app_token = insert(:oauth_token, user: nil)
3421 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3422 |> Map.put(:remote_ip, {15, 15, 15, 15})
3427 |> post("/api/v1/accounts", %{
3428 username: "#{i}lain",
3429 email: "#{i}lain@example.org",
3430 password: "PlzDontHackLain",
3435 "access_token" => token,
3436 "created_at" => _created_at,
3438 "token_type" => "Bearer"
3439 } = json_response(conn, 200)
3441 token_from_db = Repo.get_by(Token, token: token)
3442 assert token_from_db
3443 token_from_db = Repo.preload(token_from_db, :user)
3444 assert token_from_db.user
3446 assert token_from_db.user.info.confirmation_pending
3451 |> post("/api/v1/accounts", %{
3453 email: "6lain@example.org",
3454 password: "PlzDontHackLain",
3458 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3462 describe "GET /api/v1/polls/:id" do
3463 test "returns poll entity for object id", %{conn: conn} do
3464 user = insert(:user)
3467 CommonAPI.post(user, %{
3468 "status" => "Pleroma does",
3469 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3472 object = Object.normalize(activity)
3476 |> assign(:user, user)
3477 |> get("/api/v1/polls/#{object.id}")
3479 response = json_response(conn, 200)
3480 id = to_string(object.id)
3481 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3484 test "does not expose polls for private statuses", %{conn: conn} do
3485 user = insert(:user)
3486 other_user = insert(:user)
3489 CommonAPI.post(user, %{
3490 "status" => "Pleroma does",
3491 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3492 "visibility" => "private"
3495 object = Object.normalize(activity)
3499 |> assign(:user, other_user)
3500 |> get("/api/v1/polls/#{object.id}")
3502 assert json_response(conn, 404)
3506 describe "POST /api/v1/polls/:id/votes" do
3507 test "votes are added to the poll", %{conn: conn} do
3508 user = insert(:user)
3509 other_user = insert(:user)
3512 CommonAPI.post(user, %{
3513 "status" => "A very delicious sandwich",
3515 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3521 object = Object.normalize(activity)
3525 |> assign(:user, other_user)
3526 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3528 assert json_response(conn, 200)
3529 object = Object.get_by_id(object.id)
3531 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3536 test "author can't vote", %{conn: conn} do
3537 user = insert(:user)
3540 CommonAPI.post(user, %{
3541 "status" => "Am I cute?",
3542 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3545 object = Object.normalize(activity)
3548 |> assign(:user, user)
3549 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3550 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3552 object = Object.get_by_id(object.id)
3554 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3557 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3558 user = insert(:user)
3559 other_user = insert(:user)
3562 CommonAPI.post(user, %{
3563 "status" => "The glass is",
3564 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3567 object = Object.normalize(activity)
3570 |> assign(:user, other_user)
3571 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3572 |> json_response(422) == %{"error" => "Too many choices"}
3574 object = Object.get_by_id(object.id)
3576 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3581 test "does not allow choice index to be greater than options count", %{conn: conn} do
3582 user = insert(:user)
3583 other_user = insert(:user)
3586 CommonAPI.post(user, %{
3587 "status" => "Am I cute?",
3588 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3591 object = Object.normalize(activity)
3595 |> assign(:user, other_user)
3596 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3598 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3601 test "returns 404 error when object is not exist", %{conn: conn} do
3602 user = insert(:user)
3606 |> assign(:user, user)
3607 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3609 assert json_response(conn, 404) == %{"error" => "Record not found"}
3612 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3613 user = insert(:user)
3614 other_user = insert(:user)
3617 CommonAPI.post(user, %{
3618 "status" => "Am I cute?",
3619 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3620 "visibility" => "private"
3623 object = Object.normalize(activity)
3627 |> assign(:user, other_user)
3628 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3630 assert json_response(conn, 404) == %{"error" => "Record not found"}
3634 describe "GET /api/v1/statuses/:id/favourited_by" do
3636 user = insert(:user)
3637 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3641 |> assign(:user, user)
3643 [conn: conn, activity: activity]
3646 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3647 other_user = insert(:user)
3648 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3652 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3653 |> json_response(:ok)
3655 [%{"id" => id}] = response
3657 assert id == other_user.id
3660 test "returns empty array when status has not been favorited yet", %{
3666 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3667 |> json_response(:ok)
3669 assert Enum.empty?(response)
3672 test "does not return users who have favorited the status but are blocked", %{
3673 conn: %{assigns: %{user: user}} = conn,
3676 other_user = insert(:user)
3677 {:ok, user} = User.block(user, other_user)
3679 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3683 |> assign(:user, user)
3684 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3685 |> json_response(:ok)
3687 assert Enum.empty?(response)
3690 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3691 other_user = insert(:user)
3692 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3696 |> assign(:user, nil)
3697 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3698 |> json_response(:ok)
3700 [%{"id" => id}] = response
3701 assert id == other_user.id
3705 describe "GET /api/v1/statuses/:id/reblogged_by" do
3707 user = insert(:user)
3708 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3712 |> assign(:user, user)
3714 [conn: conn, activity: activity]
3717 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3718 other_user = insert(:user)
3719 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3723 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3724 |> json_response(:ok)
3726 [%{"id" => id}] = response
3728 assert id == other_user.id
3731 test "returns empty array when status has not been reblogged yet", %{
3737 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3738 |> json_response(:ok)
3740 assert Enum.empty?(response)
3743 test "does not return users who have reblogged the status but are blocked", %{
3744 conn: %{assigns: %{user: user}} = conn,
3747 other_user = insert(:user)
3748 {:ok, user} = User.block(user, other_user)
3750 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3754 |> assign(:user, user)
3755 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3756 |> json_response(:ok)
3758 assert Enum.empty?(response)
3761 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3762 other_user = insert(:user)
3763 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3767 |> assign(:user, nil)
3768 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3769 |> json_response(:ok)
3771 [%{"id" => id}] = response
3772 assert id == other_user.id
3776 describe "POST /auth/password, with valid parameters" do
3777 setup %{conn: conn} do
3778 user = insert(:user)
3779 conn = post(conn, "/auth/password?email=#{user.email}")
3780 %{conn: conn, user: user}
3783 test "it returns 204", %{conn: conn} do
3784 assert json_response(conn, :no_content)
3787 test "it creates a PasswordResetToken record for user", %{user: user} do
3788 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3792 test "it sends an email to user", %{user: user} do
3793 ObanHelpers.perform_all()
3794 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3796 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3797 notify_email = Config.get([:instance, :notify_email])
3798 instance_name = Config.get([:instance, :name])
3801 from: {instance_name, notify_email},
3802 to: {user.name, user.email},
3803 html_body: email.html_body
3808 describe "POST /auth/password, with invalid parameters" do
3810 user = insert(:user)
3814 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3815 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3816 assert conn.status == 404
3817 assert conn.resp_body == ""
3820 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3821 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3822 conn = post(conn, "/auth/password?email=#{user.email}")
3823 assert conn.status == 400
3824 assert conn.resp_body == ""
3828 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3830 user = insert(:user)
3831 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3835 |> Changeset.change()
3836 |> Changeset.put_embed(:info, info_change)
3839 assert user.info.confirmation_pending
3844 clear_config([:instance, :account_activation_required]) do
3845 Config.put([:instance, :account_activation_required], true)
3848 test "resend account confirmation email", %{conn: conn, user: user} do
3850 |> assign(:user, user)
3851 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3852 |> json_response(:no_content)
3854 ObanHelpers.perform_all()
3856 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3857 notify_email = Config.get([:instance, :notify_email])
3858 instance_name = Config.get([:instance, :name])
3861 from: {instance_name, notify_email},
3862 to: {user.name, user.email},
3863 html_body: email.html_body
3868 describe "GET /api/v1/suggestions" do
3870 user = insert(:user)
3871 other_user = insert(:user)
3872 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3873 url500 = "http://test500?#{host}&#{user.nickname}"
3874 url200 = "http://test200?#{host}&#{user.nickname}"
3877 %{method: :get, url: ^url500} ->
3878 %Tesla.Env{status: 500, body: "bad request"}
3880 %{method: :get, url: ^url200} ->
3884 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3886 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3890 [user: user, other_user: other_user]
3893 clear_config(:suggestions)
3895 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3896 Config.put([:suggestions, :enabled], false)
3900 |> assign(:user, user)
3901 |> get("/api/v1/suggestions")
3902 |> json_response(200)
3907 test "returns error", %{conn: conn, user: user} do
3908 Config.put([:suggestions, :enabled], true)
3909 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
3913 |> assign(:user, user)
3914 |> get("/api/v1/suggestions")
3915 |> json_response(500)
3917 assert res == "Something went wrong"
3920 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
3921 Config.put([:suggestions, :enabled], true)
3922 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
3926 |> assign(:user, user)
3927 |> get("/api/v1/suggestions")
3928 |> json_response(200)
3933 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
3934 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
3938 "acct" => other_user.ap_id,
3939 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
3940 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
3941 "id" => other_user.id