1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.ActivityExpiration
12 alias Pleroma.Notification
15 alias Pleroma.ScheduledActivity
16 alias Pleroma.Tests.ObanHelpers
18 alias Pleroma.Web.ActivityPub.ActivityPub
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.MastodonAPI.FilterView
21 alias Pleroma.Web.OAuth.App
22 alias Pleroma.Web.OAuth.Token
23 alias Pleroma.Web.OStatus
24 alias Pleroma.Web.Push
25 import Pleroma.Factory
26 import ExUnit.CaptureLog
28 import Swoosh.TestAssertions
30 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
33 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
37 clear_config([:instance, :public])
38 clear_config([:rich_media, :enabled])
40 test "the home timeline", %{conn: conn} do
42 following = insert(:user)
44 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
51 assert Enum.empty?(json_response(conn, 200))
53 {:ok, user} = User.follow(user, following)
57 |> assign(:user, user)
58 |> get("/api/v1/timelines/home")
60 assert [%{"content" => "test"}] = json_response(conn, 200)
63 test "the public timeline", %{conn: conn} do
64 following = insert(:user)
67 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
70 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
74 |> get("/api/v1/timelines/public", %{"local" => "False"})
76 assert length(json_response(conn, 200)) == 2
80 |> get("/api/v1/timelines/public", %{"local" => "True"})
82 assert [%{"content" => "test"}] = json_response(conn, 200)
86 |> get("/api/v1/timelines/public", %{"local" => "1"})
88 assert [%{"content" => "test"}] = json_response(conn, 200)
92 test "the public timeline when public is set to false", %{conn: conn} do
93 Config.put([:instance, :public], false)
96 |> get("/api/v1/timelines/public", %{"local" => "False"})
97 |> json_response(403) == %{"error" => "This resource requires authentication."}
100 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"
1487 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1489 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1493 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1495 assert [%{"id" => id}] = json_response(conn, 200)
1496 assert id == to_string(image_post.id)
1500 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1502 assert [%{"id" => id}] = json_response(conn, 200)
1503 assert id == to_string(image_post.id)
1506 test "gets a user's statuses without reblogs", %{conn: conn} do
1507 user = insert(:user)
1508 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1509 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1513 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1515 assert [%{"id" => id}] = json_response(conn, 200)
1516 assert id == to_string(post.id)
1520 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1522 assert [%{"id" => id}] = json_response(conn, 200)
1523 assert id == to_string(post.id)
1526 test "filters user's statuses by a hashtag", %{conn: conn} do
1527 user = insert(:user)
1528 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1529 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1533 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1535 assert [%{"id" => id}] = json_response(conn, 200)
1536 assert id == to_string(post.id)
1540 describe "user relationships" do
1541 test "returns the relationships for the current user", %{conn: conn} do
1542 user = insert(:user)
1543 other_user = insert(:user)
1544 {:ok, user} = User.follow(user, other_user)
1548 |> assign(:user, user)
1549 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1551 assert [relationship] = json_response(conn, 200)
1553 assert to_string(other_user.id) == relationship["id"]
1557 describe "media upload" do
1559 user = insert(:user)
1563 |> assign(:user, user)
1565 image = %Plug.Upload{
1566 content_type: "image/jpg",
1567 path: Path.absname("test/fixtures/image.jpg"),
1568 filename: "an_image.jpg"
1571 [conn: conn, image: image]
1574 clear_config([:media_proxy])
1575 clear_config([Pleroma.Upload])
1577 test "returns uploaded image", %{conn: conn, image: image} do
1578 desc = "Description of the image"
1582 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1583 |> json_response(:ok)
1585 assert media["type"] == "image"
1586 assert media["description"] == desc
1589 object = Repo.get(Object, media["id"])
1590 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1594 describe "locked accounts" do
1595 test "/api/v1/follow_requests works" do
1596 user = insert(:user, %{info: %User.Info{locked: true}})
1597 other_user = insert(:user)
1599 {:ok, _activity} = ActivityPub.follow(other_user, user)
1601 user = User.get_cached_by_id(user.id)
1602 other_user = User.get_cached_by_id(other_user.id)
1604 assert User.following?(other_user, user) == false
1608 |> assign(:user, user)
1609 |> get("/api/v1/follow_requests")
1611 assert [relationship] = json_response(conn, 200)
1612 assert to_string(other_user.id) == relationship["id"]
1615 test "/api/v1/follow_requests/:id/authorize works" do
1616 user = insert(:user, %{info: %User.Info{locked: true}})
1617 other_user = insert(:user)
1619 {:ok, _activity} = ActivityPub.follow(other_user, user)
1621 user = User.get_cached_by_id(user.id)
1622 other_user = User.get_cached_by_id(other_user.id)
1624 assert User.following?(other_user, user) == false
1628 |> assign(:user, user)
1629 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1631 assert relationship = json_response(conn, 200)
1632 assert to_string(other_user.id) == relationship["id"]
1634 user = User.get_cached_by_id(user.id)
1635 other_user = User.get_cached_by_id(other_user.id)
1637 assert User.following?(other_user, user) == true
1640 test "verify_credentials", %{conn: conn} do
1641 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1645 |> assign(:user, user)
1646 |> get("/api/v1/accounts/verify_credentials")
1648 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1649 assert id == to_string(user.id)
1652 test "/api/v1/follow_requests/:id/reject works" do
1653 user = insert(:user, %{info: %User.Info{locked: true}})
1654 other_user = insert(:user)
1656 {:ok, _activity} = ActivityPub.follow(other_user, user)
1658 user = User.get_cached_by_id(user.id)
1662 |> assign(:user, user)
1663 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1665 assert relationship = json_response(conn, 200)
1666 assert to_string(other_user.id) == relationship["id"]
1668 user = User.get_cached_by_id(user.id)
1669 other_user = User.get_cached_by_id(other_user.id)
1671 assert User.following?(other_user, user) == false
1675 describe "account fetching" do
1676 test "works by id" do
1677 user = insert(:user)
1681 |> get("/api/v1/accounts/#{user.id}")
1683 assert %{"id" => id} = json_response(conn, 200)
1684 assert id == to_string(user.id)
1688 |> get("/api/v1/accounts/-1")
1690 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1693 test "works by nickname" do
1694 user = insert(:user)
1698 |> get("/api/v1/accounts/#{user.nickname}")
1700 assert %{"id" => id} = json_response(conn, 200)
1701 assert id == user.id
1704 test "works by nickname for remote users" do
1705 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1706 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1707 user = insert(:user, nickname: "user@example.com", local: false)
1711 |> get("/api/v1/accounts/#{user.nickname}")
1713 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1714 assert %{"id" => id} = json_response(conn, 200)
1715 assert id == user.id
1718 test "respects limit_to_local_content == :all for remote user nicknames" do
1719 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1720 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1722 user = insert(:user, nickname: "user@example.com", local: false)
1726 |> get("/api/v1/accounts/#{user.nickname}")
1728 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1729 assert json_response(conn, 404)
1732 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1733 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1734 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1736 user = insert(:user, nickname: "user@example.com", local: false)
1737 reading_user = insert(:user)
1741 |> get("/api/v1/accounts/#{user.nickname}")
1743 assert json_response(conn, 404)
1747 |> assign(:user, reading_user)
1748 |> get("/api/v1/accounts/#{user.nickname}")
1750 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1751 assert %{"id" => id} = json_response(conn, 200)
1752 assert id == user.id
1756 test "mascot upload", %{conn: conn} do
1757 user = insert(:user)
1759 non_image_file = %Plug.Upload{
1760 content_type: "audio/mpeg",
1761 path: Path.absname("test/fixtures/sound.mp3"),
1762 filename: "sound.mp3"
1767 |> assign(:user, user)
1768 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1770 assert json_response(conn, 415)
1772 file = %Plug.Upload{
1773 content_type: "image/jpg",
1774 path: Path.absname("test/fixtures/image.jpg"),
1775 filename: "an_image.jpg"
1780 |> assign(:user, user)
1781 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1783 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1786 test "mascot retrieving", %{conn: conn} do
1787 user = insert(:user)
1788 # When user hasn't set a mascot, we should just get pleroma tan back
1791 |> assign(:user, user)
1792 |> get("/api/v1/pleroma/mascot")
1794 assert %{"url" => url} = json_response(conn, 200)
1795 assert url =~ "pleroma-fox-tan-smol"
1797 # When a user sets their mascot, we should get that back
1798 file = %Plug.Upload{
1799 content_type: "image/jpg",
1800 path: Path.absname("test/fixtures/image.jpg"),
1801 filename: "an_image.jpg"
1806 |> assign(:user, user)
1807 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1809 assert json_response(conn, 200)
1811 user = User.get_cached_by_id(user.id)
1815 |> assign(:user, user)
1816 |> get("/api/v1/pleroma/mascot")
1818 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1819 assert url =~ "an_image"
1822 test "hashtag timeline", %{conn: conn} do
1823 following = insert(:user)
1826 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1828 {:ok, [_activity]} =
1829 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1833 |> get("/api/v1/timelines/tag/2hu")
1835 assert [%{"id" => id}] = json_response(nconn, 200)
1837 assert id == to_string(activity.id)
1839 # works for different capitalization too
1842 |> get("/api/v1/timelines/tag/2HU")
1844 assert [%{"id" => id}] = json_response(nconn, 200)
1846 assert id == to_string(activity.id)
1850 test "multi-hashtag timeline", %{conn: conn} do
1851 user = insert(:user)
1853 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1854 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1855 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1859 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1861 [status_none, status_test1, status_test] = json_response(any_test, 200)
1863 assert to_string(activity_test.id) == status_test["id"]
1864 assert to_string(activity_test1.id) == status_test1["id"]
1865 assert to_string(activity_none.id) == status_none["id"]
1869 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1871 assert [status_test1] == json_response(restricted_test, 200)
1873 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1875 assert [status_none] == json_response(all_test, 200)
1878 test "getting followers", %{conn: conn} do
1879 user = insert(:user)
1880 other_user = insert(:user)
1881 {:ok, user} = User.follow(user, other_user)
1885 |> get("/api/v1/accounts/#{other_user.id}/followers")
1887 assert [%{"id" => id}] = json_response(conn, 200)
1888 assert id == to_string(user.id)
1891 test "getting followers, hide_followers", %{conn: conn} do
1892 user = insert(:user)
1893 other_user = insert(:user, %{info: %{hide_followers: true}})
1894 {:ok, _user} = User.follow(user, other_user)
1898 |> get("/api/v1/accounts/#{other_user.id}/followers")
1900 assert [] == json_response(conn, 200)
1903 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1904 user = insert(:user)
1905 other_user = insert(:user, %{info: %{hide_followers: true}})
1906 {:ok, _user} = User.follow(user, other_user)
1910 |> assign(:user, other_user)
1911 |> get("/api/v1/accounts/#{other_user.id}/followers")
1913 refute [] == json_response(conn, 200)
1916 test "getting followers, pagination", %{conn: conn} do
1917 user = insert(:user)
1918 follower1 = insert(:user)
1919 follower2 = insert(:user)
1920 follower3 = insert(:user)
1921 {:ok, _} = User.follow(follower1, user)
1922 {:ok, _} = User.follow(follower2, user)
1923 {:ok, _} = User.follow(follower3, user)
1927 |> assign(:user, user)
1931 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1933 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1934 assert id3 == follower3.id
1935 assert id2 == follower2.id
1939 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1941 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1942 assert id2 == follower2.id
1943 assert id1 == follower1.id
1947 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1949 assert [%{"id" => id2}] = json_response(res_conn, 200)
1950 assert id2 == follower2.id
1952 assert [link_header] = get_resp_header(res_conn, "link")
1953 assert link_header =~ ~r/min_id=#{follower2.id}/
1954 assert link_header =~ ~r/max_id=#{follower2.id}/
1957 test "getting following", %{conn: conn} do
1958 user = insert(:user)
1959 other_user = insert(:user)
1960 {:ok, user} = User.follow(user, other_user)
1964 |> get("/api/v1/accounts/#{user.id}/following")
1966 assert [%{"id" => id}] = json_response(conn, 200)
1967 assert id == to_string(other_user.id)
1970 test "getting following, hide_follows", %{conn: conn} do
1971 user = insert(:user, %{info: %{hide_follows: true}})
1972 other_user = insert(:user)
1973 {:ok, user} = User.follow(user, other_user)
1977 |> get("/api/v1/accounts/#{user.id}/following")
1979 assert [] == json_response(conn, 200)
1982 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1983 user = insert(:user, %{info: %{hide_follows: true}})
1984 other_user = insert(:user)
1985 {:ok, user} = User.follow(user, other_user)
1989 |> assign(:user, user)
1990 |> get("/api/v1/accounts/#{user.id}/following")
1992 refute [] == json_response(conn, 200)
1995 test "getting following, pagination", %{conn: conn} do
1996 user = insert(:user)
1997 following1 = insert(:user)
1998 following2 = insert(:user)
1999 following3 = insert(:user)
2000 {:ok, _} = User.follow(user, following1)
2001 {:ok, _} = User.follow(user, following2)
2002 {:ok, _} = User.follow(user, following3)
2006 |> assign(:user, user)
2010 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2012 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2013 assert id3 == following3.id
2014 assert id2 == following2.id
2018 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2020 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2021 assert id2 == following2.id
2022 assert id1 == following1.id
2026 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2028 assert [%{"id" => id2}] = json_response(res_conn, 200)
2029 assert id2 == following2.id
2031 assert [link_header] = get_resp_header(res_conn, "link")
2032 assert link_header =~ ~r/min_id=#{following2.id}/
2033 assert link_header =~ ~r/max_id=#{following2.id}/
2036 test "following / unfollowing a user", %{conn: conn} do
2037 user = insert(:user)
2038 other_user = insert(:user)
2042 |> assign(:user, user)
2043 |> post("/api/v1/accounts/#{other_user.id}/follow")
2045 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2047 user = User.get_cached_by_id(user.id)
2051 |> assign(:user, user)
2052 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2054 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2056 user = User.get_cached_by_id(user.id)
2060 |> assign(:user, user)
2061 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2063 assert %{"id" => id} = json_response(conn, 200)
2064 assert id == to_string(other_user.id)
2067 test "following without reblogs" do
2068 follower = insert(:user)
2069 followed = insert(:user)
2070 other_user = insert(:user)
2074 |> assign(:user, follower)
2075 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2077 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2079 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2080 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2084 |> assign(:user, User.get_cached_by_id(follower.id))
2085 |> get("/api/v1/timelines/home")
2087 assert [] == json_response(conn, 200)
2091 |> assign(:user, follower)
2092 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2094 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2098 |> assign(:user, User.get_cached_by_id(follower.id))
2099 |> get("/api/v1/timelines/home")
2101 expected_activity_id = reblog.id
2102 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2105 test "following / unfollowing errors" do
2106 user = insert(:user)
2110 |> assign(:user, user)
2113 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2114 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2117 user = User.get_cached_by_id(user.id)
2118 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2119 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2121 # self follow via uri
2122 user = User.get_cached_by_id(user.id)
2123 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2124 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2126 # follow non existing user
2127 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2128 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2130 # follow non existing user via uri
2131 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2132 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2134 # unfollow non existing user
2135 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2136 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2139 describe "mute/unmute" do
2140 test "with notifications", %{conn: conn} do
2141 user = insert(:user)
2142 other_user = insert(:user)
2146 |> assign(:user, user)
2147 |> post("/api/v1/accounts/#{other_user.id}/mute")
2149 response = json_response(conn, 200)
2151 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2152 user = User.get_cached_by_id(user.id)
2156 |> assign(:user, user)
2157 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2159 response = json_response(conn, 200)
2160 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2163 test "without notifications", %{conn: conn} do
2164 user = insert(:user)
2165 other_user = insert(:user)
2169 |> assign(:user, user)
2170 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2172 response = json_response(conn, 200)
2174 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2175 user = User.get_cached_by_id(user.id)
2179 |> assign(:user, user)
2180 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2182 response = json_response(conn, 200)
2183 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2187 test "subscribing / unsubscribing to a user", %{conn: conn} do
2188 user = insert(:user)
2189 subscription_target = insert(:user)
2193 |> assign(:user, user)
2194 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2196 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2200 |> assign(:user, user)
2201 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2203 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2206 test "getting a list of mutes", %{conn: conn} do
2207 user = insert(:user)
2208 other_user = insert(:user)
2210 {:ok, user} = User.mute(user, other_user)
2214 |> assign(:user, user)
2215 |> get("/api/v1/mutes")
2217 other_user_id = to_string(other_user.id)
2218 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2221 test "blocking / unblocking a user", %{conn: conn} do
2222 user = insert(:user)
2223 other_user = insert(:user)
2227 |> assign(:user, user)
2228 |> post("/api/v1/accounts/#{other_user.id}/block")
2230 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2232 user = User.get_cached_by_id(user.id)
2236 |> assign(:user, user)
2237 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2239 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2242 test "getting a list of blocks", %{conn: conn} do
2243 user = insert(:user)
2244 other_user = insert(:user)
2246 {:ok, user} = User.block(user, other_user)
2250 |> assign(:user, user)
2251 |> get("/api/v1/blocks")
2253 other_user_id = to_string(other_user.id)
2254 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2257 test "blocking / unblocking a domain", %{conn: conn} do
2258 user = insert(:user)
2259 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2263 |> assign(:user, user)
2264 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2266 assert %{} = json_response(conn, 200)
2267 user = User.get_cached_by_ap_id(user.ap_id)
2268 assert User.blocks?(user, other_user)
2272 |> assign(:user, user)
2273 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2275 assert %{} = json_response(conn, 200)
2276 user = User.get_cached_by_ap_id(user.ap_id)
2277 refute User.blocks?(user, other_user)
2280 test "getting a list of domain blocks", %{conn: conn} do
2281 user = insert(:user)
2283 {:ok, user} = User.block_domain(user, "bad.site")
2284 {:ok, user} = User.block_domain(user, "even.worse.site")
2288 |> assign(:user, user)
2289 |> get("/api/v1/domain_blocks")
2291 domain_blocks = json_response(conn, 200)
2293 assert "bad.site" in domain_blocks
2294 assert "even.worse.site" in domain_blocks
2297 test "unimplemented follow_requests, blocks, domain blocks" do
2298 user = insert(:user)
2300 ["blocks", "domain_blocks", "follow_requests"]
2301 |> Enum.each(fn endpoint ->
2304 |> assign(:user, user)
2305 |> get("/api/v1/#{endpoint}")
2307 assert [] = json_response(conn, 200)
2311 test "returns the favorites of a user", %{conn: conn} do
2312 user = insert(:user)
2313 other_user = insert(:user)
2315 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2316 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2318 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2322 |> assign(:user, user)
2323 |> get("/api/v1/favourites")
2325 assert [status] = json_response(first_conn, 200)
2326 assert status["id"] == to_string(activity.id)
2328 assert [{"link", _link_header}] =
2329 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2331 # Honours query params
2332 {:ok, second_activity} =
2333 CommonAPI.post(other_user, %{
2335 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2338 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2340 last_like = status["id"]
2344 |> assign(:user, user)
2345 |> get("/api/v1/favourites?since_id=#{last_like}")
2347 assert [second_status] = json_response(second_conn, 200)
2348 assert second_status["id"] == to_string(second_activity.id)
2352 |> assign(:user, user)
2353 |> get("/api/v1/favourites?limit=0")
2355 assert [] = json_response(third_conn, 200)
2358 describe "getting favorites timeline of specified user" do
2360 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2361 [current_user: current_user, user: user]
2364 test "returns list of statuses favorited by specified user", %{
2366 current_user: current_user,
2369 [activity | _] = insert_pair(:note_activity)
2370 CommonAPI.favorite(activity.id, user)
2374 |> assign(:user, current_user)
2375 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2376 |> json_response(:ok)
2380 assert length(response) == 1
2381 assert like["id"] == activity.id
2384 test "returns favorites for specified user_id when user is not logged in", %{
2388 activity = insert(:note_activity)
2389 CommonAPI.favorite(activity.id, user)
2393 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2394 |> json_response(:ok)
2396 assert length(response) == 1
2399 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2401 current_user: current_user,
2405 CommonAPI.post(current_user, %{
2406 "status" => "Hi @#{user.nickname}!",
2407 "visibility" => "direct"
2410 CommonAPI.favorite(direct.id, user)
2414 |> assign(:user, current_user)
2415 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2416 |> json_response(:ok)
2418 assert length(response) == 1
2420 anonymous_response =
2422 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2423 |> json_response(:ok)
2425 assert Enum.empty?(anonymous_response)
2428 test "does not return others' favorited DM when user is not one of recipients", %{
2430 current_user: current_user,
2433 user_two = insert(:user)
2436 CommonAPI.post(user_two, %{
2437 "status" => "Hi @#{user.nickname}!",
2438 "visibility" => "direct"
2441 CommonAPI.favorite(direct.id, user)
2445 |> assign(:user, current_user)
2446 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2447 |> json_response(:ok)
2449 assert Enum.empty?(response)
2452 test "paginates favorites using since_id and max_id", %{
2454 current_user: current_user,
2457 activities = insert_list(10, :note_activity)
2459 Enum.each(activities, fn activity ->
2460 CommonAPI.favorite(activity.id, user)
2463 third_activity = Enum.at(activities, 2)
2464 seventh_activity = Enum.at(activities, 6)
2468 |> assign(:user, current_user)
2469 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2470 since_id: third_activity.id,
2471 max_id: seventh_activity.id
2473 |> json_response(:ok)
2475 assert length(response) == 3
2476 refute third_activity in response
2477 refute seventh_activity in response
2480 test "limits favorites using limit parameter", %{
2482 current_user: current_user,
2486 |> insert_list(:note_activity)
2487 |> Enum.each(fn activity ->
2488 CommonAPI.favorite(activity.id, user)
2493 |> assign(:user, current_user)
2494 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2495 |> json_response(:ok)
2497 assert length(response) == 3
2500 test "returns empty response when user does not have any favorited statuses", %{
2502 current_user: current_user,
2507 |> assign(:user, current_user)
2508 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2509 |> json_response(:ok)
2511 assert Enum.empty?(response)
2514 test "returns 404 error when specified user is not exist", %{conn: conn} do
2515 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2517 assert json_response(conn, 404) == %{"error" => "Record not found"}
2520 test "returns 403 error when user has hidden own favorites", %{
2522 current_user: current_user
2524 user = insert(:user, %{info: %{hide_favorites: true}})
2525 activity = insert(:note_activity)
2526 CommonAPI.favorite(activity.id, user)
2530 |> assign(:user, current_user)
2531 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2533 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2536 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2537 user = insert(:user)
2538 activity = insert(:note_activity)
2539 CommonAPI.favorite(activity.id, user)
2543 |> assign(:user, current_user)
2544 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2546 assert user.info.hide_favorites
2547 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2551 test "get instance information", %{conn: conn} do
2552 conn = get(conn, "/api/v1/instance")
2553 assert result = json_response(conn, 200)
2555 email = Config.get([:instance, :email])
2556 # Note: not checking for "max_toot_chars" since it's optional
2562 "email" => from_config_email,
2564 "streaming_api" => _
2569 "registrations" => _,
2573 assert email == from_config_email
2576 test "get instance stats", %{conn: conn} do
2577 user = insert(:user, %{local: true})
2579 user2 = insert(:user, %{local: true})
2580 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2582 insert(:user, %{local: false, nickname: "u@peer1.com"})
2583 insert(:user, %{local: false, nickname: "u@peer2.com"})
2585 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2587 # Stats should count users with missing or nil `info.deactivated` value
2588 user = User.get_cached_by_id(user.id)
2589 info_change = Changeset.change(user.info, %{deactivated: nil})
2593 |> Changeset.change()
2594 |> Changeset.put_embed(:info, info_change)
2595 |> User.update_and_set_cache()
2597 Pleroma.Stats.force_update()
2599 conn = get(conn, "/api/v1/instance")
2601 assert result = json_response(conn, 200)
2603 stats = result["stats"]
2606 assert stats["user_count"] == 1
2607 assert stats["status_count"] == 1
2608 assert stats["domain_count"] == 2
2611 test "get peers", %{conn: conn} do
2612 insert(:user, %{local: false, nickname: "u@peer1.com"})
2613 insert(:user, %{local: false, nickname: "u@peer2.com"})
2615 Pleroma.Stats.force_update()
2617 conn = get(conn, "/api/v1/instance/peers")
2619 assert result = json_response(conn, 200)
2621 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2624 test "put settings", %{conn: conn} do
2625 user = insert(:user)
2629 |> assign(:user, user)
2630 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2632 assert _result = json_response(conn, 200)
2634 user = User.get_cached_by_ap_id(user.ap_id)
2635 assert user.info.settings == %{"programming" => "socks"}
2638 describe "pinned statuses" do
2640 user = insert(:user)
2641 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2643 [user: user, activity: activity]
2646 clear_config([:instance, :max_pinned_statuses]) do
2647 Config.put([:instance, :max_pinned_statuses], 1)
2650 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2651 {:ok, _} = CommonAPI.pin(activity.id, user)
2655 |> assign(:user, user)
2656 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2657 |> json_response(200)
2659 id_str = to_string(activity.id)
2661 assert [%{"id" => ^id_str, "pinned" => true}] = result
2664 test "pin status", %{conn: conn, user: user, activity: activity} do
2665 id_str = to_string(activity.id)
2667 assert %{"id" => ^id_str, "pinned" => true} =
2669 |> assign(:user, user)
2670 |> post("/api/v1/statuses/#{activity.id}/pin")
2671 |> json_response(200)
2673 assert [%{"id" => ^id_str, "pinned" => true}] =
2675 |> assign(:user, user)
2676 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2677 |> json_response(200)
2680 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2681 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2685 |> assign(:user, user)
2686 |> post("/api/v1/statuses/#{dm.id}/pin")
2688 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2691 test "unpin status", %{conn: conn, user: user, activity: activity} do
2692 {:ok, _} = CommonAPI.pin(activity.id, user)
2694 id_str = to_string(activity.id)
2695 user = refresh_record(user)
2697 assert %{"id" => ^id_str, "pinned" => false} =
2699 |> assign(:user, user)
2700 |> post("/api/v1/statuses/#{activity.id}/unpin")
2701 |> json_response(200)
2705 |> assign(:user, user)
2706 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2707 |> json_response(200)
2710 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2713 |> assign(:user, user)
2714 |> post("/api/v1/statuses/1/unpin")
2716 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2719 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2720 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2722 id_str_one = to_string(activity_one.id)
2724 assert %{"id" => ^id_str_one, "pinned" => true} =
2726 |> assign(:user, user)
2727 |> post("/api/v1/statuses/#{id_str_one}/pin")
2728 |> json_response(200)
2730 user = refresh_record(user)
2732 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2734 |> assign(:user, user)
2735 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2736 |> json_response(400)
2742 Config.put([:rich_media, :enabled], true)
2744 user = insert(:user)
2748 test "returns rich-media card", %{conn: conn, user: user} do
2749 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2752 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2753 "provider_name" => "example.com",
2754 "provider_url" => "https://example.com",
2755 "title" => "The Rock",
2757 "url" => "https://example.com/ogp",
2759 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2762 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2763 "title" => "The Rock",
2764 "type" => "video.movie",
2765 "url" => "https://example.com/ogp",
2767 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2774 |> get("/api/v1/statuses/#{activity.id}/card")
2775 |> json_response(200)
2777 assert response == card_data
2779 # works with private posts
2781 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2785 |> assign(:user, user)
2786 |> get("/api/v1/statuses/#{activity.id}/card")
2787 |> json_response(200)
2789 assert response_two == card_data
2792 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2794 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2798 |> get("/api/v1/statuses/#{activity.id}/card")
2799 |> json_response(:ok)
2801 assert response == %{
2803 "title" => "Pleroma",
2804 "description" => "",
2806 "provider_name" => "example.com",
2807 "provider_url" => "https://example.com",
2808 "url" => "https://example.com/ogp-missing-data",
2811 "title" => "Pleroma",
2812 "type" => "website",
2813 "url" => "https://example.com/ogp-missing-data"
2821 user = insert(:user)
2822 for_user = insert(:user)
2825 CommonAPI.post(user, %{
2826 "status" => "heweoo?"
2830 CommonAPI.post(user, %{
2831 "status" => "heweoo!"
2836 |> assign(:user, for_user)
2837 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2839 assert json_response(response1, 200)["bookmarked"] == true
2843 |> assign(:user, for_user)
2844 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2846 assert json_response(response2, 200)["bookmarked"] == true
2850 |> assign(:user, for_user)
2851 |> get("/api/v1/bookmarks")
2853 assert [json_response(response2, 200), json_response(response1, 200)] ==
2854 json_response(bookmarks, 200)
2858 |> assign(:user, for_user)
2859 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2861 assert json_response(response1, 200)["bookmarked"] == false
2865 |> assign(:user, for_user)
2866 |> get("/api/v1/bookmarks")
2868 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2871 describe "conversation muting" do
2873 post_user = insert(:user)
2874 user = insert(:user)
2876 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2878 [user: user, activity: activity]
2881 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2882 id_str = to_string(activity.id)
2884 assert %{"id" => ^id_str, "muted" => true} =
2886 |> assign(:user, user)
2887 |> post("/api/v1/statuses/#{activity.id}/mute")
2888 |> json_response(200)
2891 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2892 {:ok, _} = CommonAPI.add_mute(user, activity)
2896 |> assign(:user, user)
2897 |> post("/api/v1/statuses/#{activity.id}/mute")
2899 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2902 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2903 {:ok, _} = CommonAPI.add_mute(user, activity)
2905 id_str = to_string(activity.id)
2906 user = refresh_record(user)
2908 assert %{"id" => ^id_str, "muted" => false} =
2910 |> assign(:user, user)
2911 |> post("/api/v1/statuses/#{activity.id}/unmute")
2912 |> json_response(200)
2916 describe "reports" do
2918 reporter = insert(:user)
2919 target_user = insert(:user)
2921 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2923 [reporter: reporter, target_user: target_user, activity: activity]
2926 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2927 assert %{"action_taken" => false, "id" => _} =
2929 |> assign(:user, reporter)
2930 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2931 |> json_response(200)
2934 test "submit a report with statuses and comment", %{
2937 target_user: target_user,
2940 assert %{"action_taken" => false, "id" => _} =
2942 |> assign(:user, reporter)
2943 |> post("/api/v1/reports", %{
2944 "account_id" => target_user.id,
2945 "status_ids" => [activity.id],
2946 "comment" => "bad status!",
2947 "forward" => "false"
2949 |> json_response(200)
2952 test "account_id is required", %{
2957 assert %{"error" => "Valid `account_id` required"} =
2959 |> assign(:user, reporter)
2960 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2961 |> json_response(400)
2964 test "comment must be up to the size specified in the config", %{
2967 target_user: target_user
2969 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2970 comment = String.pad_trailing("a", max_size + 1, "a")
2972 error = %{"error" => "Comment must be up to #{max_size} characters"}
2976 |> assign(:user, reporter)
2977 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2978 |> json_response(400)
2981 test "returns error when account is not exist", %{
2988 |> assign(:user, reporter)
2989 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
2991 assert json_response(conn, 400) == %{"error" => "Account not found"}
2995 describe "link headers" do
2996 test "preserves parameters in link headers", %{conn: conn} do
2997 user = insert(:user)
2998 other_user = insert(:user)
3001 CommonAPI.post(other_user, %{
3002 "status" => "hi @#{user.nickname}",
3003 "visibility" => "public"
3007 CommonAPI.post(other_user, %{
3008 "status" => "hi @#{user.nickname}",
3009 "visibility" => "public"
3012 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3013 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3017 |> assign(:user, user)
3018 |> get("/api/v1/notifications", %{media_only: true})
3020 assert [link_header] = get_resp_header(conn, "link")
3021 assert link_header =~ ~r/media_only=true/
3022 assert link_header =~ ~r/min_id=#{notification2.id}/
3023 assert link_header =~ ~r/max_id=#{notification1.id}/
3027 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3028 # Need to set an old-style integer ID to reproduce the problem
3029 # (these are no longer assigned to new accounts but were preserved
3030 # for existing accounts during the migration to flakeIDs)
3031 user_one = insert(:user, %{id: 1212})
3032 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3036 |> get("/api/v1/accounts/#{user_one.id}")
3040 |> get("/api/v1/accounts/#{user_two.nickname}")
3044 |> get("/api/v1/accounts/#{user_two.id}")
3046 acc_one = json_response(resp_one, 200)
3047 acc_two = json_response(resp_two, 200)
3048 acc_three = json_response(resp_three, 200)
3049 refute acc_one == acc_two
3050 assert acc_two == acc_three
3053 describe "custom emoji" do
3054 test "with tags", %{conn: conn} do
3057 |> get("/api/v1/custom_emojis")
3058 |> json_response(200)
3060 assert Map.has_key?(emoji, "shortcode")
3061 assert Map.has_key?(emoji, "static_url")
3062 assert Map.has_key?(emoji, "tags")
3063 assert is_list(emoji["tags"])
3064 assert Map.has_key?(emoji, "category")
3065 assert Map.has_key?(emoji, "url")
3066 assert Map.has_key?(emoji, "visible_in_picker")
3070 describe "index/2 redirections" do
3071 setup %{conn: conn} do
3075 signing_salt: "cooldude"
3080 |> Plug.Session.call(Plug.Session.init(session_opts))
3083 test_path = "/web/statuses/test"
3084 %{conn: conn, path: test_path}
3087 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3088 conn = get(conn, path)
3090 assert conn.status == 302
3091 assert redirected_to(conn) == "/web/login"
3094 test "redirects not logged-in users to the login page on private instances", %{
3098 Config.put([:instance, :public], false)
3100 conn = get(conn, path)
3102 assert conn.status == 302
3103 assert redirected_to(conn) == "/web/login"
3106 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3107 token = insert(:oauth_token)
3111 |> assign(:user, token.user)
3112 |> put_session(:oauth_token, token.token)
3115 assert conn.status == 200
3118 test "saves referer path to session", %{conn: conn, path: path} do
3119 conn = get(conn, path)
3120 return_to = Plug.Conn.get_session(conn, :return_to)
3122 assert return_to == path
3125 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3126 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3127 auth = insert(:oauth_authorization, app: app)
3131 |> put_session(:return_to, path)
3132 |> get("/web/login", %{code: auth.token})
3134 assert conn.status == 302
3135 assert redirected_to(conn) == path
3138 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3139 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3140 auth = insert(:oauth_authorization, app: app)
3142 conn = get(conn, "/web/login", %{code: auth.token})
3144 assert conn.status == 302
3145 assert redirected_to(conn) == "/web/getting-started"
3149 describe "scheduled activities" do
3150 test "creates a scheduled activity", %{conn: conn} do
3151 user = insert(:user)
3152 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3156 |> assign(:user, user)
3157 |> post("/api/v1/statuses", %{
3158 "status" => "scheduled",
3159 "scheduled_at" => scheduled_at
3162 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3163 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3164 assert [] == Repo.all(Activity)
3167 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3168 user = insert(:user)
3169 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3171 file = %Plug.Upload{
3172 content_type: "image/jpg",
3173 path: Path.absname("test/fixtures/image.jpg"),
3174 filename: "an_image.jpg"
3177 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3181 |> assign(:user, user)
3182 |> post("/api/v1/statuses", %{
3183 "media_ids" => [to_string(upload.id)],
3184 "status" => "scheduled",
3185 "scheduled_at" => scheduled_at
3188 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3189 assert %{"type" => "image"} = media_attachment
3192 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3194 user = insert(:user)
3197 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3201 |> assign(:user, user)
3202 |> post("/api/v1/statuses", %{
3203 "status" => "not scheduled",
3204 "scheduled_at" => scheduled_at
3207 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3208 assert [] == Repo.all(ScheduledActivity)
3211 test "returns error when daily user limit is exceeded", %{conn: conn} do
3212 user = insert(:user)
3215 NaiveDateTime.utc_now()
3216 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3217 |> NaiveDateTime.to_iso8601()
3219 attrs = %{params: %{}, scheduled_at: today}
3220 {:ok, _} = ScheduledActivity.create(user, attrs)
3221 {:ok, _} = ScheduledActivity.create(user, attrs)
3225 |> assign(:user, user)
3226 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3228 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3231 test "returns error when total user limit is exceeded", %{conn: conn} do
3232 user = insert(:user)
3235 NaiveDateTime.utc_now()
3236 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3237 |> NaiveDateTime.to_iso8601()
3240 NaiveDateTime.utc_now()
3241 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3242 |> NaiveDateTime.to_iso8601()
3244 attrs = %{params: %{}, scheduled_at: today}
3245 {:ok, _} = ScheduledActivity.create(user, attrs)
3246 {:ok, _} = ScheduledActivity.create(user, attrs)
3247 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3251 |> assign(:user, user)
3252 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3254 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3257 test "shows scheduled activities", %{conn: conn} do
3258 user = insert(:user)
3259 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3260 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3261 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3262 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3266 |> assign(:user, user)
3271 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3273 result = json_response(conn_res, 200)
3274 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3279 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3281 result = json_response(conn_res, 200)
3282 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3287 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3289 result = json_response(conn_res, 200)
3290 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3293 test "shows a scheduled activity", %{conn: conn} do
3294 user = insert(:user)
3295 scheduled_activity = insert(:scheduled_activity, user: user)
3299 |> assign(:user, user)
3300 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3302 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3303 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3307 |> assign(:user, user)
3308 |> get("/api/v1/scheduled_statuses/404")
3310 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3313 test "updates a scheduled activity", %{conn: conn} do
3314 user = insert(:user)
3315 scheduled_activity = insert(:scheduled_activity, user: user)
3318 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3322 |> assign(:user, user)
3323 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3324 scheduled_at: new_scheduled_at
3327 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3328 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3332 |> assign(:user, user)
3333 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3335 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3338 test "deletes a scheduled activity", %{conn: conn} do
3339 user = insert(:user)
3340 scheduled_activity = insert(:scheduled_activity, user: user)
3344 |> assign(:user, user)
3345 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3347 assert %{} = json_response(res_conn, 200)
3348 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3352 |> assign(:user, user)
3353 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3355 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3359 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3360 user1 = insert(:user)
3361 user2 = insert(:user)
3362 user3 = insert(:user)
3364 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3366 # Reply to status from another user
3369 |> assign(:user, user2)
3370 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3372 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3374 activity = Activity.get_by_id_with_object(id)
3376 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3377 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3379 # Reblog from the third user
3382 |> assign(:user, user3)
3383 |> post("/api/v1/statuses/#{activity.id}/reblog")
3385 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3386 json_response(conn2, 200)
3388 assert to_string(activity.id) == id
3390 # Getting third user status
3393 |> assign(:user, user3)
3394 |> get("api/v1/timelines/home")
3396 [reblogged_activity] = json_response(conn3, 200)
3398 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3400 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3401 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3404 describe "create account by app" do
3405 test "Account registration via Application", %{conn: conn} do
3408 |> post("/api/v1/apps", %{
3409 client_name: "client_name",
3410 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3411 scopes: "read, write, follow"
3415 "client_id" => client_id,
3416 "client_secret" => client_secret,
3418 "name" => "client_name",
3419 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3422 } = json_response(conn, 200)
3426 |> post("/oauth/token", %{
3427 grant_type: "client_credentials",
3428 client_id: client_id,
3429 client_secret: client_secret
3432 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3433 json_response(conn, 200)
3436 token_from_db = Repo.get_by(Token, token: token)
3437 assert token_from_db
3439 assert scope == "read write follow"
3443 |> put_req_header("authorization", "Bearer " <> token)
3444 |> post("/api/v1/accounts", %{
3446 email: "lain@example.org",
3447 password: "PlzDontHackLain",
3452 "access_token" => token,
3453 "created_at" => _created_at,
3455 "token_type" => "Bearer"
3456 } = json_response(conn, 200)
3458 token_from_db = Repo.get_by(Token, token: token)
3459 assert token_from_db
3460 token_from_db = Repo.preload(token_from_db, :user)
3461 assert token_from_db.user
3463 assert token_from_db.user.info.confirmation_pending
3466 test "rate limit", %{conn: conn} do
3467 app_token = insert(:oauth_token, user: nil)
3470 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3471 |> Map.put(:remote_ip, {15, 15, 15, 15})
3476 |> post("/api/v1/accounts", %{
3477 username: "#{i}lain",
3478 email: "#{i}lain@example.org",
3479 password: "PlzDontHackLain",
3484 "access_token" => token,
3485 "created_at" => _created_at,
3487 "token_type" => "Bearer"
3488 } = json_response(conn, 200)
3490 token_from_db = Repo.get_by(Token, token: token)
3491 assert token_from_db
3492 token_from_db = Repo.preload(token_from_db, :user)
3493 assert token_from_db.user
3495 assert token_from_db.user.info.confirmation_pending
3500 |> post("/api/v1/accounts", %{
3502 email: "6lain@example.org",
3503 password: "PlzDontHackLain",
3507 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3511 describe "GET /api/v1/polls/:id" do
3512 test "returns poll entity for object id", %{conn: conn} do
3513 user = insert(:user)
3516 CommonAPI.post(user, %{
3517 "status" => "Pleroma does",
3518 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3521 object = Object.normalize(activity)
3525 |> assign(:user, user)
3526 |> get("/api/v1/polls/#{object.id}")
3528 response = json_response(conn, 200)
3529 id = to_string(object.id)
3530 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3533 test "does not expose polls for private statuses", %{conn: conn} do
3534 user = insert(:user)
3535 other_user = insert(:user)
3538 CommonAPI.post(user, %{
3539 "status" => "Pleroma does",
3540 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3541 "visibility" => "private"
3544 object = Object.normalize(activity)
3548 |> assign(:user, other_user)
3549 |> get("/api/v1/polls/#{object.id}")
3551 assert json_response(conn, 404)
3555 describe "POST /api/v1/polls/:id/votes" do
3556 test "votes are added to the poll", %{conn: conn} do
3557 user = insert(:user)
3558 other_user = insert(:user)
3561 CommonAPI.post(user, %{
3562 "status" => "A very delicious sandwich",
3564 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3570 object = Object.normalize(activity)
3574 |> assign(:user, other_user)
3575 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3577 assert json_response(conn, 200)
3578 object = Object.get_by_id(object.id)
3580 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3585 test "author can't vote", %{conn: conn} do
3586 user = insert(:user)
3589 CommonAPI.post(user, %{
3590 "status" => "Am I cute?",
3591 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3594 object = Object.normalize(activity)
3597 |> assign(:user, user)
3598 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3599 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3601 object = Object.get_by_id(object.id)
3603 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3606 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3607 user = insert(:user)
3608 other_user = insert(:user)
3611 CommonAPI.post(user, %{
3612 "status" => "The glass is",
3613 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3616 object = Object.normalize(activity)
3619 |> assign(:user, other_user)
3620 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3621 |> json_response(422) == %{"error" => "Too many choices"}
3623 object = Object.get_by_id(object.id)
3625 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3630 test "does not allow choice index to be greater than options count", %{conn: conn} do
3631 user = insert(:user)
3632 other_user = insert(:user)
3635 CommonAPI.post(user, %{
3636 "status" => "Am I cute?",
3637 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3640 object = Object.normalize(activity)
3644 |> assign(:user, other_user)
3645 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3647 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3650 test "returns 404 error when object is not exist", %{conn: conn} do
3651 user = insert(:user)
3655 |> assign(:user, user)
3656 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3658 assert json_response(conn, 404) == %{"error" => "Record not found"}
3661 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3662 user = insert(:user)
3663 other_user = insert(:user)
3666 CommonAPI.post(user, %{
3667 "status" => "Am I cute?",
3668 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3669 "visibility" => "private"
3672 object = Object.normalize(activity)
3676 |> assign(:user, other_user)
3677 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3679 assert json_response(conn, 404) == %{"error" => "Record not found"}
3683 describe "GET /api/v1/statuses/:id/favourited_by" do
3685 user = insert(:user)
3686 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3690 |> assign(:user, user)
3692 [conn: conn, activity: activity]
3695 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3696 other_user = insert(:user)
3697 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3701 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3702 |> json_response(:ok)
3704 [%{"id" => id}] = response
3706 assert id == other_user.id
3709 test "returns empty array when status has not been favorited yet", %{
3715 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3716 |> json_response(:ok)
3718 assert Enum.empty?(response)
3721 test "does not return users who have favorited the status but are blocked", %{
3722 conn: %{assigns: %{user: user}} = conn,
3725 other_user = insert(:user)
3726 {:ok, user} = User.block(user, other_user)
3728 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3732 |> assign(:user, user)
3733 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3734 |> json_response(:ok)
3736 assert Enum.empty?(response)
3739 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3740 other_user = insert(:user)
3741 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3745 |> assign(:user, nil)
3746 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3747 |> json_response(:ok)
3749 [%{"id" => id}] = response
3750 assert id == other_user.id
3754 describe "GET /api/v1/statuses/:id/reblogged_by" do
3756 user = insert(:user)
3757 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3761 |> assign(:user, user)
3763 [conn: conn, activity: activity]
3766 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3767 other_user = insert(:user)
3768 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3772 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3773 |> json_response(:ok)
3775 [%{"id" => id}] = response
3777 assert id == other_user.id
3780 test "returns empty array when status has not been reblogged yet", %{
3786 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3787 |> json_response(:ok)
3789 assert Enum.empty?(response)
3792 test "does not return users who have reblogged the status but are blocked", %{
3793 conn: %{assigns: %{user: user}} = conn,
3796 other_user = insert(:user)
3797 {:ok, user} = User.block(user, other_user)
3799 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3803 |> assign(:user, user)
3804 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3805 |> json_response(:ok)
3807 assert Enum.empty?(response)
3810 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3811 other_user = insert(:user)
3812 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3816 |> assign(:user, nil)
3817 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3818 |> json_response(:ok)
3820 [%{"id" => id}] = response
3821 assert id == other_user.id
3825 describe "POST /auth/password, with valid parameters" do
3826 setup %{conn: conn} do
3827 user = insert(:user)
3828 conn = post(conn, "/auth/password?email=#{user.email}")
3829 %{conn: conn, user: user}
3832 test "it returns 204", %{conn: conn} do
3833 assert json_response(conn, :no_content)
3836 test "it creates a PasswordResetToken record for user", %{user: user} do
3837 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3841 test "it sends an email to user", %{user: user} do
3842 ObanHelpers.perform_all()
3843 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3845 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3846 notify_email = Config.get([:instance, :notify_email])
3847 instance_name = Config.get([:instance, :name])
3850 from: {instance_name, notify_email},
3851 to: {user.name, user.email},
3852 html_body: email.html_body
3857 describe "POST /auth/password, with invalid parameters" do
3859 user = insert(:user)
3863 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3864 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3865 assert conn.status == 404
3866 assert conn.resp_body == ""
3869 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3870 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3871 conn = post(conn, "/auth/password?email=#{user.email}")
3872 assert conn.status == 400
3873 assert conn.resp_body == ""
3877 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3879 user = insert(:user)
3880 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3884 |> Changeset.change()
3885 |> Changeset.put_embed(:info, info_change)
3888 assert user.info.confirmation_pending
3893 clear_config([:instance, :account_activation_required]) do
3894 Config.put([:instance, :account_activation_required], true)
3897 test "resend account confirmation email", %{conn: conn, user: user} do
3899 |> assign(:user, user)
3900 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3901 |> json_response(:no_content)
3903 ObanHelpers.perform_all()
3905 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3906 notify_email = Config.get([:instance, :notify_email])
3907 instance_name = Config.get([:instance, :name])
3910 from: {instance_name, notify_email},
3911 to: {user.name, user.email},
3912 html_body: email.html_body
3917 describe "GET /api/v1/suggestions" do
3919 user = insert(:user)
3920 other_user = insert(:user)
3921 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3922 url500 = "http://test500?#{host}&#{user.nickname}"
3923 url200 = "http://test200?#{host}&#{user.nickname}"
3926 %{method: :get, url: ^url500} ->
3927 %Tesla.Env{status: 500, body: "bad request"}
3929 %{method: :get, url: ^url200} ->
3933 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3935 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3939 [user: user, other_user: other_user]
3942 clear_config(:suggestions)
3944 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3945 Config.put([:suggestions, :enabled], false)
3949 |> assign(:user, user)
3950 |> get("/api/v1/suggestions")
3951 |> json_response(200)
3956 test "returns error", %{conn: conn, user: user} do
3957 Config.put([:suggestions, :enabled], true)
3958 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
3962 |> assign(:user, user)
3963 |> get("/api/v1/suggestions")
3964 |> json_response(500)
3966 assert res == "Something went wrong"
3969 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
3970 Config.put([:suggestions, :enabled], true)
3971 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
3975 |> assign(:user, user)
3976 |> get("/api/v1/suggestions")
3977 |> json_response(200)
3982 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
3983 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
3987 "acct" => other_user.ap_id,
3988 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
3989 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
3990 "id" => other_user.id