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.Notification
13 alias Pleroma.ScheduledActivity
14 alias Pleroma.Tests.ObanHelpers
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.FilterView
19 alias Pleroma.Web.OAuth.App
20 alias Pleroma.Web.OAuth.Token
21 alias Pleroma.Web.OStatus
22 alias Pleroma.Web.Push
23 alias Pleroma.Web.TwitterAPI.TwitterAPI
24 import Pleroma.Factory
25 import ExUnit.CaptureLog
27 import Swoosh.TestAssertions
29 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
32 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
36 test "the home timeline", %{conn: conn} do
38 following = insert(:user)
40 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
44 |> assign(:user, user)
45 |> get("/api/v1/timelines/home")
47 assert Enum.empty?(json_response(conn, 200))
49 {:ok, user} = User.follow(user, following)
53 |> assign(:user, user)
54 |> get("/api/v1/timelines/home")
56 assert [%{"content" => "test"}] = json_response(conn, 200)
59 test "the public timeline", %{conn: conn} do
60 following = insert(:user)
63 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
66 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
70 |> get("/api/v1/timelines/public", %{"local" => "False"})
72 assert length(json_response(conn, 200)) == 2
76 |> get("/api/v1/timelines/public", %{"local" => "True"})
78 assert [%{"content" => "test"}] = json_response(conn, 200)
82 |> get("/api/v1/timelines/public", %{"local" => "1"})
84 assert [%{"content" => "test"}] = json_response(conn, 200)
88 test "the public timeline when public is set to false", %{conn: conn} do
89 public = Pleroma.Config.get([:instance, :public])
90 Pleroma.Config.put([:instance, :public], false)
93 Pleroma.Config.put([:instance, :public], public)
97 |> get("/api/v1/timelines/public", %{"local" => "False"})
98 |> json_response(403) == %{"error" => "This resource requires authentication."}
101 describe "posting statuses" do
107 |> assign(:user, user)
112 test "posting a status", %{conn: conn} do
113 idempotency_key = "Pikachu rocks!"
117 |> put_req_header("idempotency-key", idempotency_key)
118 |> post("/api/v1/statuses", %{
120 "spoiler_text" => "2hu",
121 "sensitive" => "false"
124 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
126 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
128 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
129 json_response(conn_one, 200)
131 assert Activity.get_by_id(id)
135 |> put_req_header("idempotency-key", idempotency_key)
136 |> post("/api/v1/statuses", %{
138 "spoiler_text" => "2hu",
139 "sensitive" => "false"
142 assert %{"id" => second_id} = json_response(conn_two, 200)
143 assert id == second_id
147 |> post("/api/v1/statuses", %{
149 "spoiler_text" => "2hu",
150 "sensitive" => "false"
153 assert %{"id" => third_id} = json_response(conn_three, 200)
154 refute id == third_id
157 test "replying to a status", %{conn: conn} do
159 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
163 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
165 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
167 activity = Activity.get_by_id(id)
169 assert activity.data["context"] == replied_to.data["context"]
170 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
173 test "replying to a direct message with visibility other than direct", %{conn: conn} do
175 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
177 Enum.each(["public", "private", "unlisted"], fn visibility ->
180 |> post("/api/v1/statuses", %{
181 "status" => "@#{user.nickname} hey",
182 "in_reply_to_id" => replied_to.id,
183 "visibility" => visibility
186 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
190 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
193 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
195 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
196 assert Activity.get_by_id(id)
199 test "posting a sensitive status", %{conn: conn} do
202 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
204 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
205 assert Activity.get_by_id(id)
208 test "posting a fake status", %{conn: conn} do
211 |> post("/api/v1/statuses", %{
213 "\"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"
216 real_status = json_response(real_conn, 200)
219 assert Object.get_by_ap_id(real_status["uri"])
223 |> Map.put("id", nil)
224 |> Map.put("url", nil)
225 |> Map.put("uri", nil)
226 |> Map.put("created_at", nil)
227 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
231 |> post("/api/v1/statuses", %{
233 "\"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",
237 fake_status = json_response(fake_conn, 200)
240 refute Object.get_by_ap_id(fake_status["uri"])
244 |> Map.put("id", nil)
245 |> Map.put("url", nil)
246 |> Map.put("uri", nil)
247 |> Map.put("created_at", nil)
248 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
250 assert real_status == fake_status
253 test "posting a status with OGP link preview", %{conn: conn} do
254 Pleroma.Config.put([:rich_media, :enabled], true)
258 |> post("/api/v1/statuses", %{
259 "status" => "https://example.com/ogp"
262 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
263 assert Activity.get_by_id(id)
264 Pleroma.Config.put([:rich_media, :enabled], false)
267 test "posting a direct status", %{conn: conn} do
268 user2 = insert(:user)
269 content = "direct cofe @#{user2.nickname}"
273 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
275 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
276 assert activity = Activity.get_by_id(id)
277 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
278 assert activity.data["to"] == [user2.ap_id]
279 assert activity.data["cc"] == []
283 describe "posting polls" do
284 test "posting a poll", %{conn: conn} do
286 time = NaiveDateTime.utc_now()
290 |> assign(:user, user)
291 |> post("/api/v1/statuses", %{
292 "status" => "Who is the #bestgrill?",
293 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
296 response = json_response(conn, 200)
298 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
299 title in ["Rei", "Asuka", "Misato"]
302 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
303 refute response["poll"]["expred"]
306 test "option limit is enforced", %{conn: conn} do
308 limit = Pleroma.Config.get([:instance, :poll_limits, :max_options])
312 |> assign(:user, user)
313 |> post("/api/v1/statuses", %{
315 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
318 %{"error" => error} = json_response(conn, 422)
319 assert error == "Poll can't contain more than #{limit} options"
322 test "option character limit is enforced", %{conn: conn} do
324 limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars])
328 |> assign(:user, user)
329 |> post("/api/v1/statuses", %{
332 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
337 %{"error" => error} = json_response(conn, 422)
338 assert error == "Poll options cannot be longer than #{limit} characters each"
341 test "minimal date limit is enforced", %{conn: conn} do
343 limit = Pleroma.Config.get([:instance, :poll_limits, :min_expiration])
347 |> assign(:user, user)
348 |> post("/api/v1/statuses", %{
349 "status" => "imagine arbitrary limits",
351 "options" => ["this post was made by pleroma gang"],
352 "expires_in" => limit - 1
356 %{"error" => error} = json_response(conn, 422)
357 assert error == "Expiration date is too soon"
360 test "maximum date limit is enforced", %{conn: conn} do
362 limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration])
366 |> assign(:user, user)
367 |> post("/api/v1/statuses", %{
368 "status" => "imagine arbitrary limits",
370 "options" => ["this post was made by pleroma gang"],
371 "expires_in" => limit + 1
375 %{"error" => error} = json_response(conn, 422)
376 assert error == "Expiration date is too far in the future"
380 test "direct timeline", %{conn: conn} do
381 user_one = insert(:user)
382 user_two = insert(:user)
384 {:ok, user_two} = User.follow(user_two, user_one)
387 CommonAPI.post(user_one, %{
388 "status" => "Hi @#{user_two.nickname}!",
389 "visibility" => "direct"
392 {:ok, _follower_only} =
393 CommonAPI.post(user_one, %{
394 "status" => "Hi @#{user_two.nickname}!",
395 "visibility" => "private"
398 # Only direct should be visible here
401 |> assign(:user, user_two)
402 |> get("api/v1/timelines/direct")
404 [status] = json_response(res_conn, 200)
406 assert %{"visibility" => "direct"} = status
407 assert status["url"] != direct.data["id"]
409 # User should be able to see his own direct message
412 |> assign(:user, user_one)
413 |> get("api/v1/timelines/direct")
415 [status] = json_response(res_conn, 200)
417 assert %{"visibility" => "direct"} = status
419 # Both should be visible here
422 |> assign(:user, user_two)
423 |> get("api/v1/timelines/home")
425 [_s1, _s2] = json_response(res_conn, 200)
428 Enum.each(1..20, fn _ ->
430 CommonAPI.post(user_one, %{
431 "status" => "Hi @#{user_two.nickname}!",
432 "visibility" => "direct"
438 |> assign(:user, user_two)
439 |> get("api/v1/timelines/direct")
441 statuses = json_response(res_conn, 200)
442 assert length(statuses) == 20
446 |> assign(:user, user_two)
447 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
449 [status] = json_response(res_conn, 200)
451 assert status["url"] != direct.data["id"]
454 test "Conversations", %{conn: conn} do
455 user_one = insert(:user)
456 user_two = insert(:user)
457 user_three = insert(:user)
459 {:ok, user_two} = User.follow(user_two, user_one)
462 CommonAPI.post(user_one, %{
463 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
464 "visibility" => "direct"
467 {:ok, _follower_only} =
468 CommonAPI.post(user_one, %{
469 "status" => "Hi @#{user_two.nickname}!",
470 "visibility" => "private"
475 |> assign(:user, user_one)
476 |> get("/api/v1/conversations")
478 assert response = json_response(res_conn, 200)
483 "accounts" => res_accounts,
484 "last_status" => res_last_status,
489 account_ids = Enum.map(res_accounts, & &1["id"])
490 assert length(res_accounts) == 2
491 assert user_two.id in account_ids
492 assert user_three.id in account_ids
493 assert is_binary(res_id)
494 assert unread == true
495 assert res_last_status["id"] == direct.id
497 # Apparently undocumented API endpoint
500 |> assign(:user, user_one)
501 |> post("/api/v1/conversations/#{res_id}/read")
503 assert response = json_response(res_conn, 200)
504 assert length(response["accounts"]) == 2
505 assert response["last_status"]["id"] == direct.id
506 assert response["unread"] == false
508 # (vanilla) Mastodon frontend behaviour
511 |> assign(:user, user_one)
512 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
514 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
517 test "doesn't include DMs from blocked users", %{conn: conn} do
518 blocker = insert(:user)
519 blocked = insert(:user)
521 {:ok, blocker} = User.block(blocker, blocked)
523 {:ok, _blocked_direct} =
524 CommonAPI.post(blocked, %{
525 "status" => "Hi @#{blocker.nickname}!",
526 "visibility" => "direct"
530 CommonAPI.post(user, %{
531 "status" => "Hi @#{blocker.nickname}!",
532 "visibility" => "direct"
537 |> assign(:user, user)
538 |> get("api/v1/timelines/direct")
540 [status] = json_response(res_conn, 200)
541 assert status["id"] == direct.id
544 test "verify_credentials", %{conn: conn} do
549 |> assign(:user, user)
550 |> get("/api/v1/accounts/verify_credentials")
552 response = json_response(conn, 200)
554 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
555 assert response["pleroma"]["chat_token"]
556 assert id == to_string(user.id)
559 test "verify_credentials default scope unlisted", %{conn: conn} do
560 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
564 |> assign(:user, user)
565 |> get("/api/v1/accounts/verify_credentials")
567 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
568 assert id == to_string(user.id)
571 test "apps/verify_credentials", %{conn: conn} do
572 token = insert(:oauth_token)
576 |> assign(:user, token.user)
577 |> assign(:token, token)
578 |> get("/api/v1/apps/verify_credentials")
580 app = Repo.preload(token, :app).app
583 "name" => app.client_name,
584 "website" => app.website,
585 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
588 assert expected == json_response(conn, 200)
591 test "user avatar can be set", %{conn: conn} do
593 avatar_image = File.read!("test/fixtures/avatar_data_uri")
597 |> assign(:user, user)
598 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
600 user = refresh_record(user)
614 assert %{"url" => _} = json_response(conn, 200)
617 test "user avatar can be reset", %{conn: conn} do
622 |> assign(:user, user)
623 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
625 user = User.get_cached_by_id(user.id)
627 assert user.avatar == nil
629 assert %{"url" => nil} = json_response(conn, 200)
632 test "can set profile banner", %{conn: conn} do
637 |> assign(:user, user)
638 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
640 user = refresh_record(user)
641 assert user.info.banner["type"] == "Image"
643 assert %{"url" => _} = json_response(conn, 200)
646 test "can reset profile banner", %{conn: conn} do
651 |> assign(:user, user)
652 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
654 user = refresh_record(user)
655 assert user.info.banner == %{}
657 assert %{"url" => nil} = json_response(conn, 200)
660 test "background image can be set", %{conn: conn} do
665 |> assign(:user, user)
666 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
668 user = refresh_record(user)
669 assert user.info.background["type"] == "Image"
670 assert %{"url" => _} = json_response(conn, 200)
673 test "background image can be reset", %{conn: conn} do
678 |> assign(:user, user)
679 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
681 user = refresh_record(user)
682 assert user.info.background == %{}
683 assert %{"url" => nil} = json_response(conn, 200)
686 test "creates an oauth app", %{conn: conn} do
688 app_attrs = build(:oauth_app)
692 |> assign(:user, user)
693 |> post("/api/v1/apps", %{
694 client_name: app_attrs.client_name,
695 redirect_uris: app_attrs.redirect_uris
698 [app] = Repo.all(App)
701 "name" => app.client_name,
702 "website" => app.website,
703 "client_id" => app.client_id,
704 "client_secret" => app.client_secret,
705 "id" => app.id |> to_string(),
706 "redirect_uri" => app.redirect_uris,
707 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
710 assert expected == json_response(conn, 200)
713 test "get a status", %{conn: conn} do
714 activity = insert(:note_activity)
718 |> get("/api/v1/statuses/#{activity.id}")
720 assert %{"id" => id} = json_response(conn, 200)
721 assert id == to_string(activity.id)
724 describe "deleting a status" do
725 test "when you created it", %{conn: conn} do
726 activity = insert(:note_activity)
727 author = User.get_cached_by_ap_id(activity.data["actor"])
731 |> assign(:user, author)
732 |> delete("/api/v1/statuses/#{activity.id}")
734 assert %{} = json_response(conn, 200)
736 refute Activity.get_by_id(activity.id)
739 test "when you didn't create it", %{conn: conn} do
740 activity = insert(:note_activity)
745 |> assign(:user, user)
746 |> delete("/api/v1/statuses/#{activity.id}")
748 assert %{"error" => _} = json_response(conn, 403)
750 assert Activity.get_by_id(activity.id) == activity
753 test "when you're an admin or moderator", %{conn: conn} do
754 activity1 = insert(:note_activity)
755 activity2 = insert(:note_activity)
756 admin = insert(:user, info: %{is_admin: true})
757 moderator = insert(:user, info: %{is_moderator: true})
761 |> assign(:user, admin)
762 |> delete("/api/v1/statuses/#{activity1.id}")
764 assert %{} = json_response(res_conn, 200)
768 |> assign(:user, moderator)
769 |> delete("/api/v1/statuses/#{activity2.id}")
771 assert %{} = json_response(res_conn, 200)
773 refute Activity.get_by_id(activity1.id)
774 refute Activity.get_by_id(activity2.id)
778 describe "filters" do
779 test "creating a filter", %{conn: conn} do
782 filter = %Pleroma.Filter{
789 |> assign(:user, user)
790 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
792 assert response = json_response(conn, 200)
793 assert response["phrase"] == filter.phrase
794 assert response["context"] == filter.context
795 assert response["irreversible"] == false
796 assert response["id"] != nil
797 assert response["id"] != ""
800 test "fetching a list of filters", %{conn: conn} do
803 query_one = %Pleroma.Filter{
810 query_two = %Pleroma.Filter{
817 {:ok, filter_one} = Pleroma.Filter.create(query_one)
818 {:ok, filter_two} = Pleroma.Filter.create(query_two)
822 |> assign(:user, user)
823 |> get("/api/v1/filters")
824 |> json_response(200)
830 filters: [filter_two, filter_one]
834 test "get a filter", %{conn: conn} do
837 query = %Pleroma.Filter{
844 {:ok, filter} = Pleroma.Filter.create(query)
848 |> assign(:user, user)
849 |> get("/api/v1/filters/#{filter.filter_id}")
851 assert _response = json_response(conn, 200)
854 test "update a filter", %{conn: conn} do
857 query = %Pleroma.Filter{
864 {:ok, _filter} = Pleroma.Filter.create(query)
866 new = %Pleroma.Filter{
873 |> assign(:user, user)
874 |> put("/api/v1/filters/#{query.filter_id}", %{
879 assert response = json_response(conn, 200)
880 assert response["phrase"] == new.phrase
881 assert response["context"] == new.context
884 test "delete a filter", %{conn: conn} do
887 query = %Pleroma.Filter{
894 {:ok, filter} = Pleroma.Filter.create(query)
898 |> assign(:user, user)
899 |> delete("/api/v1/filters/#{filter.filter_id}")
901 assert response = json_response(conn, 200)
902 assert response == %{}
907 test "creating a list", %{conn: conn} do
912 |> assign(:user, user)
913 |> post("/api/v1/lists", %{"title" => "cuties"})
915 assert %{"title" => title} = json_response(conn, 200)
916 assert title == "cuties"
919 test "adding users to a list", %{conn: conn} do
921 other_user = insert(:user)
922 {:ok, list} = Pleroma.List.create("name", user)
926 |> assign(:user, user)
927 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
929 assert %{} == json_response(conn, 200)
930 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
931 assert following == [other_user.follower_address]
934 test "removing users from a list", %{conn: conn} do
936 other_user = insert(:user)
937 third_user = insert(:user)
938 {:ok, list} = Pleroma.List.create("name", user)
939 {:ok, list} = Pleroma.List.follow(list, other_user)
940 {:ok, list} = Pleroma.List.follow(list, third_user)
944 |> assign(:user, user)
945 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
947 assert %{} == json_response(conn, 200)
948 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
949 assert following == [third_user.follower_address]
952 test "listing users in a list", %{conn: conn} do
954 other_user = insert(:user)
955 {:ok, list} = Pleroma.List.create("name", user)
956 {:ok, list} = Pleroma.List.follow(list, other_user)
960 |> assign(:user, user)
961 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
963 assert [%{"id" => id}] = json_response(conn, 200)
964 assert id == to_string(other_user.id)
967 test "retrieving a list", %{conn: conn} do
969 {:ok, list} = Pleroma.List.create("name", user)
973 |> assign(:user, user)
974 |> get("/api/v1/lists/#{list.id}")
976 assert %{"id" => id} = json_response(conn, 200)
977 assert id == to_string(list.id)
980 test "renaming a list", %{conn: conn} do
982 {:ok, list} = Pleroma.List.create("name", user)
986 |> assign(:user, user)
987 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
989 assert %{"title" => name} = json_response(conn, 200)
990 assert name == "newname"
993 test "deleting a list", %{conn: conn} do
995 {:ok, list} = Pleroma.List.create("name", user)
999 |> assign(:user, user)
1000 |> delete("/api/v1/lists/#{list.id}")
1002 assert %{} = json_response(conn, 200)
1003 assert is_nil(Repo.get(Pleroma.List, list.id))
1006 test "list timeline", %{conn: conn} do
1007 user = insert(:user)
1008 other_user = insert(:user)
1009 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1010 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1011 {:ok, list} = Pleroma.List.create("name", user)
1012 {:ok, list} = Pleroma.List.follow(list, other_user)
1016 |> assign(:user, user)
1017 |> get("/api/v1/timelines/list/#{list.id}")
1019 assert [%{"id" => id}] = json_response(conn, 200)
1021 assert id == to_string(activity_two.id)
1024 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1025 user = insert(:user)
1026 other_user = insert(:user)
1027 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1029 {:ok, _activity_two} =
1030 CommonAPI.post(other_user, %{
1031 "status" => "Marisa is cute.",
1032 "visibility" => "private"
1035 {:ok, list} = Pleroma.List.create("name", user)
1036 {:ok, list} = Pleroma.List.follow(list, other_user)
1040 |> assign(:user, user)
1041 |> get("/api/v1/timelines/list/#{list.id}")
1043 assert [%{"id" => id}] = json_response(conn, 200)
1045 assert id == to_string(activity_one.id)
1049 describe "notifications" do
1050 test "list of notifications", %{conn: conn} do
1051 user = insert(:user)
1052 other_user = insert(:user)
1054 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1056 {:ok, [_notification]} = Notification.create_notifications(activity)
1060 |> assign(:user, user)
1061 |> get("/api/v1/notifications")
1064 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1066 }\">@<span>#{user.nickname}</span></a></span>"
1068 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1069 assert response == expected_response
1072 test "getting a single notification", %{conn: conn} do
1073 user = insert(:user)
1074 other_user = insert(:user)
1076 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1078 {:ok, [notification]} = Notification.create_notifications(activity)
1082 |> assign(:user, user)
1083 |> get("/api/v1/notifications/#{notification.id}")
1086 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1088 }\">@<span>#{user.nickname}</span></a></span>"
1090 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1091 assert response == expected_response
1094 test "dismissing a single notification", %{conn: conn} do
1095 user = insert(:user)
1096 other_user = insert(:user)
1098 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1100 {:ok, [notification]} = Notification.create_notifications(activity)
1104 |> assign(:user, user)
1105 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1107 assert %{} = json_response(conn, 200)
1110 test "clearing all notifications", %{conn: conn} do
1111 user = insert(:user)
1112 other_user = insert(:user)
1114 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1116 {:ok, [_notification]} = Notification.create_notifications(activity)
1120 |> assign(:user, user)
1121 |> post("/api/v1/notifications/clear")
1123 assert %{} = json_response(conn, 200)
1127 |> assign(:user, user)
1128 |> get("/api/v1/notifications")
1130 assert all = json_response(conn, 200)
1134 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1135 user = insert(:user)
1136 other_user = insert(:user)
1138 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1139 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1140 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1141 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1143 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1144 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1145 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1146 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1150 |> assign(:user, user)
1155 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1157 result = json_response(conn_res, 200)
1158 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1163 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1165 result = json_response(conn_res, 200)
1166 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1171 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1173 result = json_response(conn_res, 200)
1174 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1177 test "filters notifications using exclude_types", %{conn: conn} do
1178 user = insert(:user)
1179 other_user = insert(:user)
1181 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1182 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1183 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1184 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1185 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1187 mention_notification_id =
1188 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1190 favorite_notification_id =
1191 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1193 reblog_notification_id =
1194 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1196 follow_notification_id =
1197 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1201 |> assign(:user, user)
1204 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1206 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1209 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1211 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1214 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1216 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1219 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1221 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1224 test "destroy multiple", %{conn: conn} do
1225 user = insert(:user)
1226 other_user = insert(:user)
1228 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1229 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1230 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1231 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1233 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1234 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1235 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1236 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1240 |> assign(:user, user)
1244 |> get("/api/v1/notifications")
1246 result = json_response(conn_res, 200)
1247 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1251 |> assign(:user, other_user)
1255 |> get("/api/v1/notifications")
1257 result = json_response(conn_res, 200)
1258 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1262 |> delete("/api/v1/notifications/destroy_multiple", %{
1263 "ids" => [notification1_id, notification2_id]
1266 assert json_response(conn_destroy, 200) == %{}
1270 |> get("/api/v1/notifications")
1272 result = json_response(conn_res, 200)
1273 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1276 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1277 user = insert(:user)
1278 user2 = insert(:user)
1280 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1281 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1283 conn = assign(conn, :user, user)
1285 conn = get(conn, "/api/v1/notifications")
1287 assert length(json_response(conn, 200)) == 1
1289 {:ok, user} = User.mute(user, user2)
1291 conn = assign(build_conn(), :user, user)
1292 conn = get(conn, "/api/v1/notifications")
1294 assert json_response(conn, 200) == []
1297 test "see notifications after muting user without notifications", %{conn: conn} do
1298 user = insert(:user)
1299 user2 = insert(:user)
1301 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1302 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1304 conn = assign(conn, :user, user)
1306 conn = get(conn, "/api/v1/notifications")
1308 assert length(json_response(conn, 200)) == 1
1310 {:ok, user} = User.mute(user, user2, false)
1312 conn = assign(build_conn(), :user, user)
1313 conn = get(conn, "/api/v1/notifications")
1315 assert length(json_response(conn, 200)) == 1
1318 test "see notifications after muting user with notifications and with_muted parameter", %{
1321 user = insert(:user)
1322 user2 = insert(:user)
1324 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1325 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1327 conn = assign(conn, :user, user)
1329 conn = get(conn, "/api/v1/notifications")
1331 assert length(json_response(conn, 200)) == 1
1333 {:ok, user} = User.mute(user, user2)
1335 conn = assign(build_conn(), :user, user)
1336 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1338 assert length(json_response(conn, 200)) == 1
1342 describe "reblogging" do
1343 test "reblogs and returns the reblogged status", %{conn: conn} do
1344 activity = insert(:note_activity)
1345 user = insert(:user)
1349 |> assign(:user, user)
1350 |> post("/api/v1/statuses/#{activity.id}/reblog")
1353 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1355 } = json_response(conn, 200)
1357 assert to_string(activity.id) == id
1360 test "reblogged status for another user", %{conn: conn} do
1361 activity = insert(:note_activity)
1362 user1 = insert(:user)
1363 user2 = insert(:user)
1364 user3 = insert(:user)
1365 CommonAPI.favorite(activity.id, user2)
1366 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1367 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1368 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1372 |> assign(:user, user3)
1373 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1376 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1377 "reblogged" => false,
1378 "favourited" => false,
1379 "bookmarked" => false
1380 } = json_response(conn_res, 200)
1384 |> assign(:user, user2)
1385 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1388 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1389 "reblogged" => true,
1390 "favourited" => true,
1391 "bookmarked" => true
1392 } = json_response(conn_res, 200)
1394 assert to_string(activity.id) == id
1397 test "returns 400 error when activity is not exist", %{conn: conn} do
1398 user = insert(:user)
1402 |> assign(:user, user)
1403 |> post("/api/v1/statuses/foo/reblog")
1405 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1409 describe "unreblogging" do
1410 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1411 activity = insert(:note_activity)
1412 user = insert(:user)
1414 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1418 |> assign(:user, user)
1419 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1421 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1423 assert to_string(activity.id) == id
1426 test "returns 400 error when activity is not exist", %{conn: conn} do
1427 user = insert(:user)
1431 |> assign(:user, user)
1432 |> post("/api/v1/statuses/foo/unreblog")
1434 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1438 describe "favoriting" do
1439 test "favs a status and returns it", %{conn: conn} do
1440 activity = insert(:note_activity)
1441 user = insert(:user)
1445 |> assign(:user, user)
1446 |> post("/api/v1/statuses/#{activity.id}/favourite")
1448 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1449 json_response(conn, 200)
1451 assert to_string(activity.id) == id
1454 test "returns 400 error for a wrong id", %{conn: conn} do
1455 user = insert(:user)
1459 |> assign(:user, user)
1460 |> post("/api/v1/statuses/1/favourite")
1462 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1466 describe "unfavoriting" do
1467 test "unfavorites a status and returns it", %{conn: conn} do
1468 activity = insert(:note_activity)
1469 user = insert(:user)
1471 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1475 |> assign(:user, user)
1476 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1478 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1479 json_response(conn, 200)
1481 assert to_string(activity.id) == id
1484 test "returns 400 error for a wrong id", %{conn: conn} do
1485 user = insert(:user)
1489 |> assign(:user, user)
1490 |> post("/api/v1/statuses/1/unfavourite")
1492 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1496 describe "user timelines" do
1497 test "gets a users statuses", %{conn: conn} do
1498 user_one = insert(:user)
1499 user_two = insert(:user)
1500 user_three = insert(:user)
1502 {:ok, user_three} = User.follow(user_three, user_one)
1504 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1506 {:ok, direct_activity} =
1507 CommonAPI.post(user_one, %{
1508 "status" => "Hi, @#{user_two.nickname}.",
1509 "visibility" => "direct"
1512 {:ok, private_activity} =
1513 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1517 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1519 assert [%{"id" => id}] = json_response(resp, 200)
1520 assert id == to_string(activity.id)
1524 |> assign(:user, user_two)
1525 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1527 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1528 assert id_one == to_string(direct_activity.id)
1529 assert id_two == to_string(activity.id)
1533 |> assign(:user, user_three)
1534 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1536 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1537 assert id_one == to_string(private_activity.id)
1538 assert id_two == to_string(activity.id)
1541 test "unimplemented pinned statuses feature", %{conn: conn} do
1542 note = insert(:note_activity)
1543 user = User.get_cached_by_ap_id(note.data["actor"])
1547 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1549 assert json_response(conn, 200) == []
1552 test "gets an users media", %{conn: conn} do
1553 note = insert(:note_activity)
1554 user = User.get_cached_by_ap_id(note.data["actor"])
1556 file = %Plug.Upload{
1557 content_type: "image/jpg",
1558 path: Path.absname("test/fixtures/image.jpg"),
1559 filename: "an_image.jpg"
1563 TwitterAPI.upload(file, user, "json")
1567 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1571 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1573 assert [%{"id" => id}] = json_response(conn, 200)
1574 assert id == to_string(image_post.id)
1578 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1580 assert [%{"id" => id}] = json_response(conn, 200)
1581 assert id == to_string(image_post.id)
1584 test "gets a user's statuses without reblogs", %{conn: conn} do
1585 user = insert(:user)
1586 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1587 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1591 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1593 assert [%{"id" => id}] = json_response(conn, 200)
1594 assert id == to_string(post.id)
1598 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1600 assert [%{"id" => id}] = json_response(conn, 200)
1601 assert id == to_string(post.id)
1604 test "filters user's statuses by a hashtag", %{conn: conn} do
1605 user = insert(:user)
1606 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1607 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1611 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1613 assert [%{"id" => id}] = json_response(conn, 200)
1614 assert id == to_string(post.id)
1618 describe "user relationships" do
1619 test "returns the relationships for the current user", %{conn: conn} do
1620 user = insert(:user)
1621 other_user = insert(:user)
1622 {:ok, user} = User.follow(user, other_user)
1626 |> assign(:user, user)
1627 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1629 assert [relationship] = json_response(conn, 200)
1631 assert to_string(other_user.id) == relationship["id"]
1635 describe "media upload" do
1637 upload_config = Pleroma.Config.get([Pleroma.Upload])
1638 proxy_config = Pleroma.Config.get([:media_proxy])
1641 Pleroma.Config.put([Pleroma.Upload], upload_config)
1642 Pleroma.Config.put([:media_proxy], proxy_config)
1645 user = insert(:user)
1649 |> assign(:user, user)
1651 image = %Plug.Upload{
1652 content_type: "image/jpg",
1653 path: Path.absname("test/fixtures/image.jpg"),
1654 filename: "an_image.jpg"
1657 [conn: conn, image: image]
1660 test "returns uploaded image", %{conn: conn, image: image} do
1661 desc = "Description of the image"
1665 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1666 |> json_response(:ok)
1668 assert media["type"] == "image"
1669 assert media["description"] == desc
1672 object = Repo.get(Object, media["id"])
1673 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1677 describe "locked accounts" do
1678 test "/api/v1/follow_requests works" do
1679 user = insert(:user, %{info: %User.Info{locked: true}})
1680 other_user = insert(:user)
1682 {:ok, _activity} = ActivityPub.follow(other_user, user)
1684 user = User.get_cached_by_id(user.id)
1685 other_user = User.get_cached_by_id(other_user.id)
1687 assert User.following?(other_user, user) == false
1691 |> assign(:user, user)
1692 |> get("/api/v1/follow_requests")
1694 assert [relationship] = json_response(conn, 200)
1695 assert to_string(other_user.id) == relationship["id"]
1698 test "/api/v1/follow_requests/:id/authorize works" do
1699 user = insert(:user, %{info: %User.Info{locked: true}})
1700 other_user = insert(:user)
1702 {:ok, _activity} = ActivityPub.follow(other_user, user)
1704 user = User.get_cached_by_id(user.id)
1705 other_user = User.get_cached_by_id(other_user.id)
1707 assert User.following?(other_user, user) == false
1711 |> assign(:user, user)
1712 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1714 assert relationship = json_response(conn, 200)
1715 assert to_string(other_user.id) == relationship["id"]
1717 user = User.get_cached_by_id(user.id)
1718 other_user = User.get_cached_by_id(other_user.id)
1720 assert User.following?(other_user, user) == true
1723 test "verify_credentials", %{conn: conn} do
1724 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1728 |> assign(:user, user)
1729 |> get("/api/v1/accounts/verify_credentials")
1731 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1732 assert id == to_string(user.id)
1735 test "/api/v1/follow_requests/:id/reject works" do
1736 user = insert(:user, %{info: %User.Info{locked: true}})
1737 other_user = insert(:user)
1739 {:ok, _activity} = ActivityPub.follow(other_user, user)
1741 user = User.get_cached_by_id(user.id)
1745 |> assign(:user, user)
1746 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1748 assert relationship = json_response(conn, 200)
1749 assert to_string(other_user.id) == relationship["id"]
1751 user = User.get_cached_by_id(user.id)
1752 other_user = User.get_cached_by_id(other_user.id)
1754 assert User.following?(other_user, user) == false
1758 test "account fetching", %{conn: conn} do
1759 user = insert(:user)
1763 |> get("/api/v1/accounts/#{user.id}")
1765 assert %{"id" => id} = json_response(conn, 200)
1766 assert id == to_string(user.id)
1770 |> get("/api/v1/accounts/-1")
1772 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1775 test "account fetching also works nickname", %{conn: conn} do
1776 user = insert(:user)
1780 |> get("/api/v1/accounts/#{user.nickname}")
1782 assert %{"id" => id} = json_response(conn, 200)
1783 assert id == user.id
1786 test "mascot upload", %{conn: conn} do
1787 user = insert(:user)
1789 non_image_file = %Plug.Upload{
1790 content_type: "audio/mpeg",
1791 path: Path.absname("test/fixtures/sound.mp3"),
1792 filename: "sound.mp3"
1797 |> assign(:user, user)
1798 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1800 assert json_response(conn, 415)
1802 file = %Plug.Upload{
1803 content_type: "image/jpg",
1804 path: Path.absname("test/fixtures/image.jpg"),
1805 filename: "an_image.jpg"
1810 |> assign(:user, user)
1811 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1813 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1816 test "mascot retrieving", %{conn: conn} do
1817 user = insert(:user)
1818 # When user hasn't set a mascot, we should just get pleroma tan back
1821 |> assign(:user, user)
1822 |> get("/api/v1/pleroma/mascot")
1824 assert %{"url" => url} = json_response(conn, 200)
1825 assert url =~ "pleroma-fox-tan-smol"
1827 # When a user sets their mascot, we should get that back
1828 file = %Plug.Upload{
1829 content_type: "image/jpg",
1830 path: Path.absname("test/fixtures/image.jpg"),
1831 filename: "an_image.jpg"
1836 |> assign(:user, user)
1837 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1839 assert json_response(conn, 200)
1841 user = User.get_cached_by_id(user.id)
1845 |> assign(:user, user)
1846 |> get("/api/v1/pleroma/mascot")
1848 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1849 assert url =~ "an_image"
1852 test "hashtag timeline", %{conn: conn} do
1853 following = insert(:user)
1856 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1858 {:ok, [_activity]} =
1859 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1863 |> get("/api/v1/timelines/tag/2hu")
1865 assert [%{"id" => id}] = json_response(nconn, 200)
1867 assert id == to_string(activity.id)
1869 # works for different capitalization too
1872 |> get("/api/v1/timelines/tag/2HU")
1874 assert [%{"id" => id}] = json_response(nconn, 200)
1876 assert id == to_string(activity.id)
1880 test "multi-hashtag timeline", %{conn: conn} do
1881 user = insert(:user)
1883 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1884 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1885 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1889 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1891 [status_none, status_test1, status_test] = json_response(any_test, 200)
1893 assert to_string(activity_test.id) == status_test["id"]
1894 assert to_string(activity_test1.id) == status_test1["id"]
1895 assert to_string(activity_none.id) == status_none["id"]
1899 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1901 assert [status_test1] == json_response(restricted_test, 200)
1903 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1905 assert [status_none] == json_response(all_test, 200)
1908 test "getting followers", %{conn: conn} do
1909 user = insert(:user)
1910 other_user = insert(:user)
1911 {:ok, user} = User.follow(user, other_user)
1915 |> get("/api/v1/accounts/#{other_user.id}/followers")
1917 assert [%{"id" => id}] = json_response(conn, 200)
1918 assert id == to_string(user.id)
1921 test "getting followers, hide_followers", %{conn: conn} do
1922 user = insert(:user)
1923 other_user = insert(:user, %{info: %{hide_followers: true}})
1924 {:ok, _user} = User.follow(user, other_user)
1928 |> get("/api/v1/accounts/#{other_user.id}/followers")
1930 assert [] == json_response(conn, 200)
1933 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1934 user = insert(:user)
1935 other_user = insert(:user, %{info: %{hide_followers: true}})
1936 {:ok, _user} = User.follow(user, other_user)
1940 |> assign(:user, other_user)
1941 |> get("/api/v1/accounts/#{other_user.id}/followers")
1943 refute [] == json_response(conn, 200)
1946 test "getting followers, pagination", %{conn: conn} do
1947 user = insert(:user)
1948 follower1 = insert(:user)
1949 follower2 = insert(:user)
1950 follower3 = insert(:user)
1951 {:ok, _} = User.follow(follower1, user)
1952 {:ok, _} = User.follow(follower2, user)
1953 {:ok, _} = User.follow(follower3, user)
1957 |> assign(:user, user)
1961 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1963 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1964 assert id3 == follower3.id
1965 assert id2 == follower2.id
1969 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1971 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1972 assert id2 == follower2.id
1973 assert id1 == follower1.id
1977 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1979 assert [%{"id" => id2}] = json_response(res_conn, 200)
1980 assert id2 == follower2.id
1982 assert [link_header] = get_resp_header(res_conn, "link")
1983 assert link_header =~ ~r/min_id=#{follower2.id}/
1984 assert link_header =~ ~r/max_id=#{follower2.id}/
1987 test "getting following", %{conn: conn} do
1988 user = insert(:user)
1989 other_user = insert(:user)
1990 {:ok, user} = User.follow(user, other_user)
1994 |> get("/api/v1/accounts/#{user.id}/following")
1996 assert [%{"id" => id}] = json_response(conn, 200)
1997 assert id == to_string(other_user.id)
2000 test "getting following, hide_follows", %{conn: conn} do
2001 user = insert(:user, %{info: %{hide_follows: true}})
2002 other_user = insert(:user)
2003 {:ok, user} = User.follow(user, other_user)
2007 |> get("/api/v1/accounts/#{user.id}/following")
2009 assert [] == json_response(conn, 200)
2012 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2013 user = insert(:user, %{info: %{hide_follows: true}})
2014 other_user = insert(:user)
2015 {:ok, user} = User.follow(user, other_user)
2019 |> assign(:user, user)
2020 |> get("/api/v1/accounts/#{user.id}/following")
2022 refute [] == json_response(conn, 200)
2025 test "getting following, pagination", %{conn: conn} do
2026 user = insert(:user)
2027 following1 = insert(:user)
2028 following2 = insert(:user)
2029 following3 = insert(:user)
2030 {:ok, _} = User.follow(user, following1)
2031 {:ok, _} = User.follow(user, following2)
2032 {:ok, _} = User.follow(user, following3)
2036 |> assign(:user, user)
2040 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2042 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2043 assert id3 == following3.id
2044 assert id2 == following2.id
2048 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2050 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2051 assert id2 == following2.id
2052 assert id1 == following1.id
2056 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2058 assert [%{"id" => id2}] = json_response(res_conn, 200)
2059 assert id2 == following2.id
2061 assert [link_header] = get_resp_header(res_conn, "link")
2062 assert link_header =~ ~r/min_id=#{following2.id}/
2063 assert link_header =~ ~r/max_id=#{following2.id}/
2066 test "following / unfollowing a user", %{conn: conn} do
2067 user = insert(:user)
2068 other_user = insert(:user)
2072 |> assign(:user, user)
2073 |> post("/api/v1/accounts/#{other_user.id}/follow")
2075 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2077 user = User.get_cached_by_id(user.id)
2081 |> assign(:user, user)
2082 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2084 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2086 user = User.get_cached_by_id(user.id)
2090 |> assign(:user, user)
2091 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2093 assert %{"id" => id} = json_response(conn, 200)
2094 assert id == to_string(other_user.id)
2097 test "following without reblogs" do
2098 follower = insert(:user)
2099 followed = insert(:user)
2100 other_user = insert(:user)
2104 |> assign(:user, follower)
2105 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2107 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2109 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2110 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2114 |> assign(:user, User.get_cached_by_id(follower.id))
2115 |> get("/api/v1/timelines/home")
2117 assert [] == json_response(conn, 200)
2121 |> assign(:user, follower)
2122 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2124 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2128 |> assign(:user, User.get_cached_by_id(follower.id))
2129 |> get("/api/v1/timelines/home")
2131 expected_activity_id = reblog.id
2132 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2135 test "following / unfollowing errors" do
2136 user = insert(:user)
2140 |> assign(:user, user)
2143 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2144 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2147 user = User.get_cached_by_id(user.id)
2148 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2149 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2151 # self follow via uri
2152 user = User.get_cached_by_id(user.id)
2153 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2154 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2156 # follow non existing user
2157 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2158 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2160 # follow non existing user via uri
2161 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2162 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2164 # unfollow non existing user
2165 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2166 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2169 describe "mute/unmute" do
2170 test "with notifications", %{conn: conn} do
2171 user = insert(:user)
2172 other_user = insert(:user)
2176 |> assign(:user, user)
2177 |> post("/api/v1/accounts/#{other_user.id}/mute")
2179 response = json_response(conn, 200)
2181 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2182 user = User.get_cached_by_id(user.id)
2186 |> assign(:user, user)
2187 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2189 response = json_response(conn, 200)
2190 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2193 test "without notifications", %{conn: conn} do
2194 user = insert(:user)
2195 other_user = insert(:user)
2199 |> assign(:user, user)
2200 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2202 response = json_response(conn, 200)
2204 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2205 user = User.get_cached_by_id(user.id)
2209 |> assign(:user, user)
2210 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2212 response = json_response(conn, 200)
2213 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2217 test "subscribing / unsubscribing to a user", %{conn: conn} do
2218 user = insert(:user)
2219 subscription_target = insert(:user)
2223 |> assign(:user, user)
2224 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2226 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2230 |> assign(:user, user)
2231 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2233 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2236 test "getting a list of mutes", %{conn: conn} do
2237 user = insert(:user)
2238 other_user = insert(:user)
2240 {:ok, user} = User.mute(user, other_user)
2244 |> assign(:user, user)
2245 |> get("/api/v1/mutes")
2247 other_user_id = to_string(other_user.id)
2248 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2251 test "blocking / unblocking a user", %{conn: conn} do
2252 user = insert(:user)
2253 other_user = insert(:user)
2257 |> assign(:user, user)
2258 |> post("/api/v1/accounts/#{other_user.id}/block")
2260 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2262 user = User.get_cached_by_id(user.id)
2266 |> assign(:user, user)
2267 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2269 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2272 test "getting a list of blocks", %{conn: conn} do
2273 user = insert(:user)
2274 other_user = insert(:user)
2276 {:ok, user} = User.block(user, other_user)
2280 |> assign(:user, user)
2281 |> get("/api/v1/blocks")
2283 other_user_id = to_string(other_user.id)
2284 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2287 test "blocking / unblocking a domain", %{conn: conn} do
2288 user = insert(:user)
2289 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2293 |> assign(:user, user)
2294 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2296 assert %{} = json_response(conn, 200)
2297 user = User.get_cached_by_ap_id(user.ap_id)
2298 assert User.blocks?(user, other_user)
2302 |> assign(:user, user)
2303 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2305 assert %{} = json_response(conn, 200)
2306 user = User.get_cached_by_ap_id(user.ap_id)
2307 refute User.blocks?(user, other_user)
2310 test "getting a list of domain blocks", %{conn: conn} do
2311 user = insert(:user)
2313 {:ok, user} = User.block_domain(user, "bad.site")
2314 {:ok, user} = User.block_domain(user, "even.worse.site")
2318 |> assign(:user, user)
2319 |> get("/api/v1/domain_blocks")
2321 domain_blocks = json_response(conn, 200)
2323 assert "bad.site" in domain_blocks
2324 assert "even.worse.site" in domain_blocks
2327 test "unimplemented follow_requests, blocks, domain blocks" do
2328 user = insert(:user)
2330 ["blocks", "domain_blocks", "follow_requests"]
2331 |> Enum.each(fn endpoint ->
2334 |> assign(:user, user)
2335 |> get("/api/v1/#{endpoint}")
2337 assert [] = json_response(conn, 200)
2341 test "returns the favorites of a user", %{conn: conn} do
2342 user = insert(:user)
2343 other_user = insert(:user)
2345 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2346 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2348 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2352 |> assign(:user, user)
2353 |> get("/api/v1/favourites")
2355 assert [status] = json_response(first_conn, 200)
2356 assert status["id"] == to_string(activity.id)
2358 assert [{"link", _link_header}] =
2359 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2361 # Honours query params
2362 {:ok, second_activity} =
2363 CommonAPI.post(other_user, %{
2365 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2368 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2370 last_like = status["id"]
2374 |> assign(:user, user)
2375 |> get("/api/v1/favourites?since_id=#{last_like}")
2377 assert [second_status] = json_response(second_conn, 200)
2378 assert second_status["id"] == to_string(second_activity.id)
2382 |> assign(:user, user)
2383 |> get("/api/v1/favourites?limit=0")
2385 assert [] = json_response(third_conn, 200)
2388 describe "getting favorites timeline of specified user" do
2390 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2391 [current_user: current_user, user: user]
2394 test "returns list of statuses favorited by specified user", %{
2396 current_user: current_user,
2399 [activity | _] = insert_pair(:note_activity)
2400 CommonAPI.favorite(activity.id, user)
2404 |> assign(:user, current_user)
2405 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2406 |> json_response(:ok)
2410 assert length(response) == 1
2411 assert like["id"] == activity.id
2414 test "returns favorites for specified user_id when user is not logged in", %{
2418 activity = insert(:note_activity)
2419 CommonAPI.favorite(activity.id, user)
2423 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2424 |> json_response(:ok)
2426 assert length(response) == 1
2429 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2431 current_user: current_user,
2435 CommonAPI.post(current_user, %{
2436 "status" => "Hi @#{user.nickname}!",
2437 "visibility" => "direct"
2440 CommonAPI.favorite(direct.id, user)
2444 |> assign(:user, current_user)
2445 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2446 |> json_response(:ok)
2448 assert length(response) == 1
2450 anonymous_response =
2452 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2453 |> json_response(:ok)
2455 assert Enum.empty?(anonymous_response)
2458 test "does not return others' favorited DM when user is not one of recipients", %{
2460 current_user: current_user,
2463 user_two = insert(:user)
2466 CommonAPI.post(user_two, %{
2467 "status" => "Hi @#{user.nickname}!",
2468 "visibility" => "direct"
2471 CommonAPI.favorite(direct.id, user)
2475 |> assign(:user, current_user)
2476 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2477 |> json_response(:ok)
2479 assert Enum.empty?(response)
2482 test "paginates favorites using since_id and max_id", %{
2484 current_user: current_user,
2487 activities = insert_list(10, :note_activity)
2489 Enum.each(activities, fn activity ->
2490 CommonAPI.favorite(activity.id, user)
2493 third_activity = Enum.at(activities, 2)
2494 seventh_activity = Enum.at(activities, 6)
2498 |> assign(:user, current_user)
2499 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2500 since_id: third_activity.id,
2501 max_id: seventh_activity.id
2503 |> json_response(:ok)
2505 assert length(response) == 3
2506 refute third_activity in response
2507 refute seventh_activity in response
2510 test "limits favorites using limit parameter", %{
2512 current_user: current_user,
2516 |> insert_list(:note_activity)
2517 |> Enum.each(fn activity ->
2518 CommonAPI.favorite(activity.id, user)
2523 |> assign(:user, current_user)
2524 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2525 |> json_response(:ok)
2527 assert length(response) == 3
2530 test "returns empty response when user does not have any favorited statuses", %{
2532 current_user: current_user,
2537 |> assign(:user, current_user)
2538 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2539 |> json_response(:ok)
2541 assert Enum.empty?(response)
2544 test "returns 404 error when specified user is not exist", %{conn: conn} do
2545 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2547 assert json_response(conn, 404) == %{"error" => "Record not found"}
2550 test "returns 403 error when user has hidden own favorites", %{
2552 current_user: current_user
2554 user = insert(:user, %{info: %{hide_favorites: true}})
2555 activity = insert(:note_activity)
2556 CommonAPI.favorite(activity.id, user)
2560 |> assign(:user, current_user)
2561 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2563 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2566 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2567 user = insert(:user)
2568 activity = insert(:note_activity)
2569 CommonAPI.favorite(activity.id, user)
2573 |> assign(:user, current_user)
2574 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2576 assert user.info.hide_favorites
2577 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2581 test "get instance information", %{conn: conn} do
2582 conn = get(conn, "/api/v1/instance")
2583 assert result = json_response(conn, 200)
2585 email = Pleroma.Config.get([:instance, :email])
2586 # Note: not checking for "max_toot_chars" since it's optional
2592 "email" => from_config_email,
2594 "streaming_api" => _
2599 "registrations" => _,
2603 assert email == from_config_email
2606 test "get instance stats", %{conn: conn} do
2607 user = insert(:user, %{local: true})
2609 user2 = insert(:user, %{local: true})
2610 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2612 insert(:user, %{local: false, nickname: "u@peer1.com"})
2613 insert(:user, %{local: false, nickname: "u@peer2.com"})
2615 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2617 # Stats should count users with missing or nil `info.deactivated` value
2618 user = User.get_cached_by_id(user.id)
2619 info_change = Changeset.change(user.info, %{deactivated: nil})
2623 |> Changeset.change()
2624 |> Changeset.put_embed(:info, info_change)
2625 |> User.update_and_set_cache()
2627 Pleroma.Stats.update_stats()
2629 conn = get(conn, "/api/v1/instance")
2631 assert result = json_response(conn, 200)
2633 stats = result["stats"]
2636 assert stats["user_count"] == 1
2637 assert stats["status_count"] == 1
2638 assert stats["domain_count"] == 2
2641 test "get peers", %{conn: conn} do
2642 insert(:user, %{local: false, nickname: "u@peer1.com"})
2643 insert(:user, %{local: false, nickname: "u@peer2.com"})
2645 Pleroma.Stats.update_stats()
2647 conn = get(conn, "/api/v1/instance/peers")
2649 assert result = json_response(conn, 200)
2651 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2654 test "put settings", %{conn: conn} do
2655 user = insert(:user)
2659 |> assign(:user, user)
2660 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2662 assert _result = json_response(conn, 200)
2664 user = User.get_cached_by_ap_id(user.ap_id)
2665 assert user.info.settings == %{"programming" => "socks"}
2668 describe "pinned statuses" do
2670 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2672 user = insert(:user)
2673 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2675 [user: user, activity: activity]
2678 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2679 {:ok, _} = CommonAPI.pin(activity.id, user)
2683 |> assign(:user, user)
2684 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2685 |> json_response(200)
2687 id_str = to_string(activity.id)
2689 assert [%{"id" => ^id_str, "pinned" => true}] = result
2692 test "pin status", %{conn: conn, user: user, activity: activity} do
2693 id_str = to_string(activity.id)
2695 assert %{"id" => ^id_str, "pinned" => true} =
2697 |> assign(:user, user)
2698 |> post("/api/v1/statuses/#{activity.id}/pin")
2699 |> json_response(200)
2701 assert [%{"id" => ^id_str, "pinned" => true}] =
2703 |> assign(:user, user)
2704 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2705 |> json_response(200)
2708 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2709 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2713 |> assign(:user, user)
2714 |> post("/api/v1/statuses/#{dm.id}/pin")
2716 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2719 test "unpin status", %{conn: conn, user: user, activity: activity} do
2720 {:ok, _} = CommonAPI.pin(activity.id, user)
2722 id_str = to_string(activity.id)
2723 user = refresh_record(user)
2725 assert %{"id" => ^id_str, "pinned" => false} =
2727 |> assign(:user, user)
2728 |> post("/api/v1/statuses/#{activity.id}/unpin")
2729 |> json_response(200)
2733 |> assign(:user, user)
2734 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2735 |> json_response(200)
2738 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2741 |> assign(:user, user)
2742 |> post("/api/v1/statuses/1/unpin")
2744 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2747 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2748 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2750 id_str_one = to_string(activity_one.id)
2752 assert %{"id" => ^id_str_one, "pinned" => true} =
2754 |> assign(:user, user)
2755 |> post("/api/v1/statuses/#{id_str_one}/pin")
2756 |> json_response(200)
2758 user = refresh_record(user)
2760 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2762 |> assign(:user, user)
2763 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2764 |> json_response(400)
2770 Pleroma.Config.put([:rich_media, :enabled], true)
2773 Pleroma.Config.put([:rich_media, :enabled], false)
2776 user = insert(:user)
2780 test "returns rich-media card", %{conn: conn, user: user} do
2781 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2784 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2785 "provider_name" => "example.com",
2786 "provider_url" => "https://example.com",
2787 "title" => "The Rock",
2789 "url" => "https://example.com/ogp",
2791 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2794 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2795 "title" => "The Rock",
2796 "type" => "video.movie",
2797 "url" => "https://example.com/ogp",
2799 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2806 |> get("/api/v1/statuses/#{activity.id}/card")
2807 |> json_response(200)
2809 assert response == card_data
2811 # works with private posts
2813 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2817 |> assign(:user, user)
2818 |> get("/api/v1/statuses/#{activity.id}/card")
2819 |> json_response(200)
2821 assert response_two == card_data
2824 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2826 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2830 |> get("/api/v1/statuses/#{activity.id}/card")
2831 |> json_response(:ok)
2833 assert response == %{
2835 "title" => "Pleroma",
2836 "description" => "",
2838 "provider_name" => "example.com",
2839 "provider_url" => "https://example.com",
2840 "url" => "https://example.com/ogp-missing-data",
2843 "title" => "Pleroma",
2844 "type" => "website",
2845 "url" => "https://example.com/ogp-missing-data"
2853 user = insert(:user)
2854 for_user = insert(:user)
2857 CommonAPI.post(user, %{
2858 "status" => "heweoo?"
2862 CommonAPI.post(user, %{
2863 "status" => "heweoo!"
2868 |> assign(:user, for_user)
2869 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2871 assert json_response(response1, 200)["bookmarked"] == true
2875 |> assign(:user, for_user)
2876 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2878 assert json_response(response2, 200)["bookmarked"] == true
2882 |> assign(:user, for_user)
2883 |> get("/api/v1/bookmarks")
2885 assert [json_response(response2, 200), json_response(response1, 200)] ==
2886 json_response(bookmarks, 200)
2890 |> assign(:user, for_user)
2891 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2893 assert json_response(response1, 200)["bookmarked"] == false
2897 |> assign(:user, for_user)
2898 |> get("/api/v1/bookmarks")
2900 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2903 describe "conversation muting" do
2905 user = insert(:user)
2906 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2908 [user: user, activity: activity]
2911 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2912 id_str = to_string(activity.id)
2914 assert %{"id" => ^id_str, "muted" => true} =
2916 |> assign(:user, user)
2917 |> post("/api/v1/statuses/#{activity.id}/mute")
2918 |> json_response(200)
2921 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2922 {:ok, _} = CommonAPI.add_mute(user, activity)
2926 |> assign(:user, user)
2927 |> post("/api/v1/statuses/#{activity.id}/mute")
2929 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2932 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2933 {:ok, _} = CommonAPI.add_mute(user, activity)
2935 id_str = to_string(activity.id)
2936 user = refresh_record(user)
2938 assert %{"id" => ^id_str, "muted" => false} =
2940 |> assign(:user, user)
2941 |> post("/api/v1/statuses/#{activity.id}/unmute")
2942 |> json_response(200)
2946 describe "reports" do
2948 reporter = insert(:user)
2949 target_user = insert(:user)
2951 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2953 [reporter: reporter, target_user: target_user, activity: activity]
2956 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2957 assert %{"action_taken" => false, "id" => _} =
2959 |> assign(:user, reporter)
2960 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2961 |> json_response(200)
2964 test "submit a report with statuses and comment", %{
2967 target_user: target_user,
2970 assert %{"action_taken" => false, "id" => _} =
2972 |> assign(:user, reporter)
2973 |> post("/api/v1/reports", %{
2974 "account_id" => target_user.id,
2975 "status_ids" => [activity.id],
2976 "comment" => "bad status!",
2977 "forward" => "false"
2979 |> json_response(200)
2982 test "account_id is required", %{
2987 assert %{"error" => "Valid `account_id` required"} =
2989 |> assign(:user, reporter)
2990 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2991 |> json_response(400)
2994 test "comment must be up to the size specified in the config", %{
2997 target_user: target_user
2999 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
3000 comment = String.pad_trailing("a", max_size + 1, "a")
3002 error = %{"error" => "Comment must be up to #{max_size} characters"}
3006 |> assign(:user, reporter)
3007 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3008 |> json_response(400)
3011 test "returns error when account is not exist", %{
3018 |> assign(:user, reporter)
3019 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3021 assert json_response(conn, 400) == %{"error" => "Account not found"}
3025 describe "link headers" do
3026 test "preserves parameters in link headers", %{conn: conn} do
3027 user = insert(:user)
3028 other_user = insert(:user)
3031 CommonAPI.post(other_user, %{
3032 "status" => "hi @#{user.nickname}",
3033 "visibility" => "public"
3037 CommonAPI.post(other_user, %{
3038 "status" => "hi @#{user.nickname}",
3039 "visibility" => "public"
3042 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3043 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3047 |> assign(:user, user)
3048 |> get("/api/v1/notifications", %{media_only: true})
3050 assert [link_header] = get_resp_header(conn, "link")
3051 assert link_header =~ ~r/media_only=true/
3052 assert link_header =~ ~r/min_id=#{notification2.id}/
3053 assert link_header =~ ~r/max_id=#{notification1.id}/
3057 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3058 # Need to set an old-style integer ID to reproduce the problem
3059 # (these are no longer assigned to new accounts but were preserved
3060 # for existing accounts during the migration to flakeIDs)
3061 user_one = insert(:user, %{id: 1212})
3062 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3066 |> get("/api/v1/accounts/#{user_one.id}")
3070 |> get("/api/v1/accounts/#{user_two.nickname}")
3074 |> get("/api/v1/accounts/#{user_two.id}")
3076 acc_one = json_response(resp_one, 200)
3077 acc_two = json_response(resp_two, 200)
3078 acc_three = json_response(resp_three, 200)
3079 refute acc_one == acc_two
3080 assert acc_two == acc_three
3083 describe "custom emoji" do
3084 test "with tags", %{conn: conn} do
3087 |> get("/api/v1/custom_emojis")
3088 |> json_response(200)
3090 assert Map.has_key?(emoji, "shortcode")
3091 assert Map.has_key?(emoji, "static_url")
3092 assert Map.has_key?(emoji, "tags")
3093 assert is_list(emoji["tags"])
3094 assert Map.has_key?(emoji, "category")
3095 assert Map.has_key?(emoji, "url")
3096 assert Map.has_key?(emoji, "visible_in_picker")
3100 describe "index/2 redirections" do
3101 setup %{conn: conn} do
3105 signing_salt: "cooldude"
3110 |> Plug.Session.call(Plug.Session.init(session_opts))
3113 test_path = "/web/statuses/test"
3114 %{conn: conn, path: test_path}
3117 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3118 conn = get(conn, path)
3120 assert conn.status == 302
3121 assert redirected_to(conn) == "/web/login"
3124 test "redirects not logged-in users to the login page on private instances", %{
3128 is_public = Pleroma.Config.get([:instance, :public])
3129 Pleroma.Config.put([:instance, :public], false)
3131 conn = get(conn, path)
3133 assert conn.status == 302
3134 assert redirected_to(conn) == "/web/login"
3136 Pleroma.Config.put([:instance, :public], is_public)
3139 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3140 token = insert(:oauth_token)
3144 |> assign(:user, token.user)
3145 |> put_session(:oauth_token, token.token)
3148 assert conn.status == 200
3151 test "saves referer path to session", %{conn: conn, path: path} do
3152 conn = get(conn, path)
3153 return_to = Plug.Conn.get_session(conn, :return_to)
3155 assert return_to == path
3158 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3159 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3160 auth = insert(:oauth_authorization, app: app)
3164 |> put_session(:return_to, path)
3165 |> get("/web/login", %{code: auth.token})
3167 assert conn.status == 302
3168 assert redirected_to(conn) == path
3171 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3172 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3173 auth = insert(:oauth_authorization, app: app)
3175 conn = get(conn, "/web/login", %{code: auth.token})
3177 assert conn.status == 302
3178 assert redirected_to(conn) == "/web/getting-started"
3182 describe "scheduled activities" do
3183 test "creates a scheduled activity", %{conn: conn} do
3184 user = insert(:user)
3185 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3189 |> assign(:user, user)
3190 |> post("/api/v1/statuses", %{
3191 "status" => "scheduled",
3192 "scheduled_at" => scheduled_at
3195 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3196 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3197 assert [] == Repo.all(Activity)
3200 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3201 user = insert(:user)
3202 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3204 file = %Plug.Upload{
3205 content_type: "image/jpg",
3206 path: Path.absname("test/fixtures/image.jpg"),
3207 filename: "an_image.jpg"
3210 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3214 |> assign(:user, user)
3215 |> post("/api/v1/statuses", %{
3216 "media_ids" => [to_string(upload.id)],
3217 "status" => "scheduled",
3218 "scheduled_at" => scheduled_at
3221 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3222 assert %{"type" => "image"} = media_attachment
3225 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3227 user = insert(:user)
3230 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3234 |> assign(:user, user)
3235 |> post("/api/v1/statuses", %{
3236 "status" => "not scheduled",
3237 "scheduled_at" => scheduled_at
3240 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3241 assert [] == Repo.all(ScheduledActivity)
3244 test "returns error when daily user limit is exceeded", %{conn: conn} do
3245 user = insert(:user)
3248 NaiveDateTime.utc_now()
3249 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3250 |> NaiveDateTime.to_iso8601()
3252 attrs = %{params: %{}, scheduled_at: today}
3253 {:ok, _} = ScheduledActivity.create(user, attrs)
3254 {:ok, _} = ScheduledActivity.create(user, attrs)
3258 |> assign(:user, user)
3259 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3261 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3264 test "returns error when total user limit is exceeded", %{conn: conn} do
3265 user = insert(:user)
3268 NaiveDateTime.utc_now()
3269 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3270 |> NaiveDateTime.to_iso8601()
3273 NaiveDateTime.utc_now()
3274 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3275 |> NaiveDateTime.to_iso8601()
3277 attrs = %{params: %{}, scheduled_at: today}
3278 {:ok, _} = ScheduledActivity.create(user, attrs)
3279 {:ok, _} = ScheduledActivity.create(user, attrs)
3280 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3284 |> assign(:user, user)
3285 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3287 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3290 test "shows scheduled activities", %{conn: conn} do
3291 user = insert(:user)
3292 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3293 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3294 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3295 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3299 |> assign(:user, user)
3304 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3306 result = json_response(conn_res, 200)
3307 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3312 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3314 result = json_response(conn_res, 200)
3315 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3320 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3322 result = json_response(conn_res, 200)
3323 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3326 test "shows a scheduled activity", %{conn: conn} do
3327 user = insert(:user)
3328 scheduled_activity = insert(:scheduled_activity, user: user)
3332 |> assign(:user, user)
3333 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3335 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3336 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3340 |> assign(:user, user)
3341 |> get("/api/v1/scheduled_statuses/404")
3343 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3346 test "updates a scheduled activity", %{conn: conn} do
3347 user = insert(:user)
3348 scheduled_activity = insert(:scheduled_activity, user: user)
3351 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3355 |> assign(:user, user)
3356 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3357 scheduled_at: new_scheduled_at
3360 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3361 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3365 |> assign(:user, user)
3366 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3368 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3371 test "deletes a scheduled activity", %{conn: conn} do
3372 user = insert(:user)
3373 scheduled_activity = insert(:scheduled_activity, user: user)
3377 |> assign(:user, user)
3378 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3380 assert %{} = json_response(res_conn, 200)
3381 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3385 |> assign(:user, user)
3386 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3388 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3392 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3393 user1 = insert(:user)
3394 user2 = insert(:user)
3395 user3 = insert(:user)
3397 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3399 # Reply to status from another user
3402 |> assign(:user, user2)
3403 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3405 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3407 activity = Activity.get_by_id_with_object(id)
3409 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3410 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3412 # Reblog from the third user
3415 |> assign(:user, user3)
3416 |> post("/api/v1/statuses/#{activity.id}/reblog")
3418 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3419 json_response(conn2, 200)
3421 assert to_string(activity.id) == id
3423 # Getting third user status
3426 |> assign(:user, user3)
3427 |> get("api/v1/timelines/home")
3429 [reblogged_activity] = json_response(conn3, 200)
3431 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3433 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3434 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3437 describe "create account by app" do
3438 test "Account registration via Application", %{conn: conn} do
3441 |> post("/api/v1/apps", %{
3442 client_name: "client_name",
3443 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3444 scopes: "read, write, follow"
3448 "client_id" => client_id,
3449 "client_secret" => client_secret,
3451 "name" => "client_name",
3452 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3455 } = json_response(conn, 200)
3459 |> post("/oauth/token", %{
3460 grant_type: "client_credentials",
3461 client_id: client_id,
3462 client_secret: client_secret
3465 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3466 json_response(conn, 200)
3469 token_from_db = Repo.get_by(Token, token: token)
3470 assert token_from_db
3472 assert scope == "read write follow"
3476 |> put_req_header("authorization", "Bearer " <> token)
3477 |> post("/api/v1/accounts", %{
3479 email: "lain@example.org",
3480 password: "PlzDontHackLain",
3485 "access_token" => token,
3486 "created_at" => _created_at,
3488 "token_type" => "Bearer"
3489 } = json_response(conn, 200)
3491 token_from_db = Repo.get_by(Token, token: token)
3492 assert token_from_db
3493 token_from_db = Repo.preload(token_from_db, :user)
3494 assert token_from_db.user
3496 assert token_from_db.user.info.confirmation_pending
3499 test "rate limit", %{conn: conn} do
3500 app_token = insert(:oauth_token, user: nil)
3503 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3504 |> Map.put(:remote_ip, {15, 15, 15, 15})
3509 |> post("/api/v1/accounts", %{
3510 username: "#{i}lain",
3511 email: "#{i}lain@example.org",
3512 password: "PlzDontHackLain",
3517 "access_token" => token,
3518 "created_at" => _created_at,
3520 "token_type" => "Bearer"
3521 } = json_response(conn, 200)
3523 token_from_db = Repo.get_by(Token, token: token)
3524 assert token_from_db
3525 token_from_db = Repo.preload(token_from_db, :user)
3526 assert token_from_db.user
3528 assert token_from_db.user.info.confirmation_pending
3533 |> post("/api/v1/accounts", %{
3535 email: "6lain@example.org",
3536 password: "PlzDontHackLain",
3540 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3544 describe "GET /api/v1/polls/:id" do
3545 test "returns poll entity for object id", %{conn: conn} do
3546 user = insert(:user)
3549 CommonAPI.post(user, %{
3550 "status" => "Pleroma does",
3551 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3554 object = Object.normalize(activity)
3558 |> assign(:user, user)
3559 |> get("/api/v1/polls/#{object.id}")
3561 response = json_response(conn, 200)
3562 id = to_string(object.id)
3563 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3566 test "does not expose polls for private statuses", %{conn: conn} do
3567 user = insert(:user)
3568 other_user = insert(:user)
3571 CommonAPI.post(user, %{
3572 "status" => "Pleroma does",
3573 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3574 "visibility" => "private"
3577 object = Object.normalize(activity)
3581 |> assign(:user, other_user)
3582 |> get("/api/v1/polls/#{object.id}")
3584 assert json_response(conn, 404)
3588 describe "POST /api/v1/polls/:id/votes" do
3589 test "votes are added to the poll", %{conn: conn} do
3590 user = insert(:user)
3591 other_user = insert(:user)
3594 CommonAPI.post(user, %{
3595 "status" => "A very delicious sandwich",
3597 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3603 object = Object.normalize(activity)
3607 |> assign(:user, other_user)
3608 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3610 assert json_response(conn, 200)
3611 object = Object.get_by_id(object.id)
3613 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3618 test "author can't vote", %{conn: conn} do
3619 user = insert(:user)
3622 CommonAPI.post(user, %{
3623 "status" => "Am I cute?",
3624 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3627 object = Object.normalize(activity)
3630 |> assign(:user, user)
3631 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3632 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3634 object = Object.get_by_id(object.id)
3636 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3639 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3640 user = insert(:user)
3641 other_user = insert(:user)
3644 CommonAPI.post(user, %{
3645 "status" => "The glass is",
3646 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3649 object = Object.normalize(activity)
3652 |> assign(:user, other_user)
3653 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3654 |> json_response(422) == %{"error" => "Too many choices"}
3656 object = Object.get_by_id(object.id)
3658 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3663 test "does not allow choice index to be greater than options count", %{conn: conn} do
3664 user = insert(:user)
3665 other_user = insert(:user)
3668 CommonAPI.post(user, %{
3669 "status" => "Am I cute?",
3670 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3673 object = Object.normalize(activity)
3677 |> assign(:user, other_user)
3678 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3680 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3683 test "returns 404 error when object is not exist", %{conn: conn} do
3684 user = insert(:user)
3688 |> assign(:user, user)
3689 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3691 assert json_response(conn, 404) == %{"error" => "Record not found"}
3694 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3695 user = insert(:user)
3696 other_user = insert(:user)
3699 CommonAPI.post(user, %{
3700 "status" => "Am I cute?",
3701 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3702 "visibility" => "private"
3705 object = Object.normalize(activity)
3709 |> assign(:user, other_user)
3710 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3712 assert json_response(conn, 404) == %{"error" => "Record not found"}
3716 describe "GET /api/v1/statuses/:id/favourited_by" do
3718 user = insert(:user)
3719 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3723 |> assign(:user, user)
3725 [conn: conn, activity: activity]
3728 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3729 other_user = insert(:user)
3730 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3734 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3735 |> json_response(:ok)
3737 [%{"id" => id}] = response
3739 assert id == other_user.id
3742 test "returns empty array when status has not been favorited yet", %{
3748 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3749 |> json_response(:ok)
3751 assert Enum.empty?(response)
3754 test "does not return users who have favorited the status but are blocked", %{
3755 conn: %{assigns: %{user: user}} = conn,
3758 other_user = insert(:user)
3759 {:ok, user} = User.block(user, other_user)
3761 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3765 |> assign(:user, user)
3766 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3767 |> json_response(:ok)
3769 assert Enum.empty?(response)
3772 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3773 other_user = insert(:user)
3774 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3778 |> assign(:user, nil)
3779 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3780 |> json_response(:ok)
3782 [%{"id" => id}] = response
3783 assert id == other_user.id
3787 describe "GET /api/v1/statuses/:id/reblogged_by" do
3789 user = insert(:user)
3790 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3794 |> assign(:user, user)
3796 [conn: conn, activity: activity]
3799 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3800 other_user = insert(:user)
3801 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3805 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3806 |> json_response(:ok)
3808 [%{"id" => id}] = response
3810 assert id == other_user.id
3813 test "returns empty array when status has not been reblogged yet", %{
3819 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3820 |> json_response(:ok)
3822 assert Enum.empty?(response)
3825 test "does not return users who have reblogged the status but are blocked", %{
3826 conn: %{assigns: %{user: user}} = conn,
3829 other_user = insert(:user)
3830 {:ok, user} = User.block(user, other_user)
3832 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3836 |> assign(:user, user)
3837 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3838 |> json_response(:ok)
3840 assert Enum.empty?(response)
3843 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3844 other_user = insert(:user)
3845 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3849 |> assign(:user, nil)
3850 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3851 |> json_response(:ok)
3853 [%{"id" => id}] = response
3854 assert id == other_user.id
3858 describe "POST /auth/password, with valid parameters" do
3859 setup %{conn: conn} do
3860 user = insert(:user)
3861 conn = post(conn, "/auth/password?email=#{user.email}")
3862 %{conn: conn, user: user}
3865 test "it returns 204", %{conn: conn} do
3866 assert json_response(conn, :no_content)
3869 test "it creates a PasswordResetToken record for user", %{user: user} do
3870 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3874 test "it sends an email to user", %{user: user} do
3875 ObanHelpers.perform_all()
3876 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3878 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3879 notify_email = Pleroma.Config.get([:instance, :notify_email])
3880 instance_name = Pleroma.Config.get([:instance, :name])
3883 from: {instance_name, notify_email},
3884 to: {user.name, user.email},
3885 html_body: email.html_body
3890 describe "POST /auth/password, with invalid parameters" do
3892 user = insert(:user)
3896 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3897 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3898 assert conn.status == 404
3899 assert conn.resp_body == ""
3902 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3903 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3904 conn = post(conn, "/auth/password?email=#{user.email}")
3905 assert conn.status == 400
3906 assert conn.resp_body == ""
3910 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3912 setting = Pleroma.Config.get([:instance, :account_activation_required])
3915 Pleroma.Config.put([:instance, :account_activation_required], true)
3916 on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
3919 user = insert(:user)
3920 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3924 |> Changeset.change()
3925 |> Changeset.put_embed(:info, info_change)
3928 assert user.info.confirmation_pending
3933 test "resend account confirmation email", %{conn: conn, user: user} do
3935 |> assign(:user, user)
3936 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3937 |> json_response(:no_content)
3939 ObanHelpers.perform_all()
3941 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3942 notify_email = Pleroma.Config.get([:instance, :notify_email])
3943 instance_name = Pleroma.Config.get([:instance, :name])
3946 from: {instance_name, notify_email},
3947 to: {user.name, user.email},
3948 html_body: email.html_body