1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.ActivityExpiration
11 alias Pleroma.Notification
14 alias Pleroma.ScheduledActivity
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
156 # An activity that will expire:
158 NaiveDateTime.utc_now()
159 |> NaiveDateTime.add(:timer.minutes(120), :millisecond)
160 |> NaiveDateTime.truncate(:second)
164 |> post("api/v1/statuses", %{
165 "status" => "oolong",
166 "expires_at" => expires_at
169 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
170 assert activity = Activity.get_by_id(fourth_id)
171 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
172 assert expiration.scheduled_at == expires_at
173 assert fourth_response["pleroma"]["expires_in"] > 0
176 test "replying to a status", %{conn: conn} do
178 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
182 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
184 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
186 activity = Activity.get_by_id(id)
188 assert activity.data["context"] == replied_to.data["context"]
189 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
192 test "replying to a direct message with visibility other than direct", %{conn: conn} do
194 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
196 Enum.each(["public", "private", "unlisted"], fn visibility ->
199 |> post("/api/v1/statuses", %{
200 "status" => "@#{user.nickname} hey",
201 "in_reply_to_id" => replied_to.id,
202 "visibility" => visibility
205 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
209 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
212 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
214 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
215 assert Activity.get_by_id(id)
218 test "posting a sensitive status", %{conn: conn} do
221 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
223 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
224 assert Activity.get_by_id(id)
227 test "posting a fake status", %{conn: conn} do
230 |> post("/api/v1/statuses", %{
232 "\"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"
235 real_status = json_response(real_conn, 200)
238 assert Object.get_by_ap_id(real_status["uri"])
242 |> Map.put("id", nil)
243 |> Map.put("url", nil)
244 |> Map.put("uri", nil)
245 |> Map.put("created_at", nil)
246 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
250 |> post("/api/v1/statuses", %{
252 "\"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",
256 fake_status = json_response(fake_conn, 200)
259 refute Object.get_by_ap_id(fake_status["uri"])
263 |> Map.put("id", nil)
264 |> Map.put("url", nil)
265 |> Map.put("uri", nil)
266 |> Map.put("created_at", nil)
267 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
269 assert real_status == fake_status
272 test "posting a status with OGP link preview", %{conn: conn} do
273 Pleroma.Config.put([:rich_media, :enabled], true)
277 |> post("/api/v1/statuses", %{
278 "status" => "https://example.com/ogp"
281 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
282 assert Activity.get_by_id(id)
283 Pleroma.Config.put([:rich_media, :enabled], false)
286 test "posting a direct status", %{conn: conn} do
287 user2 = insert(:user)
288 content = "direct cofe @#{user2.nickname}"
292 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
294 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
295 assert activity = Activity.get_by_id(id)
296 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
297 assert activity.data["to"] == [user2.ap_id]
298 assert activity.data["cc"] == []
302 describe "posting polls" do
303 test "posting a poll", %{conn: conn} do
305 time = NaiveDateTime.utc_now()
309 |> assign(:user, user)
310 |> post("/api/v1/statuses", %{
311 "status" => "Who is the #bestgrill?",
312 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
315 response = json_response(conn, 200)
317 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
318 title in ["Rei", "Asuka", "Misato"]
321 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
322 refute response["poll"]["expred"]
325 test "option limit is enforced", %{conn: conn} do
327 limit = Pleroma.Config.get([:instance, :poll_limits, :max_options])
331 |> assign(:user, user)
332 |> post("/api/v1/statuses", %{
334 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
337 %{"error" => error} = json_response(conn, 422)
338 assert error == "Poll can't contain more than #{limit} options"
341 test "option character limit is enforced", %{conn: conn} do
343 limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars])
347 |> assign(:user, user)
348 |> post("/api/v1/statuses", %{
351 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
356 %{"error" => error} = json_response(conn, 422)
357 assert error == "Poll options cannot be longer than #{limit} characters each"
360 test "minimal date limit is enforced", %{conn: conn} do
362 limit = Pleroma.Config.get([:instance, :poll_limits, :min_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 soon"
379 test "maximum date limit is enforced", %{conn: conn} do
381 limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration])
385 |> assign(:user, user)
386 |> post("/api/v1/statuses", %{
387 "status" => "imagine arbitrary limits",
389 "options" => ["this post was made by pleroma gang"],
390 "expires_in" => limit + 1
394 %{"error" => error} = json_response(conn, 422)
395 assert error == "Expiration date is too far in the future"
399 test "direct timeline", %{conn: conn} do
400 user_one = insert(:user)
401 user_two = insert(:user)
403 {:ok, user_two} = User.follow(user_two, user_one)
406 CommonAPI.post(user_one, %{
407 "status" => "Hi @#{user_two.nickname}!",
408 "visibility" => "direct"
411 {:ok, _follower_only} =
412 CommonAPI.post(user_one, %{
413 "status" => "Hi @#{user_two.nickname}!",
414 "visibility" => "private"
417 # Only direct should be visible here
420 |> assign(:user, user_two)
421 |> get("api/v1/timelines/direct")
423 [status] = json_response(res_conn, 200)
425 assert %{"visibility" => "direct"} = status
426 assert status["url"] != direct.data["id"]
428 # User should be able to see their own direct message
431 |> assign(:user, user_one)
432 |> get("api/v1/timelines/direct")
434 [status] = json_response(res_conn, 200)
436 assert %{"visibility" => "direct"} = status
438 # Both should be visible here
441 |> assign(:user, user_two)
442 |> get("api/v1/timelines/home")
444 [_s1, _s2] = json_response(res_conn, 200)
447 Enum.each(1..20, fn _ ->
449 CommonAPI.post(user_one, %{
450 "status" => "Hi @#{user_two.nickname}!",
451 "visibility" => "direct"
457 |> assign(:user, user_two)
458 |> get("api/v1/timelines/direct")
460 statuses = json_response(res_conn, 200)
461 assert length(statuses) == 20
465 |> assign(:user, user_two)
466 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
468 [status] = json_response(res_conn, 200)
470 assert status["url"] != direct.data["id"]
473 test "Conversations", %{conn: conn} do
474 user_one = insert(:user)
475 user_two = insert(:user)
476 user_three = insert(:user)
478 {:ok, user_two} = User.follow(user_two, user_one)
481 CommonAPI.post(user_one, %{
482 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
483 "visibility" => "direct"
486 {:ok, _follower_only} =
487 CommonAPI.post(user_one, %{
488 "status" => "Hi @#{user_two.nickname}!",
489 "visibility" => "private"
494 |> assign(:user, user_one)
495 |> get("/api/v1/conversations")
497 assert response = json_response(res_conn, 200)
502 "accounts" => res_accounts,
503 "last_status" => res_last_status,
508 account_ids = Enum.map(res_accounts, & &1["id"])
509 assert length(res_accounts) == 2
510 assert user_two.id in account_ids
511 assert user_three.id in account_ids
512 assert is_binary(res_id)
513 assert unread == true
514 assert res_last_status["id"] == direct.id
516 # Apparently undocumented API endpoint
519 |> assign(:user, user_one)
520 |> post("/api/v1/conversations/#{res_id}/read")
522 assert response = json_response(res_conn, 200)
523 assert length(response["accounts"]) == 2
524 assert response["last_status"]["id"] == direct.id
525 assert response["unread"] == false
527 # (vanilla) Mastodon frontend behaviour
530 |> assign(:user, user_one)
531 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
533 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
536 test "doesn't include DMs from blocked users", %{conn: conn} do
537 blocker = insert(:user)
538 blocked = insert(:user)
540 {:ok, blocker} = User.block(blocker, blocked)
542 {:ok, _blocked_direct} =
543 CommonAPI.post(blocked, %{
544 "status" => "Hi @#{blocker.nickname}!",
545 "visibility" => "direct"
549 CommonAPI.post(user, %{
550 "status" => "Hi @#{blocker.nickname}!",
551 "visibility" => "direct"
556 |> assign(:user, user)
557 |> get("api/v1/timelines/direct")
559 [status] = json_response(res_conn, 200)
560 assert status["id"] == direct.id
563 test "verify_credentials", %{conn: conn} do
568 |> assign(:user, user)
569 |> get("/api/v1/accounts/verify_credentials")
571 response = json_response(conn, 200)
573 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
574 assert response["pleroma"]["chat_token"]
575 assert id == to_string(user.id)
578 test "verify_credentials default scope unlisted", %{conn: conn} do
579 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
583 |> assign(:user, user)
584 |> get("/api/v1/accounts/verify_credentials")
586 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
587 assert id == to_string(user.id)
590 test "apps/verify_credentials", %{conn: conn} do
591 token = insert(:oauth_token)
595 |> assign(:user, token.user)
596 |> assign(:token, token)
597 |> get("/api/v1/apps/verify_credentials")
599 app = Repo.preload(token, :app).app
602 "name" => app.client_name,
603 "website" => app.website,
604 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
607 assert expected == json_response(conn, 200)
610 test "user avatar can be set", %{conn: conn} do
612 avatar_image = File.read!("test/fixtures/avatar_data_uri")
616 |> assign(:user, user)
617 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
619 user = refresh_record(user)
633 assert %{"url" => _} = json_response(conn, 200)
636 test "user avatar can be reset", %{conn: conn} do
641 |> assign(:user, user)
642 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
644 user = User.get_cached_by_id(user.id)
646 assert user.avatar == nil
648 assert %{"url" => nil} = json_response(conn, 200)
651 test "can set profile banner", %{conn: conn} do
656 |> assign(:user, user)
657 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
659 user = refresh_record(user)
660 assert user.info.banner["type"] == "Image"
662 assert %{"url" => _} = json_response(conn, 200)
665 test "can reset profile banner", %{conn: conn} do
670 |> assign(:user, user)
671 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
673 user = refresh_record(user)
674 assert user.info.banner == %{}
676 assert %{"url" => nil} = json_response(conn, 200)
679 test "background image can be set", %{conn: conn} do
684 |> assign(:user, user)
685 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
687 user = refresh_record(user)
688 assert user.info.background["type"] == "Image"
689 assert %{"url" => _} = json_response(conn, 200)
692 test "background image can be reset", %{conn: conn} do
697 |> assign(:user, user)
698 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
700 user = refresh_record(user)
701 assert user.info.background == %{}
702 assert %{"url" => nil} = json_response(conn, 200)
705 test "creates an oauth app", %{conn: conn} do
707 app_attrs = build(:oauth_app)
711 |> assign(:user, user)
712 |> post("/api/v1/apps", %{
713 client_name: app_attrs.client_name,
714 redirect_uris: app_attrs.redirect_uris
717 [app] = Repo.all(App)
720 "name" => app.client_name,
721 "website" => app.website,
722 "client_id" => app.client_id,
723 "client_secret" => app.client_secret,
724 "id" => app.id |> to_string(),
725 "redirect_uri" => app.redirect_uris,
726 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
729 assert expected == json_response(conn, 200)
732 test "get a status", %{conn: conn} do
733 activity = insert(:note_activity)
737 |> get("/api/v1/statuses/#{activity.id}")
739 assert %{"id" => id} = json_response(conn, 200)
740 assert id == to_string(activity.id)
743 describe "deleting a status" do
744 test "when you created it", %{conn: conn} do
745 activity = insert(:note_activity)
746 author = User.get_cached_by_ap_id(activity.data["actor"])
750 |> assign(:user, author)
751 |> delete("/api/v1/statuses/#{activity.id}")
753 assert %{} = json_response(conn, 200)
755 refute Activity.get_by_id(activity.id)
758 test "when you didn't create it", %{conn: conn} do
759 activity = insert(:note_activity)
764 |> assign(:user, user)
765 |> delete("/api/v1/statuses/#{activity.id}")
767 assert %{"error" => _} = json_response(conn, 403)
769 assert Activity.get_by_id(activity.id) == activity
772 test "when you're an admin or moderator", %{conn: conn} do
773 activity1 = insert(:note_activity)
774 activity2 = insert(:note_activity)
775 admin = insert(:user, info: %{is_admin: true})
776 moderator = insert(:user, info: %{is_moderator: true})
780 |> assign(:user, admin)
781 |> delete("/api/v1/statuses/#{activity1.id}")
783 assert %{} = json_response(res_conn, 200)
787 |> assign(:user, moderator)
788 |> delete("/api/v1/statuses/#{activity2.id}")
790 assert %{} = json_response(res_conn, 200)
792 refute Activity.get_by_id(activity1.id)
793 refute Activity.get_by_id(activity2.id)
797 describe "filters" do
798 test "creating a filter", %{conn: conn} do
801 filter = %Pleroma.Filter{
808 |> assign(:user, user)
809 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
811 assert response = json_response(conn, 200)
812 assert response["phrase"] == filter.phrase
813 assert response["context"] == filter.context
814 assert response["irreversible"] == false
815 assert response["id"] != nil
816 assert response["id"] != ""
819 test "fetching a list of filters", %{conn: conn} do
822 query_one = %Pleroma.Filter{
829 query_two = %Pleroma.Filter{
836 {:ok, filter_one} = Pleroma.Filter.create(query_one)
837 {:ok, filter_two} = Pleroma.Filter.create(query_two)
841 |> assign(:user, user)
842 |> get("/api/v1/filters")
843 |> json_response(200)
849 filters: [filter_two, filter_one]
853 test "get a filter", %{conn: conn} do
856 query = %Pleroma.Filter{
863 {:ok, filter} = Pleroma.Filter.create(query)
867 |> assign(:user, user)
868 |> get("/api/v1/filters/#{filter.filter_id}")
870 assert _response = json_response(conn, 200)
873 test "update a filter", %{conn: conn} do
876 query = %Pleroma.Filter{
883 {:ok, _filter} = Pleroma.Filter.create(query)
885 new = %Pleroma.Filter{
892 |> assign(:user, user)
893 |> put("/api/v1/filters/#{query.filter_id}", %{
898 assert response = json_response(conn, 200)
899 assert response["phrase"] == new.phrase
900 assert response["context"] == new.context
903 test "delete a filter", %{conn: conn} do
906 query = %Pleroma.Filter{
913 {:ok, filter} = Pleroma.Filter.create(query)
917 |> assign(:user, user)
918 |> delete("/api/v1/filters/#{filter.filter_id}")
920 assert response = json_response(conn, 200)
921 assert response == %{}
926 test "creating a list", %{conn: conn} do
931 |> assign(:user, user)
932 |> post("/api/v1/lists", %{"title" => "cuties"})
934 assert %{"title" => title} = json_response(conn, 200)
935 assert title == "cuties"
938 test "adding users to a list", %{conn: conn} do
940 other_user = insert(:user)
941 {:ok, list} = Pleroma.List.create("name", user)
945 |> assign(:user, user)
946 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
948 assert %{} == json_response(conn, 200)
949 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
950 assert following == [other_user.follower_address]
953 test "removing users from a list", %{conn: conn} do
955 other_user = insert(:user)
956 third_user = insert(:user)
957 {:ok, list} = Pleroma.List.create("name", user)
958 {:ok, list} = Pleroma.List.follow(list, other_user)
959 {:ok, list} = Pleroma.List.follow(list, third_user)
963 |> assign(:user, user)
964 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
966 assert %{} == json_response(conn, 200)
967 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
968 assert following == [third_user.follower_address]
971 test "listing users in a list", %{conn: conn} do
973 other_user = insert(:user)
974 {:ok, list} = Pleroma.List.create("name", user)
975 {:ok, list} = Pleroma.List.follow(list, other_user)
979 |> assign(:user, user)
980 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
982 assert [%{"id" => id}] = json_response(conn, 200)
983 assert id == to_string(other_user.id)
986 test "retrieving a list", %{conn: conn} do
988 {:ok, list} = Pleroma.List.create("name", user)
992 |> assign(:user, user)
993 |> get("/api/v1/lists/#{list.id}")
995 assert %{"id" => id} = json_response(conn, 200)
996 assert id == to_string(list.id)
999 test "renaming a list", %{conn: conn} do
1000 user = insert(:user)
1001 {:ok, list} = Pleroma.List.create("name", user)
1005 |> assign(:user, user)
1006 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
1008 assert %{"title" => name} = json_response(conn, 200)
1009 assert name == "newname"
1012 test "deleting a list", %{conn: conn} do
1013 user = insert(:user)
1014 {:ok, list} = Pleroma.List.create("name", user)
1018 |> assign(:user, user)
1019 |> delete("/api/v1/lists/#{list.id}")
1021 assert %{} = json_response(conn, 200)
1022 assert is_nil(Repo.get(Pleroma.List, list.id))
1025 test "list timeline", %{conn: conn} do
1026 user = insert(:user)
1027 other_user = insert(:user)
1028 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1029 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1030 {:ok, list} = Pleroma.List.create("name", user)
1031 {:ok, list} = Pleroma.List.follow(list, other_user)
1035 |> assign(:user, user)
1036 |> get("/api/v1/timelines/list/#{list.id}")
1038 assert [%{"id" => id}] = json_response(conn, 200)
1040 assert id == to_string(activity_two.id)
1043 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1044 user = insert(:user)
1045 other_user = insert(:user)
1046 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1048 {:ok, _activity_two} =
1049 CommonAPI.post(other_user, %{
1050 "status" => "Marisa is cute.",
1051 "visibility" => "private"
1054 {:ok, list} = Pleroma.List.create("name", user)
1055 {:ok, list} = Pleroma.List.follow(list, other_user)
1059 |> assign(:user, user)
1060 |> get("/api/v1/timelines/list/#{list.id}")
1062 assert [%{"id" => id}] = json_response(conn, 200)
1064 assert id == to_string(activity_one.id)
1068 describe "notifications" do
1069 test "list of notifications", %{conn: conn} do
1070 user = insert(:user)
1071 other_user = insert(:user)
1073 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1075 {:ok, [_notification]} = Notification.create_notifications(activity)
1079 |> assign(:user, user)
1080 |> get("/api/v1/notifications")
1083 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1085 }\">@<span>#{user.nickname}</span></a></span>"
1087 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1088 assert response == expected_response
1091 test "getting a single notification", %{conn: conn} do
1092 user = insert(:user)
1093 other_user = insert(:user)
1095 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1097 {:ok, [notification]} = Notification.create_notifications(activity)
1101 |> assign(:user, user)
1102 |> get("/api/v1/notifications/#{notification.id}")
1105 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1107 }\">@<span>#{user.nickname}</span></a></span>"
1109 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1110 assert response == expected_response
1113 test "dismissing a single notification", %{conn: conn} do
1114 user = insert(:user)
1115 other_user = insert(:user)
1117 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1119 {:ok, [notification]} = Notification.create_notifications(activity)
1123 |> assign(:user, user)
1124 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1126 assert %{} = json_response(conn, 200)
1129 test "clearing all notifications", %{conn: conn} do
1130 user = insert(:user)
1131 other_user = insert(:user)
1133 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1135 {:ok, [_notification]} = Notification.create_notifications(activity)
1139 |> assign(:user, user)
1140 |> post("/api/v1/notifications/clear")
1142 assert %{} = json_response(conn, 200)
1146 |> assign(:user, user)
1147 |> get("/api/v1/notifications")
1149 assert all = json_response(conn, 200)
1153 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1154 user = insert(:user)
1155 other_user = insert(:user)
1157 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1158 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1159 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1160 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1162 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1163 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1164 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1165 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1169 |> assign(:user, user)
1174 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1176 result = json_response(conn_res, 200)
1177 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1182 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1184 result = json_response(conn_res, 200)
1185 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1190 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1192 result = json_response(conn_res, 200)
1193 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1196 test "filters notifications using exclude_types", %{conn: conn} do
1197 user = insert(:user)
1198 other_user = insert(:user)
1200 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1201 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1202 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1203 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1204 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1206 mention_notification_id =
1207 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1209 favorite_notification_id =
1210 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1212 reblog_notification_id =
1213 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1215 follow_notification_id =
1216 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1220 |> assign(:user, user)
1223 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1225 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1228 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1230 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1233 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1235 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1238 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1240 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1243 test "destroy multiple", %{conn: conn} do
1244 user = insert(:user)
1245 other_user = insert(:user)
1247 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1248 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1249 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1250 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1252 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1253 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1254 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1255 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1259 |> assign(:user, user)
1263 |> get("/api/v1/notifications")
1265 result = json_response(conn_res, 200)
1266 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1270 |> assign(:user, other_user)
1274 |> get("/api/v1/notifications")
1276 result = json_response(conn_res, 200)
1277 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1281 |> delete("/api/v1/notifications/destroy_multiple", %{
1282 "ids" => [notification1_id, notification2_id]
1285 assert json_response(conn_destroy, 200) == %{}
1289 |> get("/api/v1/notifications")
1291 result = json_response(conn_res, 200)
1292 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1295 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1296 user = insert(:user)
1297 user2 = insert(:user)
1299 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1300 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1302 conn = assign(conn, :user, user)
1304 conn = get(conn, "/api/v1/notifications")
1306 assert length(json_response(conn, 200)) == 1
1308 {:ok, user} = User.mute(user, user2)
1310 conn = assign(build_conn(), :user, user)
1311 conn = get(conn, "/api/v1/notifications")
1313 assert json_response(conn, 200) == []
1316 test "see notifications after muting user without notifications", %{conn: conn} do
1317 user = insert(:user)
1318 user2 = insert(:user)
1320 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1321 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1323 conn = assign(conn, :user, user)
1325 conn = get(conn, "/api/v1/notifications")
1327 assert length(json_response(conn, 200)) == 1
1329 {:ok, user} = User.mute(user, user2, false)
1331 conn = assign(build_conn(), :user, user)
1332 conn = get(conn, "/api/v1/notifications")
1334 assert length(json_response(conn, 200)) == 1
1337 test "see notifications after muting user with notifications and with_muted parameter", %{
1340 user = insert(:user)
1341 user2 = insert(:user)
1343 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1344 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1346 conn = assign(conn, :user, user)
1348 conn = get(conn, "/api/v1/notifications")
1350 assert length(json_response(conn, 200)) == 1
1352 {:ok, user} = User.mute(user, user2)
1354 conn = assign(build_conn(), :user, user)
1355 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1357 assert length(json_response(conn, 200)) == 1
1361 describe "reblogging" do
1362 test "reblogs and returns the reblogged status", %{conn: conn} do
1363 activity = insert(:note_activity)
1364 user = insert(:user)
1368 |> assign(:user, user)
1369 |> post("/api/v1/statuses/#{activity.id}/reblog")
1372 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1374 } = json_response(conn, 200)
1376 assert to_string(activity.id) == id
1379 test "reblogged status for another user", %{conn: conn} do
1380 activity = insert(:note_activity)
1381 user1 = insert(:user)
1382 user2 = insert(:user)
1383 user3 = insert(:user)
1384 CommonAPI.favorite(activity.id, user2)
1385 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1386 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1387 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1391 |> assign(:user, user3)
1392 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1395 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1396 "reblogged" => false,
1397 "favourited" => false,
1398 "bookmarked" => false
1399 } = json_response(conn_res, 200)
1403 |> assign(:user, user2)
1404 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1407 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1408 "reblogged" => true,
1409 "favourited" => true,
1410 "bookmarked" => true
1411 } = json_response(conn_res, 200)
1413 assert to_string(activity.id) == id
1416 test "returns 400 error when activity is not exist", %{conn: conn} do
1417 user = insert(:user)
1421 |> assign(:user, user)
1422 |> post("/api/v1/statuses/foo/reblog")
1424 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1428 describe "unreblogging" do
1429 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1430 activity = insert(:note_activity)
1431 user = insert(:user)
1433 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1437 |> assign(:user, user)
1438 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1440 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1442 assert to_string(activity.id) == id
1445 test "returns 400 error when activity is not exist", %{conn: conn} do
1446 user = insert(:user)
1450 |> assign(:user, user)
1451 |> post("/api/v1/statuses/foo/unreblog")
1453 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1457 describe "favoriting" do
1458 test "favs a status and returns it", %{conn: conn} do
1459 activity = insert(:note_activity)
1460 user = insert(:user)
1464 |> assign(:user, user)
1465 |> post("/api/v1/statuses/#{activity.id}/favourite")
1467 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1468 json_response(conn, 200)
1470 assert to_string(activity.id) == id
1473 test "returns 400 error for a wrong id", %{conn: conn} do
1474 user = insert(:user)
1478 |> assign(:user, user)
1479 |> post("/api/v1/statuses/1/favourite")
1481 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1485 describe "unfavoriting" do
1486 test "unfavorites a status and returns it", %{conn: conn} do
1487 activity = insert(:note_activity)
1488 user = insert(:user)
1490 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1494 |> assign(:user, user)
1495 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1497 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1498 json_response(conn, 200)
1500 assert to_string(activity.id) == id
1503 test "returns 400 error for a wrong id", %{conn: conn} do
1504 user = insert(:user)
1508 |> assign(:user, user)
1509 |> post("/api/v1/statuses/1/unfavourite")
1511 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1515 describe "user timelines" do
1516 test "gets a users statuses", %{conn: conn} do
1517 user_one = insert(:user)
1518 user_two = insert(:user)
1519 user_three = insert(:user)
1521 {:ok, user_three} = User.follow(user_three, user_one)
1523 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1525 {:ok, direct_activity} =
1526 CommonAPI.post(user_one, %{
1527 "status" => "Hi, @#{user_two.nickname}.",
1528 "visibility" => "direct"
1531 {:ok, private_activity} =
1532 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1536 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1538 assert [%{"id" => id}] = json_response(resp, 200)
1539 assert id == to_string(activity.id)
1543 |> assign(:user, user_two)
1544 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1546 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1547 assert id_one == to_string(direct_activity.id)
1548 assert id_two == to_string(activity.id)
1552 |> assign(:user, user_three)
1553 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1555 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1556 assert id_one == to_string(private_activity.id)
1557 assert id_two == to_string(activity.id)
1560 test "unimplemented pinned statuses feature", %{conn: conn} do
1561 note = insert(:note_activity)
1562 user = User.get_cached_by_ap_id(note.data["actor"])
1566 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1568 assert json_response(conn, 200) == []
1571 test "gets an users media", %{conn: conn} do
1572 note = insert(:note_activity)
1573 user = User.get_cached_by_ap_id(note.data["actor"])
1575 file = %Plug.Upload{
1576 content_type: "image/jpg",
1577 path: Path.absname("test/fixtures/image.jpg"),
1578 filename: "an_image.jpg"
1582 TwitterAPI.upload(file, user, "json")
1586 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1590 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1592 assert [%{"id" => id}] = json_response(conn, 200)
1593 assert id == to_string(image_post.id)
1597 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1599 assert [%{"id" => id}] = json_response(conn, 200)
1600 assert id == to_string(image_post.id)
1603 test "gets a user's statuses without reblogs", %{conn: conn} do
1604 user = insert(:user)
1605 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1606 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1610 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1612 assert [%{"id" => id}] = json_response(conn, 200)
1613 assert id == to_string(post.id)
1617 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1619 assert [%{"id" => id}] = json_response(conn, 200)
1620 assert id == to_string(post.id)
1623 test "filters user's statuses by a hashtag", %{conn: conn} do
1624 user = insert(:user)
1625 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1626 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1630 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1632 assert [%{"id" => id}] = json_response(conn, 200)
1633 assert id == to_string(post.id)
1637 describe "user relationships" do
1638 test "returns the relationships for the current user", %{conn: conn} do
1639 user = insert(:user)
1640 other_user = insert(:user)
1641 {:ok, user} = User.follow(user, other_user)
1645 |> assign(:user, user)
1646 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1648 assert [relationship] = json_response(conn, 200)
1650 assert to_string(other_user.id) == relationship["id"]
1654 describe "media upload" do
1656 upload_config = Pleroma.Config.get([Pleroma.Upload])
1657 proxy_config = Pleroma.Config.get([:media_proxy])
1660 Pleroma.Config.put([Pleroma.Upload], upload_config)
1661 Pleroma.Config.put([:media_proxy], proxy_config)
1664 user = insert(:user)
1668 |> assign(:user, user)
1670 image = %Plug.Upload{
1671 content_type: "image/jpg",
1672 path: Path.absname("test/fixtures/image.jpg"),
1673 filename: "an_image.jpg"
1676 [conn: conn, image: image]
1679 test "returns uploaded image", %{conn: conn, image: image} do
1680 desc = "Description of the image"
1684 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1685 |> json_response(:ok)
1687 assert media["type"] == "image"
1688 assert media["description"] == desc
1691 object = Repo.get(Object, media["id"])
1692 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1695 test "returns proxied url when media proxy is enabled", %{conn: conn, image: image} do
1696 Pleroma.Config.put([Pleroma.Upload, :base_url], "https://media.pleroma.social")
1698 proxy_url = "https://cache.pleroma.social"
1699 Pleroma.Config.put([:media_proxy, :enabled], true)
1700 Pleroma.Config.put([:media_proxy, :base_url], proxy_url)
1704 |> post("/api/v1/media", %{"file" => image})
1705 |> json_response(:ok)
1707 assert String.starts_with?(media["url"], proxy_url)
1710 test "returns media url when proxy is enabled but media url is whitelisted", %{
1714 media_url = "https://media.pleroma.social"
1715 Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
1717 Pleroma.Config.put([:media_proxy, :enabled], true)
1718 Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
1719 Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
1723 |> post("/api/v1/media", %{"file" => image})
1724 |> json_response(:ok)
1726 assert String.starts_with?(media["url"], media_url)
1730 describe "locked accounts" do
1731 test "/api/v1/follow_requests works" do
1732 user = insert(:user, %{info: %User.Info{locked: true}})
1733 other_user = insert(:user)
1735 {:ok, _activity} = ActivityPub.follow(other_user, user)
1737 user = User.get_cached_by_id(user.id)
1738 other_user = User.get_cached_by_id(other_user.id)
1740 assert User.following?(other_user, user) == false
1744 |> assign(:user, user)
1745 |> get("/api/v1/follow_requests")
1747 assert [relationship] = json_response(conn, 200)
1748 assert to_string(other_user.id) == relationship["id"]
1751 test "/api/v1/follow_requests/:id/authorize works" do
1752 user = insert(:user, %{info: %User.Info{locked: true}})
1753 other_user = insert(:user)
1755 {:ok, _activity} = ActivityPub.follow(other_user, user)
1757 user = User.get_cached_by_id(user.id)
1758 other_user = User.get_cached_by_id(other_user.id)
1760 assert User.following?(other_user, user) == false
1764 |> assign(:user, user)
1765 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1767 assert relationship = json_response(conn, 200)
1768 assert to_string(other_user.id) == relationship["id"]
1770 user = User.get_cached_by_id(user.id)
1771 other_user = User.get_cached_by_id(other_user.id)
1773 assert User.following?(other_user, user) == true
1776 test "verify_credentials", %{conn: conn} do
1777 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1781 |> assign(:user, user)
1782 |> get("/api/v1/accounts/verify_credentials")
1784 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1785 assert id == to_string(user.id)
1788 test "/api/v1/follow_requests/:id/reject works" do
1789 user = insert(:user, %{info: %User.Info{locked: true}})
1790 other_user = insert(:user)
1792 {:ok, _activity} = ActivityPub.follow(other_user, user)
1794 user = User.get_cached_by_id(user.id)
1798 |> assign(:user, user)
1799 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1801 assert relationship = json_response(conn, 200)
1802 assert to_string(other_user.id) == relationship["id"]
1804 user = User.get_cached_by_id(user.id)
1805 other_user = User.get_cached_by_id(other_user.id)
1807 assert User.following?(other_user, user) == false
1811 test "account fetching", %{conn: conn} do
1812 user = insert(:user)
1816 |> get("/api/v1/accounts/#{user.id}")
1818 assert %{"id" => id} = json_response(conn, 200)
1819 assert id == to_string(user.id)
1823 |> get("/api/v1/accounts/-1")
1825 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1828 test "account fetching also works nickname", %{conn: conn} do
1829 user = insert(:user)
1833 |> get("/api/v1/accounts/#{user.nickname}")
1835 assert %{"id" => id} = json_response(conn, 200)
1836 assert id == user.id
1839 test "mascot upload", %{conn: conn} do
1840 user = insert(:user)
1842 non_image_file = %Plug.Upload{
1843 content_type: "audio/mpeg",
1844 path: Path.absname("test/fixtures/sound.mp3"),
1845 filename: "sound.mp3"
1850 |> assign(:user, user)
1851 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1853 assert json_response(conn, 415)
1855 file = %Plug.Upload{
1856 content_type: "image/jpg",
1857 path: Path.absname("test/fixtures/image.jpg"),
1858 filename: "an_image.jpg"
1863 |> assign(:user, user)
1864 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1866 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1869 test "mascot retrieving", %{conn: conn} do
1870 user = insert(:user)
1871 # When user hasn't set a mascot, we should just get pleroma tan back
1874 |> assign(:user, user)
1875 |> get("/api/v1/pleroma/mascot")
1877 assert %{"url" => url} = json_response(conn, 200)
1878 assert url =~ "pleroma-fox-tan-smol"
1880 # When a user sets their mascot, we should get that back
1881 file = %Plug.Upload{
1882 content_type: "image/jpg",
1883 path: Path.absname("test/fixtures/image.jpg"),
1884 filename: "an_image.jpg"
1889 |> assign(:user, user)
1890 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1892 assert json_response(conn, 200)
1894 user = User.get_cached_by_id(user.id)
1898 |> assign(:user, user)
1899 |> get("/api/v1/pleroma/mascot")
1901 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1902 assert url =~ "an_image"
1905 test "hashtag timeline", %{conn: conn} do
1906 following = insert(:user)
1909 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1911 {:ok, [_activity]} =
1912 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1916 |> get("/api/v1/timelines/tag/2hu")
1918 assert [%{"id" => id}] = json_response(nconn, 200)
1920 assert id == to_string(activity.id)
1922 # works for different capitalization too
1925 |> get("/api/v1/timelines/tag/2HU")
1927 assert [%{"id" => id}] = json_response(nconn, 200)
1929 assert id == to_string(activity.id)
1933 test "multi-hashtag timeline", %{conn: conn} do
1934 user = insert(:user)
1936 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1937 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1938 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1942 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1944 [status_none, status_test1, status_test] = json_response(any_test, 200)
1946 assert to_string(activity_test.id) == status_test["id"]
1947 assert to_string(activity_test1.id) == status_test1["id"]
1948 assert to_string(activity_none.id) == status_none["id"]
1952 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1954 assert [status_test1] == json_response(restricted_test, 200)
1956 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1958 assert [status_none] == json_response(all_test, 200)
1961 test "getting followers", %{conn: conn} do
1962 user = insert(:user)
1963 other_user = insert(:user)
1964 {:ok, user} = User.follow(user, other_user)
1968 |> get("/api/v1/accounts/#{other_user.id}/followers")
1970 assert [%{"id" => id}] = json_response(conn, 200)
1971 assert id == to_string(user.id)
1974 test "getting followers, hide_followers", %{conn: conn} do
1975 user = insert(:user)
1976 other_user = insert(:user, %{info: %{hide_followers: true}})
1977 {:ok, _user} = User.follow(user, other_user)
1981 |> get("/api/v1/accounts/#{other_user.id}/followers")
1983 assert [] == json_response(conn, 200)
1986 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1987 user = insert(:user)
1988 other_user = insert(:user, %{info: %{hide_followers: true}})
1989 {:ok, _user} = User.follow(user, other_user)
1993 |> assign(:user, other_user)
1994 |> get("/api/v1/accounts/#{other_user.id}/followers")
1996 refute [] == json_response(conn, 200)
1999 test "getting followers, pagination", %{conn: conn} do
2000 user = insert(:user)
2001 follower1 = insert(:user)
2002 follower2 = insert(:user)
2003 follower3 = insert(:user)
2004 {:ok, _} = User.follow(follower1, user)
2005 {:ok, _} = User.follow(follower2, user)
2006 {:ok, _} = User.follow(follower3, user)
2010 |> assign(:user, user)
2014 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
2016 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2017 assert id3 == follower3.id
2018 assert id2 == follower2.id
2022 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
2024 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2025 assert id2 == follower2.id
2026 assert id1 == follower1.id
2030 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
2032 assert [%{"id" => id2}] = json_response(res_conn, 200)
2033 assert id2 == follower2.id
2035 assert [link_header] = get_resp_header(res_conn, "link")
2036 assert link_header =~ ~r/min_id=#{follower2.id}/
2037 assert link_header =~ ~r/max_id=#{follower2.id}/
2040 test "getting following", %{conn: conn} do
2041 user = insert(:user)
2042 other_user = insert(:user)
2043 {:ok, user} = User.follow(user, other_user)
2047 |> get("/api/v1/accounts/#{user.id}/following")
2049 assert [%{"id" => id}] = json_response(conn, 200)
2050 assert id == to_string(other_user.id)
2053 test "getting following, hide_follows", %{conn: conn} do
2054 user = insert(:user, %{info: %{hide_follows: true}})
2055 other_user = insert(:user)
2056 {:ok, user} = User.follow(user, other_user)
2060 |> get("/api/v1/accounts/#{user.id}/following")
2062 assert [] == json_response(conn, 200)
2065 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2066 user = insert(:user, %{info: %{hide_follows: true}})
2067 other_user = insert(:user)
2068 {:ok, user} = User.follow(user, other_user)
2072 |> assign(:user, user)
2073 |> get("/api/v1/accounts/#{user.id}/following")
2075 refute [] == json_response(conn, 200)
2078 test "getting following, pagination", %{conn: conn} do
2079 user = insert(:user)
2080 following1 = insert(:user)
2081 following2 = insert(:user)
2082 following3 = insert(:user)
2083 {:ok, _} = User.follow(user, following1)
2084 {:ok, _} = User.follow(user, following2)
2085 {:ok, _} = User.follow(user, following3)
2089 |> assign(:user, user)
2093 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2095 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2096 assert id3 == following3.id
2097 assert id2 == following2.id
2101 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2103 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2104 assert id2 == following2.id
2105 assert id1 == following1.id
2109 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2111 assert [%{"id" => id2}] = json_response(res_conn, 200)
2112 assert id2 == following2.id
2114 assert [link_header] = get_resp_header(res_conn, "link")
2115 assert link_header =~ ~r/min_id=#{following2.id}/
2116 assert link_header =~ ~r/max_id=#{following2.id}/
2119 test "following / unfollowing a user", %{conn: conn} do
2120 user = insert(:user)
2121 other_user = insert(:user)
2125 |> assign(:user, user)
2126 |> post("/api/v1/accounts/#{other_user.id}/follow")
2128 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2130 user = User.get_cached_by_id(user.id)
2134 |> assign(:user, user)
2135 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2137 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2139 user = User.get_cached_by_id(user.id)
2143 |> assign(:user, user)
2144 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2146 assert %{"id" => id} = json_response(conn, 200)
2147 assert id == to_string(other_user.id)
2150 test "following without reblogs" do
2151 follower = insert(:user)
2152 followed = insert(:user)
2153 other_user = insert(:user)
2157 |> assign(:user, follower)
2158 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2160 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2162 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2163 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2167 |> assign(:user, User.get_cached_by_id(follower.id))
2168 |> get("/api/v1/timelines/home")
2170 assert [] == json_response(conn, 200)
2174 |> assign(:user, follower)
2175 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2177 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2181 |> assign(:user, User.get_cached_by_id(follower.id))
2182 |> get("/api/v1/timelines/home")
2184 expected_activity_id = reblog.id
2185 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2188 test "following / unfollowing errors" do
2189 user = insert(:user)
2193 |> assign(:user, user)
2196 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2197 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2200 user = User.get_cached_by_id(user.id)
2201 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2202 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2204 # self follow via uri
2205 user = User.get_cached_by_id(user.id)
2206 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2207 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2209 # follow non existing user
2210 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2211 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2213 # follow non existing user via uri
2214 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2215 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2217 # unfollow non existing user
2218 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2219 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2222 describe "mute/unmute" do
2223 test "with notifications", %{conn: conn} do
2224 user = insert(:user)
2225 other_user = insert(:user)
2229 |> assign(:user, user)
2230 |> post("/api/v1/accounts/#{other_user.id}/mute")
2232 response = json_response(conn, 200)
2234 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2235 user = User.get_cached_by_id(user.id)
2239 |> assign(:user, user)
2240 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2242 response = json_response(conn, 200)
2243 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2246 test "without notifications", %{conn: conn} do
2247 user = insert(:user)
2248 other_user = insert(:user)
2252 |> assign(:user, user)
2253 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2255 response = json_response(conn, 200)
2257 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2258 user = User.get_cached_by_id(user.id)
2262 |> assign(:user, user)
2263 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2265 response = json_response(conn, 200)
2266 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2270 test "subscribing / unsubscribing to a user", %{conn: conn} do
2271 user = insert(:user)
2272 subscription_target = insert(:user)
2276 |> assign(:user, user)
2277 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2279 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2283 |> assign(:user, user)
2284 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2286 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2289 test "getting a list of mutes", %{conn: conn} do
2290 user = insert(:user)
2291 other_user = insert(:user)
2293 {:ok, user} = User.mute(user, other_user)
2297 |> assign(:user, user)
2298 |> get("/api/v1/mutes")
2300 other_user_id = to_string(other_user.id)
2301 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2304 test "blocking / unblocking a user", %{conn: conn} do
2305 user = insert(:user)
2306 other_user = insert(:user)
2310 |> assign(:user, user)
2311 |> post("/api/v1/accounts/#{other_user.id}/block")
2313 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2315 user = User.get_cached_by_id(user.id)
2319 |> assign(:user, user)
2320 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2322 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2325 test "getting a list of blocks", %{conn: conn} do
2326 user = insert(:user)
2327 other_user = insert(:user)
2329 {:ok, user} = User.block(user, other_user)
2333 |> assign(:user, user)
2334 |> get("/api/v1/blocks")
2336 other_user_id = to_string(other_user.id)
2337 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2340 test "blocking / unblocking a domain", %{conn: conn} do
2341 user = insert(:user)
2342 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2346 |> assign(:user, user)
2347 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2349 assert %{} = json_response(conn, 200)
2350 user = User.get_cached_by_ap_id(user.ap_id)
2351 assert User.blocks?(user, other_user)
2355 |> assign(:user, user)
2356 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2358 assert %{} = json_response(conn, 200)
2359 user = User.get_cached_by_ap_id(user.ap_id)
2360 refute User.blocks?(user, other_user)
2363 test "getting a list of domain blocks", %{conn: conn} do
2364 user = insert(:user)
2366 {:ok, user} = User.block_domain(user, "bad.site")
2367 {:ok, user} = User.block_domain(user, "even.worse.site")
2371 |> assign(:user, user)
2372 |> get("/api/v1/domain_blocks")
2374 domain_blocks = json_response(conn, 200)
2376 assert "bad.site" in domain_blocks
2377 assert "even.worse.site" in domain_blocks
2380 test "unimplemented follow_requests, blocks, domain blocks" do
2381 user = insert(:user)
2383 ["blocks", "domain_blocks", "follow_requests"]
2384 |> Enum.each(fn endpoint ->
2387 |> assign(:user, user)
2388 |> get("/api/v1/#{endpoint}")
2390 assert [] = json_response(conn, 200)
2394 test "returns the favorites of a user", %{conn: conn} do
2395 user = insert(:user)
2396 other_user = insert(:user)
2398 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2399 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2401 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2405 |> assign(:user, user)
2406 |> get("/api/v1/favourites")
2408 assert [status] = json_response(first_conn, 200)
2409 assert status["id"] == to_string(activity.id)
2411 assert [{"link", _link_header}] =
2412 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2414 # Honours query params
2415 {:ok, second_activity} =
2416 CommonAPI.post(other_user, %{
2418 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2421 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2423 last_like = status["id"]
2427 |> assign(:user, user)
2428 |> get("/api/v1/favourites?since_id=#{last_like}")
2430 assert [second_status] = json_response(second_conn, 200)
2431 assert second_status["id"] == to_string(second_activity.id)
2435 |> assign(:user, user)
2436 |> get("/api/v1/favourites?limit=0")
2438 assert [] = json_response(third_conn, 200)
2441 describe "getting favorites timeline of specified user" do
2443 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2444 [current_user: current_user, user: user]
2447 test "returns list of statuses favorited by specified user", %{
2449 current_user: current_user,
2452 [activity | _] = insert_pair(:note_activity)
2453 CommonAPI.favorite(activity.id, user)
2457 |> assign(:user, current_user)
2458 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2459 |> json_response(:ok)
2463 assert length(response) == 1
2464 assert like["id"] == activity.id
2467 test "returns favorites for specified user_id when user is not logged in", %{
2471 activity = insert(:note_activity)
2472 CommonAPI.favorite(activity.id, user)
2476 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2477 |> json_response(:ok)
2479 assert length(response) == 1
2482 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2484 current_user: current_user,
2488 CommonAPI.post(current_user, %{
2489 "status" => "Hi @#{user.nickname}!",
2490 "visibility" => "direct"
2493 CommonAPI.favorite(direct.id, user)
2497 |> assign(:user, current_user)
2498 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2499 |> json_response(:ok)
2501 assert length(response) == 1
2503 anonymous_response =
2505 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2506 |> json_response(:ok)
2508 assert Enum.empty?(anonymous_response)
2511 test "does not return others' favorited DM when user is not one of recipients", %{
2513 current_user: current_user,
2516 user_two = insert(:user)
2519 CommonAPI.post(user_two, %{
2520 "status" => "Hi @#{user.nickname}!",
2521 "visibility" => "direct"
2524 CommonAPI.favorite(direct.id, user)
2528 |> assign(:user, current_user)
2529 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2530 |> json_response(:ok)
2532 assert Enum.empty?(response)
2535 test "paginates favorites using since_id and max_id", %{
2537 current_user: current_user,
2540 activities = insert_list(10, :note_activity)
2542 Enum.each(activities, fn activity ->
2543 CommonAPI.favorite(activity.id, user)
2546 third_activity = Enum.at(activities, 2)
2547 seventh_activity = Enum.at(activities, 6)
2551 |> assign(:user, current_user)
2552 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2553 since_id: third_activity.id,
2554 max_id: seventh_activity.id
2556 |> json_response(:ok)
2558 assert length(response) == 3
2559 refute third_activity in response
2560 refute seventh_activity in response
2563 test "limits favorites using limit parameter", %{
2565 current_user: current_user,
2569 |> insert_list(:note_activity)
2570 |> Enum.each(fn activity ->
2571 CommonAPI.favorite(activity.id, user)
2576 |> assign(:user, current_user)
2577 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2578 |> json_response(:ok)
2580 assert length(response) == 3
2583 test "returns empty response when user does not have any favorited statuses", %{
2585 current_user: current_user,
2590 |> assign(:user, current_user)
2591 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2592 |> json_response(:ok)
2594 assert Enum.empty?(response)
2597 test "returns 404 error when specified user is not exist", %{conn: conn} do
2598 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2600 assert json_response(conn, 404) == %{"error" => "Record not found"}
2603 test "returns 403 error when user has hidden own favorites", %{
2605 current_user: current_user
2607 user = insert(:user, %{info: %{hide_favorites: true}})
2608 activity = insert(:note_activity)
2609 CommonAPI.favorite(activity.id, user)
2613 |> assign(:user, current_user)
2614 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2616 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2619 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2620 user = insert(:user)
2621 activity = insert(:note_activity)
2622 CommonAPI.favorite(activity.id, user)
2626 |> assign(:user, current_user)
2627 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2629 assert user.info.hide_favorites
2630 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2634 test "get instance information", %{conn: conn} do
2635 conn = get(conn, "/api/v1/instance")
2636 assert result = json_response(conn, 200)
2638 email = Pleroma.Config.get([:instance, :email])
2639 # Note: not checking for "max_toot_chars" since it's optional
2645 "email" => from_config_email,
2647 "streaming_api" => _
2652 "registrations" => _,
2656 assert email == from_config_email
2659 test "get instance stats", %{conn: conn} do
2660 user = insert(:user, %{local: true})
2662 user2 = insert(:user, %{local: true})
2663 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2665 insert(:user, %{local: false, nickname: "u@peer1.com"})
2666 insert(:user, %{local: false, nickname: "u@peer2.com"})
2668 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2670 # Stats should count users with missing or nil `info.deactivated` value
2671 user = User.get_cached_by_id(user.id)
2672 info_change = Changeset.change(user.info, %{deactivated: nil})
2676 |> Changeset.change()
2677 |> Changeset.put_embed(:info, info_change)
2678 |> User.update_and_set_cache()
2680 Pleroma.Stats.update_stats()
2682 conn = get(conn, "/api/v1/instance")
2684 assert result = json_response(conn, 200)
2686 stats = result["stats"]
2689 assert stats["user_count"] == 1
2690 assert stats["status_count"] == 1
2691 assert stats["domain_count"] == 2
2694 test "get peers", %{conn: conn} do
2695 insert(:user, %{local: false, nickname: "u@peer1.com"})
2696 insert(:user, %{local: false, nickname: "u@peer2.com"})
2698 Pleroma.Stats.update_stats()
2700 conn = get(conn, "/api/v1/instance/peers")
2702 assert result = json_response(conn, 200)
2704 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2707 test "put settings", %{conn: conn} do
2708 user = insert(:user)
2712 |> assign(:user, user)
2713 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2715 assert _result = json_response(conn, 200)
2717 user = User.get_cached_by_ap_id(user.ap_id)
2718 assert user.info.settings == %{"programming" => "socks"}
2721 describe "pinned statuses" do
2723 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2725 user = insert(:user)
2726 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2728 [user: user, activity: activity]
2731 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2732 {:ok, _} = CommonAPI.pin(activity.id, user)
2736 |> assign(:user, user)
2737 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2738 |> json_response(200)
2740 id_str = to_string(activity.id)
2742 assert [%{"id" => ^id_str, "pinned" => true}] = result
2745 test "pin status", %{conn: conn, user: user, activity: activity} do
2746 id_str = to_string(activity.id)
2748 assert %{"id" => ^id_str, "pinned" => true} =
2750 |> assign(:user, user)
2751 |> post("/api/v1/statuses/#{activity.id}/pin")
2752 |> json_response(200)
2754 assert [%{"id" => ^id_str, "pinned" => true}] =
2756 |> assign(:user, user)
2757 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2758 |> json_response(200)
2761 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2762 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2766 |> assign(:user, user)
2767 |> post("/api/v1/statuses/#{dm.id}/pin")
2769 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2772 test "unpin status", %{conn: conn, user: user, activity: activity} do
2773 {:ok, _} = CommonAPI.pin(activity.id, user)
2775 id_str = to_string(activity.id)
2776 user = refresh_record(user)
2778 assert %{"id" => ^id_str, "pinned" => false} =
2780 |> assign(:user, user)
2781 |> post("/api/v1/statuses/#{activity.id}/unpin")
2782 |> json_response(200)
2786 |> assign(:user, user)
2787 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2788 |> json_response(200)
2791 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2794 |> assign(:user, user)
2795 |> post("/api/v1/statuses/1/unpin")
2797 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2800 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2801 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2803 id_str_one = to_string(activity_one.id)
2805 assert %{"id" => ^id_str_one, "pinned" => true} =
2807 |> assign(:user, user)
2808 |> post("/api/v1/statuses/#{id_str_one}/pin")
2809 |> json_response(200)
2811 user = refresh_record(user)
2813 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2815 |> assign(:user, user)
2816 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2817 |> json_response(400)
2823 Pleroma.Config.put([:rich_media, :enabled], true)
2826 Pleroma.Config.put([:rich_media, :enabled], false)
2829 user = insert(:user)
2833 test "returns rich-media card", %{conn: conn, user: user} do
2834 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2837 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2838 "provider_name" => "example.com",
2839 "provider_url" => "https://example.com",
2840 "title" => "The Rock",
2842 "url" => "https://example.com/ogp",
2844 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2847 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2848 "title" => "The Rock",
2849 "type" => "video.movie",
2850 "url" => "https://example.com/ogp",
2852 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2859 |> get("/api/v1/statuses/#{activity.id}/card")
2860 |> json_response(200)
2862 assert response == card_data
2864 # works with private posts
2866 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2870 |> assign(:user, user)
2871 |> get("/api/v1/statuses/#{activity.id}/card")
2872 |> json_response(200)
2874 assert response_two == card_data
2877 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2879 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2883 |> get("/api/v1/statuses/#{activity.id}/card")
2884 |> json_response(:ok)
2886 assert response == %{
2888 "title" => "Pleroma",
2889 "description" => "",
2891 "provider_name" => "example.com",
2892 "provider_url" => "https://example.com",
2893 "url" => "https://example.com/ogp-missing-data",
2896 "title" => "Pleroma",
2897 "type" => "website",
2898 "url" => "https://example.com/ogp-missing-data"
2906 user = insert(:user)
2907 for_user = insert(:user)
2910 CommonAPI.post(user, %{
2911 "status" => "heweoo?"
2915 CommonAPI.post(user, %{
2916 "status" => "heweoo!"
2921 |> assign(:user, for_user)
2922 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2924 assert json_response(response1, 200)["bookmarked"] == true
2928 |> assign(:user, for_user)
2929 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2931 assert json_response(response2, 200)["bookmarked"] == true
2935 |> assign(:user, for_user)
2936 |> get("/api/v1/bookmarks")
2938 assert [json_response(response2, 200), json_response(response1, 200)] ==
2939 json_response(bookmarks, 200)
2943 |> assign(:user, for_user)
2944 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2946 assert json_response(response1, 200)["bookmarked"] == false
2950 |> assign(:user, for_user)
2951 |> get("/api/v1/bookmarks")
2953 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2956 describe "conversation muting" do
2958 user = insert(:user)
2959 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2961 [user: user, activity: activity]
2964 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2965 id_str = to_string(activity.id)
2967 assert %{"id" => ^id_str, "muted" => true} =
2969 |> assign(:user, user)
2970 |> post("/api/v1/statuses/#{activity.id}/mute")
2971 |> json_response(200)
2974 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2975 {:ok, _} = CommonAPI.add_mute(user, activity)
2979 |> assign(:user, user)
2980 |> post("/api/v1/statuses/#{activity.id}/mute")
2982 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2985 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2986 {:ok, _} = CommonAPI.add_mute(user, activity)
2988 id_str = to_string(activity.id)
2989 user = refresh_record(user)
2991 assert %{"id" => ^id_str, "muted" => false} =
2993 |> assign(:user, user)
2994 |> post("/api/v1/statuses/#{activity.id}/unmute")
2995 |> json_response(200)
2999 describe "reports" do
3001 reporter = insert(:user)
3002 target_user = insert(:user)
3004 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
3006 [reporter: reporter, target_user: target_user, activity: activity]
3009 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
3010 assert %{"action_taken" => false, "id" => _} =
3012 |> assign(:user, reporter)
3013 |> post("/api/v1/reports", %{"account_id" => target_user.id})
3014 |> json_response(200)
3017 test "submit a report with statuses and comment", %{
3020 target_user: target_user,
3023 assert %{"action_taken" => false, "id" => _} =
3025 |> assign(:user, reporter)
3026 |> post("/api/v1/reports", %{
3027 "account_id" => target_user.id,
3028 "status_ids" => [activity.id],
3029 "comment" => "bad status!",
3030 "forward" => "false"
3032 |> json_response(200)
3035 test "account_id is required", %{
3040 assert %{"error" => "Valid `account_id` required"} =
3042 |> assign(:user, reporter)
3043 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3044 |> json_response(400)
3047 test "comment must be up to the size specified in the config", %{
3050 target_user: target_user
3052 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
3053 comment = String.pad_trailing("a", max_size + 1, "a")
3055 error = %{"error" => "Comment must be up to #{max_size} characters"}
3059 |> assign(:user, reporter)
3060 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3061 |> json_response(400)
3064 test "returns error when account is not exist", %{
3071 |> assign(:user, reporter)
3072 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3074 assert json_response(conn, 400) == %{"error" => "Account not found"}
3078 describe "link headers" do
3079 test "preserves parameters in link headers", %{conn: conn} do
3080 user = insert(:user)
3081 other_user = insert(:user)
3084 CommonAPI.post(other_user, %{
3085 "status" => "hi @#{user.nickname}",
3086 "visibility" => "public"
3090 CommonAPI.post(other_user, %{
3091 "status" => "hi @#{user.nickname}",
3092 "visibility" => "public"
3095 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3096 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3100 |> assign(:user, user)
3101 |> get("/api/v1/notifications", %{media_only: true})
3103 assert [link_header] = get_resp_header(conn, "link")
3104 assert link_header =~ ~r/media_only=true/
3105 assert link_header =~ ~r/min_id=#{notification2.id}/
3106 assert link_header =~ ~r/max_id=#{notification1.id}/
3110 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3111 # Need to set an old-style integer ID to reproduce the problem
3112 # (these are no longer assigned to new accounts but were preserved
3113 # for existing accounts during the migration to flakeIDs)
3114 user_one = insert(:user, %{id: 1212})
3115 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3119 |> get("/api/v1/accounts/#{user_one.id}")
3123 |> get("/api/v1/accounts/#{user_two.nickname}")
3127 |> get("/api/v1/accounts/#{user_two.id}")
3129 acc_one = json_response(resp_one, 200)
3130 acc_two = json_response(resp_two, 200)
3131 acc_three = json_response(resp_three, 200)
3132 refute acc_one == acc_two
3133 assert acc_two == acc_three
3136 describe "custom emoji" do
3137 test "with tags", %{conn: conn} do
3140 |> get("/api/v1/custom_emojis")
3141 |> json_response(200)
3143 assert Map.has_key?(emoji, "shortcode")
3144 assert Map.has_key?(emoji, "static_url")
3145 assert Map.has_key?(emoji, "tags")
3146 assert is_list(emoji["tags"])
3147 assert Map.has_key?(emoji, "category")
3148 assert Map.has_key?(emoji, "url")
3149 assert Map.has_key?(emoji, "visible_in_picker")
3153 describe "index/2 redirections" do
3154 setup %{conn: conn} do
3158 signing_salt: "cooldude"
3163 |> Plug.Session.call(Plug.Session.init(session_opts))
3166 test_path = "/web/statuses/test"
3167 %{conn: conn, path: test_path}
3170 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3171 conn = get(conn, path)
3173 assert conn.status == 302
3174 assert redirected_to(conn) == "/web/login"
3177 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3178 token = insert(:oauth_token)
3182 |> assign(:user, token.user)
3183 |> put_session(:oauth_token, token.token)
3186 assert conn.status == 200
3189 test "saves referer path to session", %{conn: conn, path: path} do
3190 conn = get(conn, path)
3191 return_to = Plug.Conn.get_session(conn, :return_to)
3193 assert return_to == path
3196 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3197 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3198 auth = insert(:oauth_authorization, app: app)
3202 |> put_session(:return_to, path)
3203 |> get("/web/login", %{code: auth.token})
3205 assert conn.status == 302
3206 assert redirected_to(conn) == path
3209 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3210 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3211 auth = insert(:oauth_authorization, app: app)
3213 conn = get(conn, "/web/login", %{code: auth.token})
3215 assert conn.status == 302
3216 assert redirected_to(conn) == "/web/getting-started"
3220 describe "scheduled activities" do
3221 test "creates a scheduled activity", %{conn: conn} do
3222 user = insert(:user)
3223 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3227 |> assign(:user, user)
3228 |> post("/api/v1/statuses", %{
3229 "status" => "scheduled",
3230 "scheduled_at" => scheduled_at
3233 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3234 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3235 assert [] == Repo.all(Activity)
3238 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3239 user = insert(:user)
3240 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3242 file = %Plug.Upload{
3243 content_type: "image/jpg",
3244 path: Path.absname("test/fixtures/image.jpg"),
3245 filename: "an_image.jpg"
3248 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3252 |> assign(:user, user)
3253 |> post("/api/v1/statuses", %{
3254 "media_ids" => [to_string(upload.id)],
3255 "status" => "scheduled",
3256 "scheduled_at" => scheduled_at
3259 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3260 assert %{"type" => "image"} = media_attachment
3263 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3265 user = insert(:user)
3268 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3272 |> assign(:user, user)
3273 |> post("/api/v1/statuses", %{
3274 "status" => "not scheduled",
3275 "scheduled_at" => scheduled_at
3278 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3279 assert [] == Repo.all(ScheduledActivity)
3282 test "returns error when daily user limit is exceeded", %{conn: conn} do
3283 user = insert(:user)
3286 NaiveDateTime.utc_now()
3287 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3288 |> NaiveDateTime.to_iso8601()
3290 attrs = %{params: %{}, scheduled_at: today}
3291 {:ok, _} = ScheduledActivity.create(user, attrs)
3292 {:ok, _} = ScheduledActivity.create(user, attrs)
3296 |> assign(:user, user)
3297 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3299 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3302 test "returns error when total user limit is exceeded", %{conn: conn} do
3303 user = insert(:user)
3306 NaiveDateTime.utc_now()
3307 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3308 |> NaiveDateTime.to_iso8601()
3311 NaiveDateTime.utc_now()
3312 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3313 |> NaiveDateTime.to_iso8601()
3315 attrs = %{params: %{}, scheduled_at: today}
3316 {:ok, _} = ScheduledActivity.create(user, attrs)
3317 {:ok, _} = ScheduledActivity.create(user, attrs)
3318 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3322 |> assign(:user, user)
3323 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3325 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3328 test "shows scheduled activities", %{conn: conn} do
3329 user = insert(:user)
3330 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3331 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3332 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3333 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3337 |> assign(:user, user)
3342 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3344 result = json_response(conn_res, 200)
3345 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3350 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3352 result = json_response(conn_res, 200)
3353 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3358 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3360 result = json_response(conn_res, 200)
3361 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3364 test "shows a scheduled activity", %{conn: conn} do
3365 user = insert(:user)
3366 scheduled_activity = insert(:scheduled_activity, user: user)
3370 |> assign(:user, user)
3371 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3373 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3374 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3378 |> assign(:user, user)
3379 |> get("/api/v1/scheduled_statuses/404")
3381 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3384 test "updates a scheduled activity", %{conn: conn} do
3385 user = insert(:user)
3386 scheduled_activity = insert(:scheduled_activity, user: user)
3389 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3393 |> assign(:user, user)
3394 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3395 scheduled_at: new_scheduled_at
3398 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3399 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3403 |> assign(:user, user)
3404 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3406 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3409 test "deletes a scheduled activity", %{conn: conn} do
3410 user = insert(:user)
3411 scheduled_activity = insert(:scheduled_activity, user: user)
3415 |> assign(:user, user)
3416 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3418 assert %{} = json_response(res_conn, 200)
3419 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3423 |> assign(:user, user)
3424 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3426 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3430 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3431 user1 = insert(:user)
3432 user2 = insert(:user)
3433 user3 = insert(:user)
3435 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3437 # Reply to status from another user
3440 |> assign(:user, user2)
3441 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3443 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3445 activity = Activity.get_by_id_with_object(id)
3447 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3448 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3450 # Reblog from the third user
3453 |> assign(:user, user3)
3454 |> post("/api/v1/statuses/#{activity.id}/reblog")
3456 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3457 json_response(conn2, 200)
3459 assert to_string(activity.id) == id
3461 # Getting third user status
3464 |> assign(:user, user3)
3465 |> get("api/v1/timelines/home")
3467 [reblogged_activity] = json_response(conn3, 200)
3469 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3471 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3472 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3475 describe "create account by app" do
3476 test "Account registration via Application", %{conn: conn} do
3479 |> post("/api/v1/apps", %{
3480 client_name: "client_name",
3481 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3482 scopes: "read, write, follow"
3486 "client_id" => client_id,
3487 "client_secret" => client_secret,
3489 "name" => "client_name",
3490 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3493 } = json_response(conn, 200)
3497 |> post("/oauth/token", %{
3498 grant_type: "client_credentials",
3499 client_id: client_id,
3500 client_secret: client_secret
3503 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3504 json_response(conn, 200)
3507 token_from_db = Repo.get_by(Token, token: token)
3508 assert token_from_db
3510 assert scope == "read write follow"
3514 |> put_req_header("authorization", "Bearer " <> token)
3515 |> post("/api/v1/accounts", %{
3517 email: "lain@example.org",
3518 password: "PlzDontHackLain",
3523 "access_token" => token,
3524 "created_at" => _created_at,
3526 "token_type" => "Bearer"
3527 } = json_response(conn, 200)
3529 token_from_db = Repo.get_by(Token, token: token)
3530 assert token_from_db
3531 token_from_db = Repo.preload(token_from_db, :user)
3532 assert token_from_db.user
3534 assert token_from_db.user.info.confirmation_pending
3537 test "rate limit", %{conn: conn} do
3538 app_token = insert(:oauth_token, user: nil)
3541 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3542 |> Map.put(:remote_ip, {15, 15, 15, 15})
3547 |> post("/api/v1/accounts", %{
3548 username: "#{i}lain",
3549 email: "#{i}lain@example.org",
3550 password: "PlzDontHackLain",
3555 "access_token" => token,
3556 "created_at" => _created_at,
3558 "token_type" => "Bearer"
3559 } = json_response(conn, 200)
3561 token_from_db = Repo.get_by(Token, token: token)
3562 assert token_from_db
3563 token_from_db = Repo.preload(token_from_db, :user)
3564 assert token_from_db.user
3566 assert token_from_db.user.info.confirmation_pending
3571 |> post("/api/v1/accounts", %{
3573 email: "6lain@example.org",
3574 password: "PlzDontHackLain",
3578 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3582 describe "GET /api/v1/polls/:id" do
3583 test "returns poll entity for object id", %{conn: conn} do
3584 user = insert(:user)
3587 CommonAPI.post(user, %{
3588 "status" => "Pleroma does",
3589 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3592 object = Object.normalize(activity)
3596 |> assign(:user, user)
3597 |> get("/api/v1/polls/#{object.id}")
3599 response = json_response(conn, 200)
3600 id = to_string(object.id)
3601 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3604 test "does not expose polls for private statuses", %{conn: conn} do
3605 user = insert(:user)
3606 other_user = insert(:user)
3609 CommonAPI.post(user, %{
3610 "status" => "Pleroma does",
3611 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3612 "visibility" => "private"
3615 object = Object.normalize(activity)
3619 |> assign(:user, other_user)
3620 |> get("/api/v1/polls/#{object.id}")
3622 assert json_response(conn, 404)
3626 describe "POST /api/v1/polls/:id/votes" do
3627 test "votes are added to the poll", %{conn: conn} do
3628 user = insert(:user)
3629 other_user = insert(:user)
3632 CommonAPI.post(user, %{
3633 "status" => "A very delicious sandwich",
3635 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3641 object = Object.normalize(activity)
3645 |> assign(:user, other_user)
3646 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3648 assert json_response(conn, 200)
3649 object = Object.get_by_id(object.id)
3651 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3656 test "author can't vote", %{conn: conn} do
3657 user = insert(:user)
3660 CommonAPI.post(user, %{
3661 "status" => "Am I cute?",
3662 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3665 object = Object.normalize(activity)
3668 |> assign(:user, user)
3669 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3670 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3672 object = Object.get_by_id(object.id)
3674 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3677 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3678 user = insert(:user)
3679 other_user = insert(:user)
3682 CommonAPI.post(user, %{
3683 "status" => "The glass is",
3684 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3687 object = Object.normalize(activity)
3690 |> assign(:user, other_user)
3691 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3692 |> json_response(422) == %{"error" => "Too many choices"}
3694 object = Object.get_by_id(object.id)
3696 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3701 test "does not allow choice index to be greater than options count", %{conn: conn} do
3702 user = insert(:user)
3703 other_user = insert(:user)
3706 CommonAPI.post(user, %{
3707 "status" => "Am I cute?",
3708 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3711 object = Object.normalize(activity)
3715 |> assign(:user, other_user)
3716 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3718 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3721 test "returns 404 error when object is not exist", %{conn: conn} do
3722 user = insert(:user)
3726 |> assign(:user, user)
3727 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3729 assert json_response(conn, 404) == %{"error" => "Record not found"}
3732 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3733 user = insert(:user)
3734 other_user = insert(:user)
3737 CommonAPI.post(user, %{
3738 "status" => "Am I cute?",
3739 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3740 "visibility" => "private"
3743 object = Object.normalize(activity)
3747 |> assign(:user, other_user)
3748 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3750 assert json_response(conn, 404) == %{"error" => "Record not found"}
3754 describe "GET /api/v1/statuses/:id/favourited_by" do
3756 user = insert(:user)
3757 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3761 |> assign(:user, user)
3763 [conn: conn, activity: activity]
3766 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3767 other_user = insert(:user)
3768 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3772 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3773 |> json_response(:ok)
3775 [%{"id" => id}] = response
3777 assert id == other_user.id
3780 test "returns empty array when status has not been favorited yet", %{
3786 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3787 |> json_response(:ok)
3789 assert Enum.empty?(response)
3792 test "does not return users who have favorited the status but are blocked", %{
3793 conn: %{assigns: %{user: user}} = conn,
3796 other_user = insert(:user)
3797 {:ok, user} = User.block(user, other_user)
3799 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3803 |> assign(:user, user)
3804 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3805 |> json_response(:ok)
3807 assert Enum.empty?(response)
3810 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3811 other_user = insert(:user)
3812 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3816 |> assign(:user, nil)
3817 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3818 |> json_response(:ok)
3820 [%{"id" => id}] = response
3821 assert id == other_user.id
3825 describe "GET /api/v1/statuses/:id/reblogged_by" do
3827 user = insert(:user)
3828 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3832 |> assign(:user, user)
3834 [conn: conn, activity: activity]
3837 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3838 other_user = insert(:user)
3839 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3843 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3844 |> json_response(:ok)
3846 [%{"id" => id}] = response
3848 assert id == other_user.id
3851 test "returns empty array when status has not been reblogged yet", %{
3857 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3858 |> json_response(:ok)
3860 assert Enum.empty?(response)
3863 test "does not return users who have reblogged the status but are blocked", %{
3864 conn: %{assigns: %{user: user}} = conn,
3867 other_user = insert(:user)
3868 {:ok, user} = User.block(user, other_user)
3870 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3874 |> assign(:user, user)
3875 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3876 |> json_response(:ok)
3878 assert Enum.empty?(response)
3881 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3882 other_user = insert(:user)
3883 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3887 |> assign(:user, nil)
3888 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3889 |> json_response(:ok)
3891 [%{"id" => id}] = response
3892 assert id == other_user.id
3896 describe "POST /auth/password, with valid parameters" do
3897 setup %{conn: conn} do
3898 user = insert(:user)
3899 conn = post(conn, "/auth/password?email=#{user.email}")
3900 %{conn: conn, user: user}
3903 test "it returns 204", %{conn: conn} do
3904 assert json_response(conn, :no_content)
3907 test "it creates a PasswordResetToken record for user", %{user: user} do
3908 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3912 test "it sends an email to user", %{user: user} do
3913 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3915 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3916 notify_email = Pleroma.Config.get([:instance, :notify_email])
3917 instance_name = Pleroma.Config.get([:instance, :name])
3920 from: {instance_name, notify_email},
3921 to: {user.name, user.email},
3922 html_body: email.html_body
3927 describe "POST /auth/password, with invalid parameters" do
3929 user = insert(:user)
3933 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3934 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3935 assert conn.status == 404
3936 assert conn.resp_body == ""
3939 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3940 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3941 conn = post(conn, "/auth/password?email=#{user.email}")
3942 assert conn.status == 400
3943 assert conn.resp_body == ""