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
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.MastodonAPI.FilterView
20 alias Pleroma.Web.OAuth.App
21 alias Pleroma.Web.OAuth.Token
22 alias Pleroma.Web.OStatus
23 alias Pleroma.Web.Push
24 alias Pleroma.Web.TwitterAPI.TwitterAPI
25 import Pleroma.Factory
26 import ExUnit.CaptureLog
28 import Swoosh.TestAssertions
30 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
33 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
37 clear_config([:instance, :public])
38 clear_config([:rich_media, :enabled])
40 test "the home timeline", %{conn: conn} do
42 following = insert(:user)
44 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
51 assert Enum.empty?(json_response(conn, 200))
53 {:ok, user} = User.follow(user, following)
57 |> assign(:user, user)
58 |> get("/api/v1/timelines/home")
60 assert [%{"content" => "test"}] = json_response(conn, 200)
63 test "the public timeline", %{conn: conn} do
64 following = insert(:user)
67 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
70 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
74 |> get("/api/v1/timelines/public", %{"local" => "False"})
76 assert length(json_response(conn, 200)) == 2
80 |> get("/api/v1/timelines/public", %{"local" => "True"})
82 assert [%{"content" => "test"}] = json_response(conn, 200)
86 |> get("/api/v1/timelines/public", %{"local" => "1"})
88 assert [%{"content" => "test"}] = json_response(conn, 200)
92 test "the public timeline when public is set to false", %{conn: conn} do
93 Config.put([:instance, :public], false)
96 |> get("/api/v1/timelines/public", %{"local" => "False"})
97 |> json_response(403) == %{"error" => "This resource requires authentication."}
100 describe "posting statuses" do
106 |> assign(:user, user)
111 test "posting a status", %{conn: conn} do
112 idempotency_key = "Pikachu rocks!"
116 |> put_req_header("idempotency-key", idempotency_key)
117 |> post("/api/v1/statuses", %{
119 "spoiler_text" => "2hu",
120 "sensitive" => "false"
123 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
125 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
127 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
128 json_response(conn_one, 200)
130 assert Activity.get_by_id(id)
134 |> put_req_header("idempotency-key", idempotency_key)
135 |> post("/api/v1/statuses", %{
137 "spoiler_text" => "2hu",
138 "sensitive" => "false"
141 assert %{"id" => second_id} = json_response(conn_two, 200)
142 assert id == second_id
146 |> post("/api/v1/statuses", %{
148 "spoiler_text" => "2hu",
149 "sensitive" => "false"
152 assert %{"id" => third_id} = json_response(conn_three, 200)
153 refute id == third_id
155 # An activity that will expire:
157 expires_in = 120 * 60
161 |> post("api/v1/statuses", %{
162 "status" => "oolong",
163 "expires_in" => expires_in
166 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
167 assert activity = Activity.get_by_id(fourth_id)
168 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
170 estimated_expires_at =
171 NaiveDateTime.utc_now()
172 |> NaiveDateTime.add(expires_in)
173 |> NaiveDateTime.truncate(:second)
175 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
176 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
178 assert fourth_response["pleroma"]["expires_at"] ==
179 NaiveDateTime.to_iso8601(expiration.scheduled_at)
182 test "replying to a status", %{conn: conn} do
184 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
188 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
190 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
192 activity = Activity.get_by_id(id)
194 assert activity.data["context"] == replied_to.data["context"]
195 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
198 test "replying to a direct message with visibility other than direct", %{conn: conn} do
200 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
202 Enum.each(["public", "private", "unlisted"], fn visibility ->
205 |> post("/api/v1/statuses", %{
206 "status" => "@#{user.nickname} hey",
207 "in_reply_to_id" => replied_to.id,
208 "visibility" => visibility
211 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
215 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
218 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
220 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
221 assert Activity.get_by_id(id)
224 test "posting a sensitive status", %{conn: conn} do
227 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
229 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
230 assert Activity.get_by_id(id)
233 test "posting a fake status", %{conn: conn} do
236 |> post("/api/v1/statuses", %{
238 "\"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"
241 real_status = json_response(real_conn, 200)
244 assert Object.get_by_ap_id(real_status["uri"])
248 |> Map.put("id", nil)
249 |> Map.put("url", nil)
250 |> Map.put("uri", nil)
251 |> Map.put("created_at", nil)
252 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
256 |> post("/api/v1/statuses", %{
258 "\"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",
262 fake_status = json_response(fake_conn, 200)
265 refute Object.get_by_ap_id(fake_status["uri"])
269 |> Map.put("id", nil)
270 |> Map.put("url", nil)
271 |> Map.put("uri", nil)
272 |> Map.put("created_at", nil)
273 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
275 assert real_status == fake_status
278 test "posting a status with OGP link preview", %{conn: conn} do
279 Config.put([:rich_media, :enabled], true)
283 |> post("/api/v1/statuses", %{
284 "status" => "https://example.com/ogp"
287 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
288 assert Activity.get_by_id(id)
291 test "posting a direct status", %{conn: conn} do
292 user2 = insert(:user)
293 content = "direct cofe @#{user2.nickname}"
297 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
299 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
300 assert activity = Activity.get_by_id(id)
301 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
302 assert activity.data["to"] == [user2.ap_id]
303 assert activity.data["cc"] == []
307 describe "posting polls" do
308 test "posting a poll", %{conn: conn} do
310 time = NaiveDateTime.utc_now()
314 |> assign(:user, user)
315 |> post("/api/v1/statuses", %{
316 "status" => "Who is the #bestgrill?",
317 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
320 response = json_response(conn, 200)
322 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
323 title in ["Rei", "Asuka", "Misato"]
326 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
327 refute response["poll"]["expred"]
330 test "option limit is enforced", %{conn: conn} do
332 limit = Config.get([:instance, :poll_limits, :max_options])
336 |> assign(:user, user)
337 |> post("/api/v1/statuses", %{
339 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
342 %{"error" => error} = json_response(conn, 422)
343 assert error == "Poll can't contain more than #{limit} options"
346 test "option character limit is enforced", %{conn: conn} do
348 limit = Config.get([:instance, :poll_limits, :max_option_chars])
352 |> assign(:user, user)
353 |> post("/api/v1/statuses", %{
356 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
361 %{"error" => error} = json_response(conn, 422)
362 assert error == "Poll options cannot be longer than #{limit} characters each"
365 test "minimal date limit is enforced", %{conn: conn} do
367 limit = Config.get([:instance, :poll_limits, :min_expiration])
371 |> assign(:user, user)
372 |> post("/api/v1/statuses", %{
373 "status" => "imagine arbitrary limits",
375 "options" => ["this post was made by pleroma gang"],
376 "expires_in" => limit - 1
380 %{"error" => error} = json_response(conn, 422)
381 assert error == "Expiration date is too soon"
384 test "maximum date limit is enforced", %{conn: conn} do
386 limit = Config.get([:instance, :poll_limits, :max_expiration])
390 |> assign(:user, user)
391 |> post("/api/v1/statuses", %{
392 "status" => "imagine arbitrary limits",
394 "options" => ["this post was made by pleroma gang"],
395 "expires_in" => limit + 1
399 %{"error" => error} = json_response(conn, 422)
400 assert error == "Expiration date is too far in the future"
404 test "direct timeline", %{conn: conn} do
405 user_one = insert(:user)
406 user_two = insert(:user)
408 {:ok, user_two} = User.follow(user_two, user_one)
411 CommonAPI.post(user_one, %{
412 "status" => "Hi @#{user_two.nickname}!",
413 "visibility" => "direct"
416 {:ok, _follower_only} =
417 CommonAPI.post(user_one, %{
418 "status" => "Hi @#{user_two.nickname}!",
419 "visibility" => "private"
422 # Only direct should be visible here
425 |> assign(:user, user_two)
426 |> get("api/v1/timelines/direct")
428 [status] = json_response(res_conn, 200)
430 assert %{"visibility" => "direct"} = status
431 assert status["url"] != direct.data["id"]
433 # User should be able to see their own direct message
436 |> assign(:user, user_one)
437 |> get("api/v1/timelines/direct")
439 [status] = json_response(res_conn, 200)
441 assert %{"visibility" => "direct"} = status
443 # Both should be visible here
446 |> assign(:user, user_two)
447 |> get("api/v1/timelines/home")
449 [_s1, _s2] = json_response(res_conn, 200)
452 Enum.each(1..20, fn _ ->
454 CommonAPI.post(user_one, %{
455 "status" => "Hi @#{user_two.nickname}!",
456 "visibility" => "direct"
462 |> assign(:user, user_two)
463 |> get("api/v1/timelines/direct")
465 statuses = json_response(res_conn, 200)
466 assert length(statuses) == 20
470 |> assign(:user, user_two)
471 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
473 [status] = json_response(res_conn, 200)
475 assert status["url"] != direct.data["id"]
478 test "Conversations", %{conn: conn} do
479 user_one = insert(:user)
480 user_two = insert(:user)
481 user_three = insert(:user)
483 {:ok, user_two} = User.follow(user_two, user_one)
486 CommonAPI.post(user_one, %{
487 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
488 "visibility" => "direct"
491 {:ok, _follower_only} =
492 CommonAPI.post(user_one, %{
493 "status" => "Hi @#{user_two.nickname}!",
494 "visibility" => "private"
499 |> assign(:user, user_one)
500 |> get("/api/v1/conversations")
502 assert response = json_response(res_conn, 200)
507 "accounts" => res_accounts,
508 "last_status" => res_last_status,
513 account_ids = Enum.map(res_accounts, & &1["id"])
514 assert length(res_accounts) == 2
515 assert user_two.id in account_ids
516 assert user_three.id in account_ids
517 assert is_binary(res_id)
518 assert unread == true
519 assert res_last_status["id"] == direct.id
521 # Apparently undocumented API endpoint
524 |> assign(:user, user_one)
525 |> post("/api/v1/conversations/#{res_id}/read")
527 assert response = json_response(res_conn, 200)
528 assert length(response["accounts"]) == 2
529 assert response["last_status"]["id"] == direct.id
530 assert response["unread"] == false
532 # (vanilla) Mastodon frontend behaviour
535 |> assign(:user, user_one)
536 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
538 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
541 test "doesn't include DMs from blocked users", %{conn: conn} do
542 blocker = insert(:user)
543 blocked = insert(:user)
545 {:ok, blocker} = User.block(blocker, blocked)
547 {:ok, _blocked_direct} =
548 CommonAPI.post(blocked, %{
549 "status" => "Hi @#{blocker.nickname}!",
550 "visibility" => "direct"
554 CommonAPI.post(user, %{
555 "status" => "Hi @#{blocker.nickname}!",
556 "visibility" => "direct"
561 |> assign(:user, user)
562 |> get("api/v1/timelines/direct")
564 [status] = json_response(res_conn, 200)
565 assert status["id"] == direct.id
568 test "verify_credentials", %{conn: conn} do
573 |> assign(:user, user)
574 |> get("/api/v1/accounts/verify_credentials")
576 response = json_response(conn, 200)
578 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
579 assert response["pleroma"]["chat_token"]
580 assert id == to_string(user.id)
583 test "verify_credentials default scope unlisted", %{conn: conn} do
584 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
588 |> assign(:user, user)
589 |> get("/api/v1/accounts/verify_credentials")
591 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
592 assert id == to_string(user.id)
595 test "apps/verify_credentials", %{conn: conn} do
596 token = insert(:oauth_token)
600 |> assign(:user, token.user)
601 |> assign(:token, token)
602 |> get("/api/v1/apps/verify_credentials")
604 app = Repo.preload(token, :app).app
607 "name" => app.client_name,
608 "website" => app.website,
609 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
612 assert expected == json_response(conn, 200)
615 test "user avatar can be set", %{conn: conn} do
617 avatar_image = File.read!("test/fixtures/avatar_data_uri")
621 |> assign(:user, user)
622 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
624 user = refresh_record(user)
638 assert %{"url" => _} = json_response(conn, 200)
641 test "user avatar can be reset", %{conn: conn} do
646 |> assign(:user, user)
647 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
649 user = User.get_cached_by_id(user.id)
651 assert user.avatar == nil
653 assert %{"url" => nil} = json_response(conn, 200)
656 test "can set profile banner", %{conn: conn} do
661 |> assign(:user, user)
662 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
664 user = refresh_record(user)
665 assert user.info.banner["type"] == "Image"
667 assert %{"url" => _} = json_response(conn, 200)
670 test "can reset profile banner", %{conn: conn} do
675 |> assign(:user, user)
676 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
678 user = refresh_record(user)
679 assert user.info.banner == %{}
681 assert %{"url" => nil} = json_response(conn, 200)
684 test "background image can be set", %{conn: conn} do
689 |> assign(:user, user)
690 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
692 user = refresh_record(user)
693 assert user.info.background["type"] == "Image"
694 assert %{"url" => _} = json_response(conn, 200)
697 test "background image can be reset", %{conn: conn} do
702 |> assign(:user, user)
703 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
705 user = refresh_record(user)
706 assert user.info.background == %{}
707 assert %{"url" => nil} = json_response(conn, 200)
710 test "creates an oauth app", %{conn: conn} do
712 app_attrs = build(:oauth_app)
716 |> assign(:user, user)
717 |> post("/api/v1/apps", %{
718 client_name: app_attrs.client_name,
719 redirect_uris: app_attrs.redirect_uris
722 [app] = Repo.all(App)
725 "name" => app.client_name,
726 "website" => app.website,
727 "client_id" => app.client_id,
728 "client_secret" => app.client_secret,
729 "id" => app.id |> to_string(),
730 "redirect_uri" => app.redirect_uris,
731 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
734 assert expected == json_response(conn, 200)
737 test "get a status", %{conn: conn} do
738 activity = insert(:note_activity)
742 |> get("/api/v1/statuses/#{activity.id}")
744 assert %{"id" => id} = json_response(conn, 200)
745 assert id == to_string(activity.id)
748 describe "deleting a status" do
749 test "when you created it", %{conn: conn} do
750 activity = insert(:note_activity)
751 author = User.get_cached_by_ap_id(activity.data["actor"])
755 |> assign(:user, author)
756 |> delete("/api/v1/statuses/#{activity.id}")
758 assert %{} = json_response(conn, 200)
760 refute Activity.get_by_id(activity.id)
763 test "when you didn't create it", %{conn: conn} do
764 activity = insert(:note_activity)
769 |> assign(:user, user)
770 |> delete("/api/v1/statuses/#{activity.id}")
772 assert %{"error" => _} = json_response(conn, 403)
774 assert Activity.get_by_id(activity.id) == activity
777 test "when you're an admin or moderator", %{conn: conn} do
778 activity1 = insert(:note_activity)
779 activity2 = insert(:note_activity)
780 admin = insert(:user, info: %{is_admin: true})
781 moderator = insert(:user, info: %{is_moderator: true})
785 |> assign(:user, admin)
786 |> delete("/api/v1/statuses/#{activity1.id}")
788 assert %{} = json_response(res_conn, 200)
792 |> assign(:user, moderator)
793 |> delete("/api/v1/statuses/#{activity2.id}")
795 assert %{} = json_response(res_conn, 200)
797 refute Activity.get_by_id(activity1.id)
798 refute Activity.get_by_id(activity2.id)
802 describe "filters" do
803 test "creating a filter", %{conn: conn} do
806 filter = %Pleroma.Filter{
813 |> assign(:user, user)
814 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
816 assert response = json_response(conn, 200)
817 assert response["phrase"] == filter.phrase
818 assert response["context"] == filter.context
819 assert response["irreversible"] == false
820 assert response["id"] != nil
821 assert response["id"] != ""
824 test "fetching a list of filters", %{conn: conn} do
827 query_one = %Pleroma.Filter{
834 query_two = %Pleroma.Filter{
841 {:ok, filter_one} = Pleroma.Filter.create(query_one)
842 {:ok, filter_two} = Pleroma.Filter.create(query_two)
846 |> assign(:user, user)
847 |> get("/api/v1/filters")
848 |> json_response(200)
854 filters: [filter_two, filter_one]
858 test "get a filter", %{conn: conn} do
861 query = %Pleroma.Filter{
868 {:ok, filter} = Pleroma.Filter.create(query)
872 |> assign(:user, user)
873 |> get("/api/v1/filters/#{filter.filter_id}")
875 assert _response = json_response(conn, 200)
878 test "update a filter", %{conn: conn} do
881 query = %Pleroma.Filter{
888 {:ok, _filter} = Pleroma.Filter.create(query)
890 new = %Pleroma.Filter{
897 |> assign(:user, user)
898 |> put("/api/v1/filters/#{query.filter_id}", %{
903 assert response = json_response(conn, 200)
904 assert response["phrase"] == new.phrase
905 assert response["context"] == new.context
908 test "delete a filter", %{conn: conn} do
911 query = %Pleroma.Filter{
918 {:ok, filter} = Pleroma.Filter.create(query)
922 |> assign(:user, user)
923 |> delete("/api/v1/filters/#{filter.filter_id}")
925 assert response = json_response(conn, 200)
926 assert response == %{}
930 describe "list timelines" do
931 test "list timeline", %{conn: conn} do
933 other_user = insert(:user)
934 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
935 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
936 {:ok, list} = Pleroma.List.create("name", user)
937 {:ok, list} = Pleroma.List.follow(list, other_user)
941 |> assign(:user, user)
942 |> get("/api/v1/timelines/list/#{list.id}")
944 assert [%{"id" => id}] = json_response(conn, 200)
946 assert id == to_string(activity_two.id)
949 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
951 other_user = insert(:user)
952 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
954 {:ok, _activity_two} =
955 CommonAPI.post(other_user, %{
956 "status" => "Marisa is cute.",
957 "visibility" => "private"
960 {:ok, list} = Pleroma.List.create("name", user)
961 {:ok, list} = Pleroma.List.follow(list, other_user)
965 |> assign(:user, user)
966 |> get("/api/v1/timelines/list/#{list.id}")
968 assert [%{"id" => id}] = json_response(conn, 200)
970 assert id == to_string(activity_one.id)
974 describe "notifications" do
975 test "list of notifications", %{conn: conn} do
977 other_user = insert(:user)
979 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
981 {:ok, [_notification]} = Notification.create_notifications(activity)
985 |> assign(:user, user)
986 |> get("/api/v1/notifications")
989 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
991 }\">@<span>#{user.nickname}</span></a></span>"
993 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
994 assert response == expected_response
997 test "getting a single notification", %{conn: conn} do
999 other_user = insert(:user)
1001 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1003 {:ok, [notification]} = Notification.create_notifications(activity)
1007 |> assign(:user, user)
1008 |> get("/api/v1/notifications/#{notification.id}")
1011 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1013 }\">@<span>#{user.nickname}</span></a></span>"
1015 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1016 assert response == expected_response
1019 test "dismissing a single notification", %{conn: conn} do
1020 user = insert(:user)
1021 other_user = insert(:user)
1023 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1025 {:ok, [notification]} = Notification.create_notifications(activity)
1029 |> assign(:user, user)
1030 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1032 assert %{} = json_response(conn, 200)
1035 test "clearing all notifications", %{conn: conn} do
1036 user = insert(:user)
1037 other_user = insert(:user)
1039 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1041 {:ok, [_notification]} = Notification.create_notifications(activity)
1045 |> assign(:user, user)
1046 |> post("/api/v1/notifications/clear")
1048 assert %{} = json_response(conn, 200)
1052 |> assign(:user, user)
1053 |> get("/api/v1/notifications")
1055 assert all = json_response(conn, 200)
1059 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1060 user = insert(:user)
1061 other_user = insert(:user)
1063 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1064 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1065 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1066 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1068 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1069 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1070 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1071 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1075 |> assign(:user, user)
1080 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1082 result = json_response(conn_res, 200)
1083 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1088 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1090 result = json_response(conn_res, 200)
1091 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1096 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1098 result = json_response(conn_res, 200)
1099 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1102 test "filters notifications using exclude_types", %{conn: conn} do
1103 user = insert(:user)
1104 other_user = insert(:user)
1106 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1107 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1108 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1109 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1110 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1112 mention_notification_id =
1113 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1115 favorite_notification_id =
1116 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1118 reblog_notification_id =
1119 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1121 follow_notification_id =
1122 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1126 |> assign(:user, user)
1129 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1131 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1134 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1136 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1139 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1141 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1144 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1146 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1149 test "destroy multiple", %{conn: conn} do
1150 user = insert(:user)
1151 other_user = insert(:user)
1153 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1154 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1155 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1156 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1158 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1159 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1160 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1161 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1165 |> assign(:user, user)
1169 |> get("/api/v1/notifications")
1171 result = json_response(conn_res, 200)
1172 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1176 |> assign(:user, other_user)
1180 |> get("/api/v1/notifications")
1182 result = json_response(conn_res, 200)
1183 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1187 |> delete("/api/v1/notifications/destroy_multiple", %{
1188 "ids" => [notification1_id, notification2_id]
1191 assert json_response(conn_destroy, 200) == %{}
1195 |> get("/api/v1/notifications")
1197 result = json_response(conn_res, 200)
1198 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1201 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1202 user = insert(:user)
1203 user2 = insert(:user)
1205 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1206 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1208 conn = assign(conn, :user, user)
1210 conn = get(conn, "/api/v1/notifications")
1212 assert length(json_response(conn, 200)) == 1
1214 {:ok, user} = User.mute(user, user2)
1216 conn = assign(build_conn(), :user, user)
1217 conn = get(conn, "/api/v1/notifications")
1219 assert json_response(conn, 200) == []
1222 test "see notifications after muting user without notifications", %{conn: conn} do
1223 user = insert(:user)
1224 user2 = insert(:user)
1226 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1227 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1229 conn = assign(conn, :user, user)
1231 conn = get(conn, "/api/v1/notifications")
1233 assert length(json_response(conn, 200)) == 1
1235 {:ok, user} = User.mute(user, user2, false)
1237 conn = assign(build_conn(), :user, user)
1238 conn = get(conn, "/api/v1/notifications")
1240 assert length(json_response(conn, 200)) == 1
1243 test "see notifications after muting user with notifications and with_muted parameter", %{
1246 user = insert(:user)
1247 user2 = insert(:user)
1249 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1250 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1252 conn = assign(conn, :user, user)
1254 conn = get(conn, "/api/v1/notifications")
1256 assert length(json_response(conn, 200)) == 1
1258 {:ok, user} = User.mute(user, user2)
1260 conn = assign(build_conn(), :user, user)
1261 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1263 assert length(json_response(conn, 200)) == 1
1267 describe "reblogging" do
1268 test "reblogs and returns the reblogged status", %{conn: conn} do
1269 activity = insert(:note_activity)
1270 user = insert(:user)
1274 |> assign(:user, user)
1275 |> post("/api/v1/statuses/#{activity.id}/reblog")
1278 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1280 } = json_response(conn, 200)
1282 assert to_string(activity.id) == id
1285 test "reblogged status for another user", %{conn: conn} do
1286 activity = insert(:note_activity)
1287 user1 = insert(:user)
1288 user2 = insert(:user)
1289 user3 = insert(:user)
1290 CommonAPI.favorite(activity.id, user2)
1291 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1292 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1293 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1297 |> assign(:user, user3)
1298 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1301 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1302 "reblogged" => false,
1303 "favourited" => false,
1304 "bookmarked" => false
1305 } = json_response(conn_res, 200)
1309 |> assign(:user, user2)
1310 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1313 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1314 "reblogged" => true,
1315 "favourited" => true,
1316 "bookmarked" => true
1317 } = json_response(conn_res, 200)
1319 assert to_string(activity.id) == id
1322 test "returns 400 error when activity is not exist", %{conn: conn} do
1323 user = insert(:user)
1327 |> assign(:user, user)
1328 |> post("/api/v1/statuses/foo/reblog")
1330 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1334 describe "unreblogging" do
1335 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1336 activity = insert(:note_activity)
1337 user = insert(:user)
1339 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1343 |> assign(:user, user)
1344 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1346 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1348 assert to_string(activity.id) == id
1351 test "returns 400 error when activity is not exist", %{conn: conn} do
1352 user = insert(:user)
1356 |> assign(:user, user)
1357 |> post("/api/v1/statuses/foo/unreblog")
1359 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1363 describe "favoriting" do
1364 test "favs a status and returns it", %{conn: conn} do
1365 activity = insert(:note_activity)
1366 user = insert(:user)
1370 |> assign(:user, user)
1371 |> post("/api/v1/statuses/#{activity.id}/favourite")
1373 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1374 json_response(conn, 200)
1376 assert to_string(activity.id) == id
1379 test "returns 400 error for a wrong id", %{conn: conn} do
1380 user = insert(:user)
1384 |> assign(:user, user)
1385 |> post("/api/v1/statuses/1/favourite")
1387 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1391 describe "unfavoriting" do
1392 test "unfavorites a status and returns it", %{conn: conn} do
1393 activity = insert(:note_activity)
1394 user = insert(:user)
1396 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1400 |> assign(:user, user)
1401 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1403 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1404 json_response(conn, 200)
1406 assert to_string(activity.id) == id
1409 test "returns 400 error for a wrong id", %{conn: conn} do
1410 user = insert(:user)
1414 |> assign(:user, user)
1415 |> post("/api/v1/statuses/1/unfavourite")
1417 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1421 describe "user timelines" do
1422 test "gets a users statuses", %{conn: conn} do
1423 user_one = insert(:user)
1424 user_two = insert(:user)
1425 user_three = insert(:user)
1427 {:ok, user_three} = User.follow(user_three, user_one)
1429 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1431 {:ok, direct_activity} =
1432 CommonAPI.post(user_one, %{
1433 "status" => "Hi, @#{user_two.nickname}.",
1434 "visibility" => "direct"
1437 {:ok, private_activity} =
1438 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1442 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1444 assert [%{"id" => id}] = json_response(resp, 200)
1445 assert id == to_string(activity.id)
1449 |> assign(:user, user_two)
1450 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1452 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1453 assert id_one == to_string(direct_activity.id)
1454 assert id_two == to_string(activity.id)
1458 |> assign(:user, user_three)
1459 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1461 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1462 assert id_one == to_string(private_activity.id)
1463 assert id_two == to_string(activity.id)
1466 test "unimplemented pinned statuses feature", %{conn: conn} do
1467 note = insert(:note_activity)
1468 user = User.get_cached_by_ap_id(note.data["actor"])
1472 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1474 assert json_response(conn, 200) == []
1477 test "gets an users media", %{conn: conn} do
1478 note = insert(:note_activity)
1479 user = User.get_cached_by_ap_id(note.data["actor"])
1481 file = %Plug.Upload{
1482 content_type: "image/jpg",
1483 path: Path.absname("test/fixtures/image.jpg"),
1484 filename: "an_image.jpg"
1488 TwitterAPI.upload(file, user, "json")
1492 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1496 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1498 assert [%{"id" => id}] = json_response(conn, 200)
1499 assert id == to_string(image_post.id)
1503 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1505 assert [%{"id" => id}] = json_response(conn, 200)
1506 assert id == to_string(image_post.id)
1509 test "gets a user's statuses without reblogs", %{conn: conn} do
1510 user = insert(:user)
1511 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1512 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1516 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1518 assert [%{"id" => id}] = json_response(conn, 200)
1519 assert id == to_string(post.id)
1523 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1525 assert [%{"id" => id}] = json_response(conn, 200)
1526 assert id == to_string(post.id)
1529 test "filters user's statuses by a hashtag", %{conn: conn} do
1530 user = insert(:user)
1531 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1532 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1536 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1538 assert [%{"id" => id}] = json_response(conn, 200)
1539 assert id == to_string(post.id)
1543 describe "user relationships" do
1544 test "returns the relationships for the current user", %{conn: conn} do
1545 user = insert(:user)
1546 other_user = insert(:user)
1547 {:ok, user} = User.follow(user, other_user)
1551 |> assign(:user, user)
1552 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1554 assert [relationship] = json_response(conn, 200)
1556 assert to_string(other_user.id) == relationship["id"]
1560 describe "media upload" do
1562 user = insert(:user)
1566 |> assign(:user, user)
1568 image = %Plug.Upload{
1569 content_type: "image/jpg",
1570 path: Path.absname("test/fixtures/image.jpg"),
1571 filename: "an_image.jpg"
1574 [conn: conn, image: image]
1577 clear_config([:media_proxy])
1578 clear_config([Pleroma.Upload])
1580 test "returns uploaded image", %{conn: conn, image: image} do
1581 desc = "Description of the image"
1585 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1586 |> json_response(:ok)
1588 assert media["type"] == "image"
1589 assert media["description"] == desc
1592 object = Repo.get(Object, media["id"])
1593 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1597 describe "locked accounts" do
1598 test "/api/v1/follow_requests works" do
1599 user = insert(:user, %{info: %User.Info{locked: true}})
1600 other_user = insert(:user)
1602 {:ok, _activity} = ActivityPub.follow(other_user, user)
1604 user = User.get_cached_by_id(user.id)
1605 other_user = User.get_cached_by_id(other_user.id)
1607 assert User.following?(other_user, user) == false
1611 |> assign(:user, user)
1612 |> get("/api/v1/follow_requests")
1614 assert [relationship] = json_response(conn, 200)
1615 assert to_string(other_user.id) == relationship["id"]
1618 test "/api/v1/follow_requests/:id/authorize works" do
1619 user = insert(:user, %{info: %User.Info{locked: true}})
1620 other_user = insert(:user)
1622 {:ok, _activity} = ActivityPub.follow(other_user, user)
1624 user = User.get_cached_by_id(user.id)
1625 other_user = User.get_cached_by_id(other_user.id)
1627 assert User.following?(other_user, user) == false
1631 |> assign(:user, user)
1632 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1634 assert relationship = json_response(conn, 200)
1635 assert to_string(other_user.id) == relationship["id"]
1637 user = User.get_cached_by_id(user.id)
1638 other_user = User.get_cached_by_id(other_user.id)
1640 assert User.following?(other_user, user) == true
1643 test "verify_credentials", %{conn: conn} do
1644 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1648 |> assign(:user, user)
1649 |> get("/api/v1/accounts/verify_credentials")
1651 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1652 assert id == to_string(user.id)
1655 test "/api/v1/follow_requests/:id/reject works" do
1656 user = insert(:user, %{info: %User.Info{locked: true}})
1657 other_user = insert(:user)
1659 {:ok, _activity} = ActivityPub.follow(other_user, user)
1661 user = User.get_cached_by_id(user.id)
1665 |> assign(:user, user)
1666 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1668 assert relationship = json_response(conn, 200)
1669 assert to_string(other_user.id) == relationship["id"]
1671 user = User.get_cached_by_id(user.id)
1672 other_user = User.get_cached_by_id(other_user.id)
1674 assert User.following?(other_user, user) == false
1678 test "account fetching", %{conn: conn} do
1679 user = insert(:user)
1683 |> get("/api/v1/accounts/#{user.id}")
1685 assert %{"id" => id} = json_response(conn, 200)
1686 assert id == to_string(user.id)
1690 |> get("/api/v1/accounts/-1")
1692 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1695 test "account fetching also works nickname", %{conn: conn} do
1696 user = insert(:user)
1700 |> get("/api/v1/accounts/#{user.nickname}")
1702 assert %{"id" => id} = json_response(conn, 200)
1703 assert id == user.id
1706 test "mascot upload", %{conn: conn} do
1707 user = insert(:user)
1709 non_image_file = %Plug.Upload{
1710 content_type: "audio/mpeg",
1711 path: Path.absname("test/fixtures/sound.mp3"),
1712 filename: "sound.mp3"
1717 |> assign(:user, user)
1718 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1720 assert json_response(conn, 415)
1722 file = %Plug.Upload{
1723 content_type: "image/jpg",
1724 path: Path.absname("test/fixtures/image.jpg"),
1725 filename: "an_image.jpg"
1730 |> assign(:user, user)
1731 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1733 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1736 test "mascot retrieving", %{conn: conn} do
1737 user = insert(:user)
1738 # When user hasn't set a mascot, we should just get pleroma tan back
1741 |> assign(:user, user)
1742 |> get("/api/v1/pleroma/mascot")
1744 assert %{"url" => url} = json_response(conn, 200)
1745 assert url =~ "pleroma-fox-tan-smol"
1747 # When a user sets their mascot, we should get that back
1748 file = %Plug.Upload{
1749 content_type: "image/jpg",
1750 path: Path.absname("test/fixtures/image.jpg"),
1751 filename: "an_image.jpg"
1756 |> assign(:user, user)
1757 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1759 assert json_response(conn, 200)
1761 user = User.get_cached_by_id(user.id)
1765 |> assign(:user, user)
1766 |> get("/api/v1/pleroma/mascot")
1768 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1769 assert url =~ "an_image"
1772 test "hashtag timeline", %{conn: conn} do
1773 following = insert(:user)
1776 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1778 {:ok, [_activity]} =
1779 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1783 |> get("/api/v1/timelines/tag/2hu")
1785 assert [%{"id" => id}] = json_response(nconn, 200)
1787 assert id == to_string(activity.id)
1789 # works for different capitalization too
1792 |> get("/api/v1/timelines/tag/2HU")
1794 assert [%{"id" => id}] = json_response(nconn, 200)
1796 assert id == to_string(activity.id)
1800 test "multi-hashtag timeline", %{conn: conn} do
1801 user = insert(:user)
1803 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1804 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1805 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1809 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1811 [status_none, status_test1, status_test] = json_response(any_test, 200)
1813 assert to_string(activity_test.id) == status_test["id"]
1814 assert to_string(activity_test1.id) == status_test1["id"]
1815 assert to_string(activity_none.id) == status_none["id"]
1819 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1821 assert [status_test1] == json_response(restricted_test, 200)
1823 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1825 assert [status_none] == json_response(all_test, 200)
1828 test "getting followers", %{conn: conn} do
1829 user = insert(:user)
1830 other_user = insert(:user)
1831 {:ok, user} = User.follow(user, other_user)
1835 |> get("/api/v1/accounts/#{other_user.id}/followers")
1837 assert [%{"id" => id}] = json_response(conn, 200)
1838 assert id == to_string(user.id)
1841 test "getting followers, hide_followers", %{conn: conn} do
1842 user = insert(:user)
1843 other_user = insert(:user, %{info: %{hide_followers: true}})
1844 {:ok, _user} = User.follow(user, other_user)
1848 |> get("/api/v1/accounts/#{other_user.id}/followers")
1850 assert [] == json_response(conn, 200)
1853 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1854 user = insert(:user)
1855 other_user = insert(:user, %{info: %{hide_followers: true}})
1856 {:ok, _user} = User.follow(user, other_user)
1860 |> assign(:user, other_user)
1861 |> get("/api/v1/accounts/#{other_user.id}/followers")
1863 refute [] == json_response(conn, 200)
1866 test "getting followers, pagination", %{conn: conn} do
1867 user = insert(:user)
1868 follower1 = insert(:user)
1869 follower2 = insert(:user)
1870 follower3 = insert(:user)
1871 {:ok, _} = User.follow(follower1, user)
1872 {:ok, _} = User.follow(follower2, user)
1873 {:ok, _} = User.follow(follower3, user)
1877 |> assign(:user, user)
1881 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1883 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1884 assert id3 == follower3.id
1885 assert id2 == follower2.id
1889 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1891 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1892 assert id2 == follower2.id
1893 assert id1 == follower1.id
1897 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1899 assert [%{"id" => id2}] = json_response(res_conn, 200)
1900 assert id2 == follower2.id
1902 assert [link_header] = get_resp_header(res_conn, "link")
1903 assert link_header =~ ~r/min_id=#{follower2.id}/
1904 assert link_header =~ ~r/max_id=#{follower2.id}/
1907 test "getting following", %{conn: conn} do
1908 user = insert(:user)
1909 other_user = insert(:user)
1910 {:ok, user} = User.follow(user, other_user)
1914 |> get("/api/v1/accounts/#{user.id}/following")
1916 assert [%{"id" => id}] = json_response(conn, 200)
1917 assert id == to_string(other_user.id)
1920 test "getting following, hide_follows", %{conn: conn} do
1921 user = insert(:user, %{info: %{hide_follows: true}})
1922 other_user = insert(:user)
1923 {:ok, user} = User.follow(user, other_user)
1927 |> get("/api/v1/accounts/#{user.id}/following")
1929 assert [] == json_response(conn, 200)
1932 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1933 user = insert(:user, %{info: %{hide_follows: true}})
1934 other_user = insert(:user)
1935 {:ok, user} = User.follow(user, other_user)
1939 |> assign(:user, user)
1940 |> get("/api/v1/accounts/#{user.id}/following")
1942 refute [] == json_response(conn, 200)
1945 test "getting following, pagination", %{conn: conn} do
1946 user = insert(:user)
1947 following1 = insert(:user)
1948 following2 = insert(:user)
1949 following3 = insert(:user)
1950 {:ok, _} = User.follow(user, following1)
1951 {:ok, _} = User.follow(user, following2)
1952 {:ok, _} = User.follow(user, following3)
1956 |> assign(:user, user)
1960 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1962 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1963 assert id3 == following3.id
1964 assert id2 == following2.id
1968 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1970 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1971 assert id2 == following2.id
1972 assert id1 == following1.id
1976 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1978 assert [%{"id" => id2}] = json_response(res_conn, 200)
1979 assert id2 == following2.id
1981 assert [link_header] = get_resp_header(res_conn, "link")
1982 assert link_header =~ ~r/min_id=#{following2.id}/
1983 assert link_header =~ ~r/max_id=#{following2.id}/
1986 test "following / unfollowing a user", %{conn: conn} do
1987 user = insert(:user)
1988 other_user = insert(:user)
1992 |> assign(:user, user)
1993 |> post("/api/v1/accounts/#{other_user.id}/follow")
1995 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1997 user = User.get_cached_by_id(user.id)
2001 |> assign(:user, user)
2002 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2004 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2006 user = User.get_cached_by_id(user.id)
2010 |> assign(:user, user)
2011 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2013 assert %{"id" => id} = json_response(conn, 200)
2014 assert id == to_string(other_user.id)
2017 test "following without reblogs" do
2018 follower = insert(:user)
2019 followed = insert(:user)
2020 other_user = insert(:user)
2024 |> assign(:user, follower)
2025 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2027 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2029 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2030 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2034 |> assign(:user, User.get_cached_by_id(follower.id))
2035 |> get("/api/v1/timelines/home")
2037 assert [] == json_response(conn, 200)
2041 |> assign(:user, follower)
2042 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2044 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2048 |> assign(:user, User.get_cached_by_id(follower.id))
2049 |> get("/api/v1/timelines/home")
2051 expected_activity_id = reblog.id
2052 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2055 test "following / unfollowing errors" do
2056 user = insert(:user)
2060 |> assign(:user, user)
2063 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2064 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2067 user = User.get_cached_by_id(user.id)
2068 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2069 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2071 # self follow via uri
2072 user = User.get_cached_by_id(user.id)
2073 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2074 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2076 # follow non existing user
2077 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2078 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2080 # follow non existing user via uri
2081 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2082 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2084 # unfollow non existing user
2085 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2086 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2089 describe "mute/unmute" do
2090 test "with notifications", %{conn: conn} do
2091 user = insert(:user)
2092 other_user = insert(:user)
2096 |> assign(:user, user)
2097 |> post("/api/v1/accounts/#{other_user.id}/mute")
2099 response = json_response(conn, 200)
2101 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2102 user = User.get_cached_by_id(user.id)
2106 |> assign(:user, user)
2107 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2109 response = json_response(conn, 200)
2110 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2113 test "without notifications", %{conn: conn} do
2114 user = insert(:user)
2115 other_user = insert(:user)
2119 |> assign(:user, user)
2120 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2122 response = json_response(conn, 200)
2124 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2125 user = User.get_cached_by_id(user.id)
2129 |> assign(:user, user)
2130 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2132 response = json_response(conn, 200)
2133 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2137 test "subscribing / unsubscribing to a user", %{conn: conn} do
2138 user = insert(:user)
2139 subscription_target = insert(:user)
2143 |> assign(:user, user)
2144 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2146 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2150 |> assign(:user, user)
2151 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2153 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2156 test "getting a list of mutes", %{conn: conn} do
2157 user = insert(:user)
2158 other_user = insert(:user)
2160 {:ok, user} = User.mute(user, other_user)
2164 |> assign(:user, user)
2165 |> get("/api/v1/mutes")
2167 other_user_id = to_string(other_user.id)
2168 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2171 test "blocking / unblocking a user", %{conn: conn} do
2172 user = insert(:user)
2173 other_user = insert(:user)
2177 |> assign(:user, user)
2178 |> post("/api/v1/accounts/#{other_user.id}/block")
2180 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2182 user = User.get_cached_by_id(user.id)
2186 |> assign(:user, user)
2187 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2189 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2192 test "getting a list of blocks", %{conn: conn} do
2193 user = insert(:user)
2194 other_user = insert(:user)
2196 {:ok, user} = User.block(user, other_user)
2200 |> assign(:user, user)
2201 |> get("/api/v1/blocks")
2203 other_user_id = to_string(other_user.id)
2204 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2207 test "blocking / unblocking a domain", %{conn: conn} do
2208 user = insert(:user)
2209 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2213 |> assign(:user, user)
2214 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2216 assert %{} = json_response(conn, 200)
2217 user = User.get_cached_by_ap_id(user.ap_id)
2218 assert User.blocks?(user, other_user)
2222 |> assign(:user, user)
2223 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2225 assert %{} = json_response(conn, 200)
2226 user = User.get_cached_by_ap_id(user.ap_id)
2227 refute User.blocks?(user, other_user)
2230 test "getting a list of domain blocks", %{conn: conn} do
2231 user = insert(:user)
2233 {:ok, user} = User.block_domain(user, "bad.site")
2234 {:ok, user} = User.block_domain(user, "even.worse.site")
2238 |> assign(:user, user)
2239 |> get("/api/v1/domain_blocks")
2241 domain_blocks = json_response(conn, 200)
2243 assert "bad.site" in domain_blocks
2244 assert "even.worse.site" in domain_blocks
2247 test "unimplemented follow_requests, blocks, domain blocks" do
2248 user = insert(:user)
2250 ["blocks", "domain_blocks", "follow_requests"]
2251 |> Enum.each(fn endpoint ->
2254 |> assign(:user, user)
2255 |> get("/api/v1/#{endpoint}")
2257 assert [] = json_response(conn, 200)
2261 test "returns the favorites of a user", %{conn: conn} do
2262 user = insert(:user)
2263 other_user = insert(:user)
2265 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2266 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2268 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2272 |> assign(:user, user)
2273 |> get("/api/v1/favourites")
2275 assert [status] = json_response(first_conn, 200)
2276 assert status["id"] == to_string(activity.id)
2278 assert [{"link", _link_header}] =
2279 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2281 # Honours query params
2282 {:ok, second_activity} =
2283 CommonAPI.post(other_user, %{
2285 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2288 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2290 last_like = status["id"]
2294 |> assign(:user, user)
2295 |> get("/api/v1/favourites?since_id=#{last_like}")
2297 assert [second_status] = json_response(second_conn, 200)
2298 assert second_status["id"] == to_string(second_activity.id)
2302 |> assign(:user, user)
2303 |> get("/api/v1/favourites?limit=0")
2305 assert [] = json_response(third_conn, 200)
2308 describe "getting favorites timeline of specified user" do
2310 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2311 [current_user: current_user, user: user]
2314 test "returns list of statuses favorited by specified user", %{
2316 current_user: current_user,
2319 [activity | _] = insert_pair(:note_activity)
2320 CommonAPI.favorite(activity.id, user)
2324 |> assign(:user, current_user)
2325 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2326 |> json_response(:ok)
2330 assert length(response) == 1
2331 assert like["id"] == activity.id
2334 test "returns favorites for specified user_id when user is not logged in", %{
2338 activity = insert(:note_activity)
2339 CommonAPI.favorite(activity.id, user)
2343 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2344 |> json_response(:ok)
2346 assert length(response) == 1
2349 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2351 current_user: current_user,
2355 CommonAPI.post(current_user, %{
2356 "status" => "Hi @#{user.nickname}!",
2357 "visibility" => "direct"
2360 CommonAPI.favorite(direct.id, user)
2364 |> assign(:user, current_user)
2365 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2366 |> json_response(:ok)
2368 assert length(response) == 1
2370 anonymous_response =
2372 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2373 |> json_response(:ok)
2375 assert Enum.empty?(anonymous_response)
2378 test "does not return others' favorited DM when user is not one of recipients", %{
2380 current_user: current_user,
2383 user_two = insert(:user)
2386 CommonAPI.post(user_two, %{
2387 "status" => "Hi @#{user.nickname}!",
2388 "visibility" => "direct"
2391 CommonAPI.favorite(direct.id, user)
2395 |> assign(:user, current_user)
2396 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2397 |> json_response(:ok)
2399 assert Enum.empty?(response)
2402 test "paginates favorites using since_id and max_id", %{
2404 current_user: current_user,
2407 activities = insert_list(10, :note_activity)
2409 Enum.each(activities, fn activity ->
2410 CommonAPI.favorite(activity.id, user)
2413 third_activity = Enum.at(activities, 2)
2414 seventh_activity = Enum.at(activities, 6)
2418 |> assign(:user, current_user)
2419 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2420 since_id: third_activity.id,
2421 max_id: seventh_activity.id
2423 |> json_response(:ok)
2425 assert length(response) == 3
2426 refute third_activity in response
2427 refute seventh_activity in response
2430 test "limits favorites using limit parameter", %{
2432 current_user: current_user,
2436 |> insert_list(:note_activity)
2437 |> Enum.each(fn activity ->
2438 CommonAPI.favorite(activity.id, user)
2443 |> assign(:user, current_user)
2444 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2445 |> json_response(:ok)
2447 assert length(response) == 3
2450 test "returns empty response when user does not have any favorited statuses", %{
2452 current_user: current_user,
2457 |> assign(:user, current_user)
2458 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2459 |> json_response(:ok)
2461 assert Enum.empty?(response)
2464 test "returns 404 error when specified user is not exist", %{conn: conn} do
2465 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2467 assert json_response(conn, 404) == %{"error" => "Record not found"}
2470 test "returns 403 error when user has hidden own favorites", %{
2472 current_user: current_user
2474 user = insert(:user, %{info: %{hide_favorites: true}})
2475 activity = insert(:note_activity)
2476 CommonAPI.favorite(activity.id, user)
2480 |> assign(:user, current_user)
2481 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2483 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2486 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2487 user = insert(:user)
2488 activity = insert(:note_activity)
2489 CommonAPI.favorite(activity.id, user)
2493 |> assign(:user, current_user)
2494 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2496 assert user.info.hide_favorites
2497 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2501 test "get instance information", %{conn: conn} do
2502 conn = get(conn, "/api/v1/instance")
2503 assert result = json_response(conn, 200)
2505 email = Config.get([:instance, :email])
2506 # Note: not checking for "max_toot_chars" since it's optional
2512 "email" => from_config_email,
2514 "streaming_api" => _
2519 "registrations" => _,
2523 assert email == from_config_email
2526 test "get instance stats", %{conn: conn} do
2527 user = insert(:user, %{local: true})
2529 user2 = insert(:user, %{local: true})
2530 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2532 insert(:user, %{local: false, nickname: "u@peer1.com"})
2533 insert(:user, %{local: false, nickname: "u@peer2.com"})
2535 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2537 # Stats should count users with missing or nil `info.deactivated` value
2538 user = User.get_cached_by_id(user.id)
2539 info_change = Changeset.change(user.info, %{deactivated: nil})
2543 |> Changeset.change()
2544 |> Changeset.put_embed(:info, info_change)
2545 |> User.update_and_set_cache()
2547 Pleroma.Stats.force_update()
2549 conn = get(conn, "/api/v1/instance")
2551 assert result = json_response(conn, 200)
2553 stats = result["stats"]
2556 assert stats["user_count"] == 1
2557 assert stats["status_count"] == 1
2558 assert stats["domain_count"] == 2
2561 test "get peers", %{conn: conn} do
2562 insert(:user, %{local: false, nickname: "u@peer1.com"})
2563 insert(:user, %{local: false, nickname: "u@peer2.com"})
2565 Pleroma.Stats.force_update()
2567 conn = get(conn, "/api/v1/instance/peers")
2569 assert result = json_response(conn, 200)
2571 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2574 test "put settings", %{conn: conn} do
2575 user = insert(:user)
2579 |> assign(:user, user)
2580 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2582 assert _result = json_response(conn, 200)
2584 user = User.get_cached_by_ap_id(user.ap_id)
2585 assert user.info.settings == %{"programming" => "socks"}
2588 describe "pinned statuses" do
2590 user = insert(:user)
2591 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2593 [user: user, activity: activity]
2596 clear_config([:instance, :max_pinned_statuses]) do
2597 Config.put([:instance, :max_pinned_statuses], 1)
2600 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2601 {:ok, _} = CommonAPI.pin(activity.id, user)
2605 |> assign(:user, user)
2606 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2607 |> json_response(200)
2609 id_str = to_string(activity.id)
2611 assert [%{"id" => ^id_str, "pinned" => true}] = result
2614 test "pin status", %{conn: conn, user: user, activity: activity} do
2615 id_str = to_string(activity.id)
2617 assert %{"id" => ^id_str, "pinned" => true} =
2619 |> assign(:user, user)
2620 |> post("/api/v1/statuses/#{activity.id}/pin")
2621 |> json_response(200)
2623 assert [%{"id" => ^id_str, "pinned" => true}] =
2625 |> assign(:user, user)
2626 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2627 |> json_response(200)
2630 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2631 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2635 |> assign(:user, user)
2636 |> post("/api/v1/statuses/#{dm.id}/pin")
2638 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2641 test "unpin status", %{conn: conn, user: user, activity: activity} do
2642 {:ok, _} = CommonAPI.pin(activity.id, user)
2644 id_str = to_string(activity.id)
2645 user = refresh_record(user)
2647 assert %{"id" => ^id_str, "pinned" => false} =
2649 |> assign(:user, user)
2650 |> post("/api/v1/statuses/#{activity.id}/unpin")
2651 |> json_response(200)
2655 |> assign(:user, user)
2656 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2657 |> json_response(200)
2660 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2663 |> assign(:user, user)
2664 |> post("/api/v1/statuses/1/unpin")
2666 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2669 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2670 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2672 id_str_one = to_string(activity_one.id)
2674 assert %{"id" => ^id_str_one, "pinned" => true} =
2676 |> assign(:user, user)
2677 |> post("/api/v1/statuses/#{id_str_one}/pin")
2678 |> json_response(200)
2680 user = refresh_record(user)
2682 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2684 |> assign(:user, user)
2685 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2686 |> json_response(400)
2692 Config.put([:rich_media, :enabled], true)
2694 user = insert(:user)
2698 test "returns rich-media card", %{conn: conn, user: user} do
2699 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2702 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2703 "provider_name" => "example.com",
2704 "provider_url" => "https://example.com",
2705 "title" => "The Rock",
2707 "url" => "https://example.com/ogp",
2709 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2712 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2713 "title" => "The Rock",
2714 "type" => "video.movie",
2715 "url" => "https://example.com/ogp",
2717 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2724 |> get("/api/v1/statuses/#{activity.id}/card")
2725 |> json_response(200)
2727 assert response == card_data
2729 # works with private posts
2731 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2735 |> assign(:user, user)
2736 |> get("/api/v1/statuses/#{activity.id}/card")
2737 |> json_response(200)
2739 assert response_two == card_data
2742 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2744 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2748 |> get("/api/v1/statuses/#{activity.id}/card")
2749 |> json_response(:ok)
2751 assert response == %{
2753 "title" => "Pleroma",
2754 "description" => "",
2756 "provider_name" => "example.com",
2757 "provider_url" => "https://example.com",
2758 "url" => "https://example.com/ogp-missing-data",
2761 "title" => "Pleroma",
2762 "type" => "website",
2763 "url" => "https://example.com/ogp-missing-data"
2771 user = insert(:user)
2772 for_user = insert(:user)
2775 CommonAPI.post(user, %{
2776 "status" => "heweoo?"
2780 CommonAPI.post(user, %{
2781 "status" => "heweoo!"
2786 |> assign(:user, for_user)
2787 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2789 assert json_response(response1, 200)["bookmarked"] == true
2793 |> assign(:user, for_user)
2794 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2796 assert json_response(response2, 200)["bookmarked"] == true
2800 |> assign(:user, for_user)
2801 |> get("/api/v1/bookmarks")
2803 assert [json_response(response2, 200), json_response(response1, 200)] ==
2804 json_response(bookmarks, 200)
2808 |> assign(:user, for_user)
2809 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2811 assert json_response(response1, 200)["bookmarked"] == false
2815 |> assign(:user, for_user)
2816 |> get("/api/v1/bookmarks")
2818 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2821 describe "conversation muting" do
2823 post_user = insert(:user)
2824 user = insert(:user)
2826 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2828 [user: user, activity: activity]
2831 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2832 id_str = to_string(activity.id)
2834 assert %{"id" => ^id_str, "muted" => true} =
2836 |> assign(:user, user)
2837 |> post("/api/v1/statuses/#{activity.id}/mute")
2838 |> json_response(200)
2841 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2842 {:ok, _} = CommonAPI.add_mute(user, activity)
2846 |> assign(:user, user)
2847 |> post("/api/v1/statuses/#{activity.id}/mute")
2849 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2852 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2853 {:ok, _} = CommonAPI.add_mute(user, activity)
2855 id_str = to_string(activity.id)
2856 user = refresh_record(user)
2858 assert %{"id" => ^id_str, "muted" => false} =
2860 |> assign(:user, user)
2861 |> post("/api/v1/statuses/#{activity.id}/unmute")
2862 |> json_response(200)
2866 describe "reports" do
2868 reporter = insert(:user)
2869 target_user = insert(:user)
2871 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2873 [reporter: reporter, target_user: target_user, activity: activity]
2876 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2877 assert %{"action_taken" => false, "id" => _} =
2879 |> assign(:user, reporter)
2880 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2881 |> json_response(200)
2884 test "submit a report with statuses and comment", %{
2887 target_user: target_user,
2890 assert %{"action_taken" => false, "id" => _} =
2892 |> assign(:user, reporter)
2893 |> post("/api/v1/reports", %{
2894 "account_id" => target_user.id,
2895 "status_ids" => [activity.id],
2896 "comment" => "bad status!",
2897 "forward" => "false"
2899 |> json_response(200)
2902 test "account_id is required", %{
2907 assert %{"error" => "Valid `account_id` required"} =
2909 |> assign(:user, reporter)
2910 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2911 |> json_response(400)
2914 test "comment must be up to the size specified in the config", %{
2917 target_user: target_user
2919 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2920 comment = String.pad_trailing("a", max_size + 1, "a")
2922 error = %{"error" => "Comment must be up to #{max_size} characters"}
2926 |> assign(:user, reporter)
2927 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2928 |> json_response(400)
2931 test "returns error when account is not exist", %{
2938 |> assign(:user, reporter)
2939 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
2941 assert json_response(conn, 400) == %{"error" => "Account not found"}
2945 describe "link headers" do
2946 test "preserves parameters in link headers", %{conn: conn} do
2947 user = insert(:user)
2948 other_user = insert(:user)
2951 CommonAPI.post(other_user, %{
2952 "status" => "hi @#{user.nickname}",
2953 "visibility" => "public"
2957 CommonAPI.post(other_user, %{
2958 "status" => "hi @#{user.nickname}",
2959 "visibility" => "public"
2962 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2963 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2967 |> assign(:user, user)
2968 |> get("/api/v1/notifications", %{media_only: true})
2970 assert [link_header] = get_resp_header(conn, "link")
2971 assert link_header =~ ~r/media_only=true/
2972 assert link_header =~ ~r/min_id=#{notification2.id}/
2973 assert link_header =~ ~r/max_id=#{notification1.id}/
2977 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2978 # Need to set an old-style integer ID to reproduce the problem
2979 # (these are no longer assigned to new accounts but were preserved
2980 # for existing accounts during the migration to flakeIDs)
2981 user_one = insert(:user, %{id: 1212})
2982 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2986 |> get("/api/v1/accounts/#{user_one.id}")
2990 |> get("/api/v1/accounts/#{user_two.nickname}")
2994 |> get("/api/v1/accounts/#{user_two.id}")
2996 acc_one = json_response(resp_one, 200)
2997 acc_two = json_response(resp_two, 200)
2998 acc_three = json_response(resp_three, 200)
2999 refute acc_one == acc_two
3000 assert acc_two == acc_three
3003 describe "custom emoji" do
3004 test "with tags", %{conn: conn} do
3007 |> get("/api/v1/custom_emojis")
3008 |> json_response(200)
3010 assert Map.has_key?(emoji, "shortcode")
3011 assert Map.has_key?(emoji, "static_url")
3012 assert Map.has_key?(emoji, "tags")
3013 assert is_list(emoji["tags"])
3014 assert Map.has_key?(emoji, "category")
3015 assert Map.has_key?(emoji, "url")
3016 assert Map.has_key?(emoji, "visible_in_picker")
3020 describe "index/2 redirections" do
3021 setup %{conn: conn} do
3025 signing_salt: "cooldude"
3030 |> Plug.Session.call(Plug.Session.init(session_opts))
3033 test_path = "/web/statuses/test"
3034 %{conn: conn, path: test_path}
3037 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3038 conn = get(conn, path)
3040 assert conn.status == 302
3041 assert redirected_to(conn) == "/web/login"
3044 test "redirects not logged-in users to the login page on private instances", %{
3048 Config.put([:instance, :public], false)
3050 conn = get(conn, path)
3052 assert conn.status == 302
3053 assert redirected_to(conn) == "/web/login"
3056 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3057 token = insert(:oauth_token)
3061 |> assign(:user, token.user)
3062 |> put_session(:oauth_token, token.token)
3065 assert conn.status == 200
3068 test "saves referer path to session", %{conn: conn, path: path} do
3069 conn = get(conn, path)
3070 return_to = Plug.Conn.get_session(conn, :return_to)
3072 assert return_to == path
3075 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3076 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3077 auth = insert(:oauth_authorization, app: app)
3081 |> put_session(:return_to, path)
3082 |> get("/web/login", %{code: auth.token})
3084 assert conn.status == 302
3085 assert redirected_to(conn) == path
3088 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3089 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3090 auth = insert(:oauth_authorization, app: app)
3092 conn = get(conn, "/web/login", %{code: auth.token})
3094 assert conn.status == 302
3095 assert redirected_to(conn) == "/web/getting-started"
3099 describe "scheduled activities" do
3100 test "creates a scheduled activity", %{conn: conn} do
3101 user = insert(:user)
3102 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3106 |> assign(:user, user)
3107 |> post("/api/v1/statuses", %{
3108 "status" => "scheduled",
3109 "scheduled_at" => scheduled_at
3112 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3113 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3114 assert [] == Repo.all(Activity)
3117 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3118 user = insert(:user)
3119 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3121 file = %Plug.Upload{
3122 content_type: "image/jpg",
3123 path: Path.absname("test/fixtures/image.jpg"),
3124 filename: "an_image.jpg"
3127 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3131 |> assign(:user, user)
3132 |> post("/api/v1/statuses", %{
3133 "media_ids" => [to_string(upload.id)],
3134 "status" => "scheduled",
3135 "scheduled_at" => scheduled_at
3138 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3139 assert %{"type" => "image"} = media_attachment
3142 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3144 user = insert(:user)
3147 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3151 |> assign(:user, user)
3152 |> post("/api/v1/statuses", %{
3153 "status" => "not scheduled",
3154 "scheduled_at" => scheduled_at
3157 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3158 assert [] == Repo.all(ScheduledActivity)
3161 test "returns error when daily user limit is exceeded", %{conn: conn} do
3162 user = insert(:user)
3165 NaiveDateTime.utc_now()
3166 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3167 |> NaiveDateTime.to_iso8601()
3169 attrs = %{params: %{}, scheduled_at: today}
3170 {:ok, _} = ScheduledActivity.create(user, attrs)
3171 {:ok, _} = ScheduledActivity.create(user, attrs)
3175 |> assign(:user, user)
3176 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3178 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3181 test "returns error when total user limit is exceeded", %{conn: conn} do
3182 user = insert(:user)
3185 NaiveDateTime.utc_now()
3186 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3187 |> NaiveDateTime.to_iso8601()
3190 NaiveDateTime.utc_now()
3191 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3192 |> NaiveDateTime.to_iso8601()
3194 attrs = %{params: %{}, scheduled_at: today}
3195 {:ok, _} = ScheduledActivity.create(user, attrs)
3196 {:ok, _} = ScheduledActivity.create(user, attrs)
3197 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3201 |> assign(:user, user)
3202 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3204 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3207 test "shows scheduled activities", %{conn: conn} do
3208 user = insert(:user)
3209 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3210 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3211 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3212 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3216 |> assign(:user, user)
3221 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3223 result = json_response(conn_res, 200)
3224 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3229 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3231 result = json_response(conn_res, 200)
3232 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3237 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3239 result = json_response(conn_res, 200)
3240 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3243 test "shows a scheduled activity", %{conn: conn} do
3244 user = insert(:user)
3245 scheduled_activity = insert(:scheduled_activity, user: user)
3249 |> assign(:user, user)
3250 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3252 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3253 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3257 |> assign(:user, user)
3258 |> get("/api/v1/scheduled_statuses/404")
3260 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3263 test "updates a scheduled activity", %{conn: conn} do
3264 user = insert(:user)
3265 scheduled_activity = insert(:scheduled_activity, user: user)
3268 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3272 |> assign(:user, user)
3273 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3274 scheduled_at: new_scheduled_at
3277 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3278 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3282 |> assign(:user, user)
3283 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3285 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3288 test "deletes a scheduled activity", %{conn: conn} do
3289 user = insert(:user)
3290 scheduled_activity = insert(:scheduled_activity, user: user)
3294 |> assign(:user, user)
3295 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3297 assert %{} = json_response(res_conn, 200)
3298 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3302 |> assign(:user, user)
3303 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3305 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3309 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3310 user1 = insert(:user)
3311 user2 = insert(:user)
3312 user3 = insert(:user)
3314 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3316 # Reply to status from another user
3319 |> assign(:user, user2)
3320 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3322 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3324 activity = Activity.get_by_id_with_object(id)
3326 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3327 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3329 # Reblog from the third user
3332 |> assign(:user, user3)
3333 |> post("/api/v1/statuses/#{activity.id}/reblog")
3335 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3336 json_response(conn2, 200)
3338 assert to_string(activity.id) == id
3340 # Getting third user status
3343 |> assign(:user, user3)
3344 |> get("api/v1/timelines/home")
3346 [reblogged_activity] = json_response(conn3, 200)
3348 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3350 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3351 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3354 describe "create account by app" do
3355 test "Account registration via Application", %{conn: conn} do
3358 |> post("/api/v1/apps", %{
3359 client_name: "client_name",
3360 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3361 scopes: "read, write, follow"
3365 "client_id" => client_id,
3366 "client_secret" => client_secret,
3368 "name" => "client_name",
3369 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3372 } = json_response(conn, 200)
3376 |> post("/oauth/token", %{
3377 grant_type: "client_credentials",
3378 client_id: client_id,
3379 client_secret: client_secret
3382 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3383 json_response(conn, 200)
3386 token_from_db = Repo.get_by(Token, token: token)
3387 assert token_from_db
3389 assert scope == "read write follow"
3393 |> put_req_header("authorization", "Bearer " <> token)
3394 |> post("/api/v1/accounts", %{
3396 email: "lain@example.org",
3397 password: "PlzDontHackLain",
3402 "access_token" => token,
3403 "created_at" => _created_at,
3405 "token_type" => "Bearer"
3406 } = json_response(conn, 200)
3408 token_from_db = Repo.get_by(Token, token: token)
3409 assert token_from_db
3410 token_from_db = Repo.preload(token_from_db, :user)
3411 assert token_from_db.user
3413 assert token_from_db.user.info.confirmation_pending
3416 test "rate limit", %{conn: conn} do
3417 app_token = insert(:oauth_token, user: nil)
3420 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3421 |> Map.put(:remote_ip, {15, 15, 15, 15})
3426 |> post("/api/v1/accounts", %{
3427 username: "#{i}lain",
3428 email: "#{i}lain@example.org",
3429 password: "PlzDontHackLain",
3434 "access_token" => token,
3435 "created_at" => _created_at,
3437 "token_type" => "Bearer"
3438 } = json_response(conn, 200)
3440 token_from_db = Repo.get_by(Token, token: token)
3441 assert token_from_db
3442 token_from_db = Repo.preload(token_from_db, :user)
3443 assert token_from_db.user
3445 assert token_from_db.user.info.confirmation_pending
3450 |> post("/api/v1/accounts", %{
3452 email: "6lain@example.org",
3453 password: "PlzDontHackLain",
3457 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3461 describe "GET /api/v1/polls/:id" do
3462 test "returns poll entity for object id", %{conn: conn} do
3463 user = insert(:user)
3466 CommonAPI.post(user, %{
3467 "status" => "Pleroma does",
3468 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3471 object = Object.normalize(activity)
3475 |> assign(:user, user)
3476 |> get("/api/v1/polls/#{object.id}")
3478 response = json_response(conn, 200)
3479 id = to_string(object.id)
3480 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3483 test "does not expose polls for private statuses", %{conn: conn} do
3484 user = insert(:user)
3485 other_user = insert(:user)
3488 CommonAPI.post(user, %{
3489 "status" => "Pleroma does",
3490 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3491 "visibility" => "private"
3494 object = Object.normalize(activity)
3498 |> assign(:user, other_user)
3499 |> get("/api/v1/polls/#{object.id}")
3501 assert json_response(conn, 404)
3505 describe "POST /api/v1/polls/:id/votes" do
3506 test "votes are added to the poll", %{conn: conn} do
3507 user = insert(:user)
3508 other_user = insert(:user)
3511 CommonAPI.post(user, %{
3512 "status" => "A very delicious sandwich",
3514 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3520 object = Object.normalize(activity)
3524 |> assign(:user, other_user)
3525 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3527 assert json_response(conn, 200)
3528 object = Object.get_by_id(object.id)
3530 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3535 test "author can't vote", %{conn: conn} do
3536 user = insert(:user)
3539 CommonAPI.post(user, %{
3540 "status" => "Am I cute?",
3541 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3544 object = Object.normalize(activity)
3547 |> assign(:user, user)
3548 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3549 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3551 object = Object.get_by_id(object.id)
3553 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3556 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3557 user = insert(:user)
3558 other_user = insert(:user)
3561 CommonAPI.post(user, %{
3562 "status" => "The glass is",
3563 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3566 object = Object.normalize(activity)
3569 |> assign(:user, other_user)
3570 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3571 |> json_response(422) == %{"error" => "Too many choices"}
3573 object = Object.get_by_id(object.id)
3575 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3580 test "does not allow choice index to be greater than options count", %{conn: conn} do
3581 user = insert(:user)
3582 other_user = insert(:user)
3585 CommonAPI.post(user, %{
3586 "status" => "Am I cute?",
3587 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3590 object = Object.normalize(activity)
3594 |> assign(:user, other_user)
3595 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3597 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3600 test "returns 404 error when object is not exist", %{conn: conn} do
3601 user = insert(:user)
3605 |> assign(:user, user)
3606 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3608 assert json_response(conn, 404) == %{"error" => "Record not found"}
3611 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3612 user = insert(:user)
3613 other_user = insert(:user)
3616 CommonAPI.post(user, %{
3617 "status" => "Am I cute?",
3618 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3619 "visibility" => "private"
3622 object = Object.normalize(activity)
3626 |> assign(:user, other_user)
3627 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3629 assert json_response(conn, 404) == %{"error" => "Record not found"}
3633 describe "GET /api/v1/statuses/:id/favourited_by" do
3635 user = insert(:user)
3636 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3640 |> assign(:user, user)
3642 [conn: conn, activity: activity]
3645 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3646 other_user = insert(:user)
3647 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3651 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3652 |> json_response(:ok)
3654 [%{"id" => id}] = response
3656 assert id == other_user.id
3659 test "returns empty array when status has not been favorited yet", %{
3665 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3666 |> json_response(:ok)
3668 assert Enum.empty?(response)
3671 test "does not return users who have favorited the status but are blocked", %{
3672 conn: %{assigns: %{user: user}} = conn,
3675 other_user = insert(:user)
3676 {:ok, user} = User.block(user, other_user)
3678 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3682 |> assign(:user, user)
3683 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3684 |> json_response(:ok)
3686 assert Enum.empty?(response)
3689 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3690 other_user = insert(:user)
3691 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3695 |> assign(:user, nil)
3696 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3697 |> json_response(:ok)
3699 [%{"id" => id}] = response
3700 assert id == other_user.id
3704 describe "GET /api/v1/statuses/:id/reblogged_by" do
3706 user = insert(:user)
3707 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3711 |> assign(:user, user)
3713 [conn: conn, activity: activity]
3716 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3717 other_user = insert(:user)
3718 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3722 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3723 |> json_response(:ok)
3725 [%{"id" => id}] = response
3727 assert id == other_user.id
3730 test "returns empty array when status has not been reblogged yet", %{
3736 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3737 |> json_response(:ok)
3739 assert Enum.empty?(response)
3742 test "does not return users who have reblogged the status but are blocked", %{
3743 conn: %{assigns: %{user: user}} = conn,
3746 other_user = insert(:user)
3747 {:ok, user} = User.block(user, other_user)
3749 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3753 |> assign(:user, user)
3754 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3755 |> json_response(:ok)
3757 assert Enum.empty?(response)
3760 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3761 other_user = insert(:user)
3762 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3766 |> assign(:user, nil)
3767 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3768 |> json_response(:ok)
3770 [%{"id" => id}] = response
3771 assert id == other_user.id
3775 describe "POST /auth/password, with valid parameters" do
3776 setup %{conn: conn} do
3777 user = insert(:user)
3778 conn = post(conn, "/auth/password?email=#{user.email}")
3779 %{conn: conn, user: user}
3782 test "it returns 204", %{conn: conn} do
3783 assert json_response(conn, :no_content)
3786 test "it creates a PasswordResetToken record for user", %{user: user} do
3787 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3791 test "it sends an email to user", %{user: user} do
3792 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3794 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3795 notify_email = Config.get([:instance, :notify_email])
3796 instance_name = Config.get([:instance, :name])
3799 from: {instance_name, notify_email},
3800 to: {user.name, user.email},
3801 html_body: email.html_body
3806 describe "POST /auth/password, with invalid parameters" do
3808 user = insert(:user)
3812 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3813 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3814 assert conn.status == 404
3815 assert conn.resp_body == ""
3818 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3819 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3820 conn = post(conn, "/auth/password?email=#{user.email}")
3821 assert conn.status == 400
3822 assert conn.resp_body == ""
3826 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3828 user = insert(:user)
3829 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3833 |> Changeset.change()
3834 |> Changeset.put_embed(:info, info_change)
3837 assert user.info.confirmation_pending
3842 clear_config([:instance, :account_activation_required]) do
3843 Config.put([:instance, :account_activation_required], true)
3846 test "resend account confirmation email", %{conn: conn, user: user} do
3848 |> assign(:user, user)
3849 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3850 |> json_response(:no_content)
3852 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3853 notify_email = Config.get([:instance, :notify_email])
3854 instance_name = Config.get([:instance, :name])
3857 from: {instance_name, notify_email},
3858 to: {user.name, user.email},
3859 html_body: email.html_body
3864 describe "GET /api/v1/suggestions" do
3866 user = insert(:user)
3867 other_user = insert(:user)
3868 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3869 url500 = "http://test500?#{host}&#{user.nickname}"
3870 url200 = "http://test200?#{host}&#{user.nickname}"
3873 %{method: :get, url: ^url500} ->
3874 %Tesla.Env{status: 500, body: "bad request"}
3876 %{method: :get, url: ^url200} ->
3880 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3882 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3886 [user: user, other_user: other_user]
3889 clear_config(:suggestions)
3891 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3892 Config.put([:suggestions, :enabled], false)
3896 |> assign(:user, user)
3897 |> get("/api/v1/suggestions")
3898 |> json_response(200)
3903 test "returns error", %{conn: conn, user: user} do
3904 Config.put([:suggestions, :enabled], true)
3905 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
3909 |> assign(:user, user)
3910 |> get("/api/v1/suggestions")
3911 |> json_response(500)
3913 assert res == "Something went wrong"
3916 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
3917 Config.put([:suggestions, :enabled], true)
3918 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
3922 |> assign(:user, user)
3923 |> get("/api/v1/suggestions")
3924 |> json_response(200)
3929 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
3930 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
3934 "acct" => other_user.ap_id,
3935 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
3936 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
3937 "id" => other_user.id