1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.ActivityExpiration
12 alias Pleroma.Notification
15 alias Pleroma.ScheduledActivity
16 alias Pleroma.SubscriptionNotification
17 alias Pleroma.Tests.ObanHelpers
19 alias Pleroma.Web.ActivityPub.ActivityPub
20 alias Pleroma.Web.CommonAPI
21 alias Pleroma.Web.MastodonAPI.FilterView
22 alias Pleroma.Web.OAuth.App
23 alias Pleroma.Web.OAuth.Token
24 alias Pleroma.Web.OStatus
25 alias Pleroma.Web.Push
26 import Pleroma.Factory
27 import ExUnit.CaptureLog
29 import Swoosh.TestAssertions
31 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
34 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
38 clear_config([:instance, :public])
39 clear_config([:rich_media, :enabled])
41 test "the home timeline", %{conn: conn} do
43 following = insert(:user)
45 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
49 |> assign(:user, user)
50 |> get("/api/v1/timelines/home")
52 assert Enum.empty?(json_response(conn, 200))
54 {:ok, user} = User.follow(user, following)
58 |> assign(:user, user)
59 |> get("/api/v1/timelines/home")
61 assert [%{"content" => "test"}] = json_response(conn, 200)
64 test "the public timeline", %{conn: conn} do
65 following = insert(:user)
68 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
71 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
75 |> get("/api/v1/timelines/public", %{"local" => "False"})
77 assert length(json_response(conn, 200)) == 2
81 |> get("/api/v1/timelines/public", %{"local" => "True"})
83 assert [%{"content" => "test"}] = json_response(conn, 200)
87 |> get("/api/v1/timelines/public", %{"local" => "1"})
89 assert [%{"content" => "test"}] = json_response(conn, 200)
93 test "the public timeline when public is set to false", %{conn: conn} do
94 Config.put([:instance, :public], false)
97 |> get("/api/v1/timelines/public", %{"local" => "False"})
98 |> json_response(403) == %{"error" => "This resource requires authentication."}
101 describe "posting statuses" do
107 |> assign(:user, user)
112 test "posting a status", %{conn: conn} do
113 idempotency_key = "Pikachu rocks!"
117 |> put_req_header("idempotency-key", idempotency_key)
118 |> post("/api/v1/statuses", %{
120 "spoiler_text" => "2hu",
121 "sensitive" => "false"
124 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
126 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
128 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
129 json_response(conn_one, 200)
131 assert Activity.get_by_id(id)
135 |> put_req_header("idempotency-key", idempotency_key)
136 |> post("/api/v1/statuses", %{
138 "spoiler_text" => "2hu",
139 "sensitive" => "false"
142 assert %{"id" => second_id} = json_response(conn_two, 200)
143 assert id == second_id
147 |> post("/api/v1/statuses", %{
149 "spoiler_text" => "2hu",
150 "sensitive" => "false"
153 assert %{"id" => third_id} = json_response(conn_three, 200)
154 refute id == third_id
156 # An activity that will expire:
158 expires_in = 120 * 60
162 |> post("api/v1/statuses", %{
163 "status" => "oolong",
164 "expires_in" => expires_in
167 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
168 assert activity = Activity.get_by_id(fourth_id)
169 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
171 estimated_expires_at =
172 NaiveDateTime.utc_now()
173 |> NaiveDateTime.add(expires_in)
174 |> NaiveDateTime.truncate(:second)
176 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
177 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
179 assert fourth_response["pleroma"]["expires_at"] ==
180 NaiveDateTime.to_iso8601(expiration.scheduled_at)
183 test "replying to a status", %{conn: conn} do
185 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
189 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
191 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
193 activity = Activity.get_by_id(id)
195 assert activity.data["context"] == replied_to.data["context"]
196 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
199 test "replying to a direct message with visibility other than direct", %{conn: conn} do
201 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
203 Enum.each(["public", "private", "unlisted"], fn visibility ->
206 |> post("/api/v1/statuses", %{
207 "status" => "@#{user.nickname} hey",
208 "in_reply_to_id" => replied_to.id,
209 "visibility" => visibility
212 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
216 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
219 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
221 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
222 assert Activity.get_by_id(id)
225 test "posting a sensitive status", %{conn: conn} do
228 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
230 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
231 assert Activity.get_by_id(id)
234 test "posting a fake status", %{conn: conn} do
237 |> post("/api/v1/statuses", %{
239 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
242 real_status = json_response(real_conn, 200)
245 assert Object.get_by_ap_id(real_status["uri"])
249 |> Map.put("id", nil)
250 |> Map.put("url", nil)
251 |> Map.put("uri", nil)
252 |> Map.put("created_at", nil)
253 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
257 |> post("/api/v1/statuses", %{
259 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
263 fake_status = json_response(fake_conn, 200)
266 refute Object.get_by_ap_id(fake_status["uri"])
270 |> Map.put("id", nil)
271 |> Map.put("url", nil)
272 |> Map.put("uri", nil)
273 |> Map.put("created_at", nil)
274 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
276 assert real_status == fake_status
279 test "posting a status with OGP link preview", %{conn: conn} do
280 Config.put([:rich_media, :enabled], true)
284 |> post("/api/v1/statuses", %{
285 "status" => "https://example.com/ogp"
288 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
289 assert Activity.get_by_id(id)
292 test "posting a direct status", %{conn: conn} do
293 user2 = insert(:user)
294 content = "direct cofe @#{user2.nickname}"
298 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
300 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
301 assert activity = Activity.get_by_id(id)
302 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
303 assert activity.data["to"] == [user2.ap_id]
304 assert activity.data["cc"] == []
308 describe "posting polls" do
309 test "posting a poll", %{conn: conn} do
311 time = NaiveDateTime.utc_now()
315 |> assign(:user, user)
316 |> post("/api/v1/statuses", %{
317 "status" => "Who is the #bestgrill?",
318 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
321 response = json_response(conn, 200)
323 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
324 title in ["Rei", "Asuka", "Misato"]
327 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
328 refute response["poll"]["expred"]
331 test "option limit is enforced", %{conn: conn} do
333 limit = Config.get([:instance, :poll_limits, :max_options])
337 |> assign(:user, user)
338 |> post("/api/v1/statuses", %{
340 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
343 %{"error" => error} = json_response(conn, 422)
344 assert error == "Poll can't contain more than #{limit} options"
347 test "option character limit is enforced", %{conn: conn} do
349 limit = Config.get([:instance, :poll_limits, :max_option_chars])
353 |> assign(:user, user)
354 |> post("/api/v1/statuses", %{
357 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
362 %{"error" => error} = json_response(conn, 422)
363 assert error == "Poll options cannot be longer than #{limit} characters each"
366 test "minimal date limit is enforced", %{conn: conn} do
368 limit = Config.get([:instance, :poll_limits, :min_expiration])
372 |> assign(:user, user)
373 |> post("/api/v1/statuses", %{
374 "status" => "imagine arbitrary limits",
376 "options" => ["this post was made by pleroma gang"],
377 "expires_in" => limit - 1
381 %{"error" => error} = json_response(conn, 422)
382 assert error == "Expiration date is too soon"
385 test "maximum date limit is enforced", %{conn: conn} do
387 limit = Config.get([:instance, :poll_limits, :max_expiration])
391 |> assign(:user, user)
392 |> post("/api/v1/statuses", %{
393 "status" => "imagine arbitrary limits",
395 "options" => ["this post was made by pleroma gang"],
396 "expires_in" => limit + 1
400 %{"error" => error} = json_response(conn, 422)
401 assert error == "Expiration date is too far in the future"
405 test "direct timeline", %{conn: conn} do
406 user_one = insert(:user)
407 user_two = insert(:user)
409 {:ok, user_two} = User.follow(user_two, user_one)
412 CommonAPI.post(user_one, %{
413 "status" => "Hi @#{user_two.nickname}!",
414 "visibility" => "direct"
417 {:ok, _follower_only} =
418 CommonAPI.post(user_one, %{
419 "status" => "Hi @#{user_two.nickname}!",
420 "visibility" => "private"
423 # Only direct should be visible here
426 |> assign(:user, user_two)
427 |> get("api/v1/timelines/direct")
429 [status] = json_response(res_conn, 200)
431 assert %{"visibility" => "direct"} = status
432 assert status["url"] != direct.data["id"]
434 # User should be able to see their own direct message
437 |> assign(:user, user_one)
438 |> get("api/v1/timelines/direct")
440 [status] = json_response(res_conn, 200)
442 assert %{"visibility" => "direct"} = status
444 # Both should be visible here
447 |> assign(:user, user_two)
448 |> get("api/v1/timelines/home")
450 [_s1, _s2] = json_response(res_conn, 200)
453 Enum.each(1..20, fn _ ->
455 CommonAPI.post(user_one, %{
456 "status" => "Hi @#{user_two.nickname}!",
457 "visibility" => "direct"
463 |> assign(:user, user_two)
464 |> get("api/v1/timelines/direct")
466 statuses = json_response(res_conn, 200)
467 assert length(statuses) == 20
471 |> assign(:user, user_two)
472 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
474 [status] = json_response(res_conn, 200)
476 assert status["url"] != direct.data["id"]
479 test "Conversations", %{conn: conn} do
480 user_one = insert(:user)
481 user_two = insert(:user)
482 user_three = insert(:user)
484 {:ok, user_two} = User.follow(user_two, user_one)
487 CommonAPI.post(user_one, %{
488 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
489 "visibility" => "direct"
492 {:ok, _follower_only} =
493 CommonAPI.post(user_one, %{
494 "status" => "Hi @#{user_two.nickname}!",
495 "visibility" => "private"
500 |> assign(:user, user_one)
501 |> get("/api/v1/conversations")
503 assert response = json_response(res_conn, 200)
508 "accounts" => res_accounts,
509 "last_status" => res_last_status,
514 account_ids = Enum.map(res_accounts, & &1["id"])
515 assert length(res_accounts) == 2
516 assert user_two.id in account_ids
517 assert user_three.id in account_ids
518 assert is_binary(res_id)
519 assert unread == true
520 assert res_last_status["id"] == direct.id
522 # Apparently undocumented API endpoint
525 |> assign(:user, user_one)
526 |> post("/api/v1/conversations/#{res_id}/read")
528 assert response = json_response(res_conn, 200)
529 assert length(response["accounts"]) == 2
530 assert response["last_status"]["id"] == direct.id
531 assert response["unread"] == false
533 # (vanilla) Mastodon frontend behaviour
536 |> assign(:user, user_one)
537 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
539 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
542 test "doesn't include DMs from blocked users", %{conn: conn} do
543 blocker = insert(:user)
544 blocked = insert(:user)
546 {:ok, blocker} = User.block(blocker, blocked)
548 {:ok, _blocked_direct} =
549 CommonAPI.post(blocked, %{
550 "status" => "Hi @#{blocker.nickname}!",
551 "visibility" => "direct"
555 CommonAPI.post(user, %{
556 "status" => "Hi @#{blocker.nickname}!",
557 "visibility" => "direct"
562 |> assign(:user, user)
563 |> get("api/v1/timelines/direct")
565 [status] = json_response(res_conn, 200)
566 assert status["id"] == direct.id
569 test "verify_credentials", %{conn: conn} do
574 |> assign(:user, user)
575 |> get("/api/v1/accounts/verify_credentials")
577 response = json_response(conn, 200)
579 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
580 assert response["pleroma"]["chat_token"]
581 assert id == to_string(user.id)
584 test "verify_credentials default scope unlisted", %{conn: conn} do
585 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
589 |> assign(:user, user)
590 |> get("/api/v1/accounts/verify_credentials")
592 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
593 assert id == to_string(user.id)
596 test "apps/verify_credentials", %{conn: conn} do
597 token = insert(:oauth_token)
601 |> assign(:user, token.user)
602 |> assign(:token, token)
603 |> get("/api/v1/apps/verify_credentials")
605 app = Repo.preload(token, :app).app
608 "name" => app.client_name,
609 "website" => app.website,
610 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
613 assert expected == json_response(conn, 200)
616 test "user avatar can be set", %{conn: conn} do
618 avatar_image = File.read!("test/fixtures/avatar_data_uri")
622 |> assign(:user, user)
623 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
625 user = refresh_record(user)
639 assert %{"url" => _} = json_response(conn, 200)
642 test "user avatar can be reset", %{conn: conn} do
647 |> assign(:user, user)
648 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
650 user = User.get_cached_by_id(user.id)
652 assert user.avatar == nil
654 assert %{"url" => nil} = json_response(conn, 200)
657 test "can set profile banner", %{conn: conn} do
662 |> assign(:user, user)
663 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
665 user = refresh_record(user)
666 assert user.info.banner["type"] == "Image"
668 assert %{"url" => _} = json_response(conn, 200)
671 test "can reset profile banner", %{conn: conn} do
676 |> assign(:user, user)
677 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
679 user = refresh_record(user)
680 assert user.info.banner == %{}
682 assert %{"url" => nil} = json_response(conn, 200)
685 test "background image can be set", %{conn: conn} do
690 |> assign(:user, user)
691 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
693 user = refresh_record(user)
694 assert user.info.background["type"] == "Image"
695 assert %{"url" => _} = json_response(conn, 200)
698 test "background image can be reset", %{conn: conn} do
703 |> assign(:user, user)
704 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
706 user = refresh_record(user)
707 assert user.info.background == %{}
708 assert %{"url" => nil} = json_response(conn, 200)
711 test "creates an oauth app", %{conn: conn} do
713 app_attrs = build(:oauth_app)
717 |> assign(:user, user)
718 |> post("/api/v1/apps", %{
719 client_name: app_attrs.client_name,
720 redirect_uris: app_attrs.redirect_uris
723 [app] = Repo.all(App)
726 "name" => app.client_name,
727 "website" => app.website,
728 "client_id" => app.client_id,
729 "client_secret" => app.client_secret,
730 "id" => app.id |> to_string(),
731 "redirect_uri" => app.redirect_uris,
732 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
735 assert expected == json_response(conn, 200)
738 test "get a status", %{conn: conn} do
739 activity = insert(:note_activity)
743 |> get("/api/v1/statuses/#{activity.id}")
745 assert %{"id" => id} = json_response(conn, 200)
746 assert id == to_string(activity.id)
749 test "get statuses by IDs", %{conn: conn} do
750 %{id: id1} = insert(:note_activity)
751 %{id: id2} = insert(:note_activity)
753 query_string = "ids[]=#{id1}&ids[]=#{id2}"
754 conn = get(conn, "/api/v1/statuses/?#{query_string}")
756 assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"])
759 describe "deleting a status" do
760 test "when you created it", %{conn: conn} do
761 activity = insert(:note_activity)
762 author = User.get_cached_by_ap_id(activity.data["actor"])
766 |> assign(:user, author)
767 |> delete("/api/v1/statuses/#{activity.id}")
769 assert %{} = json_response(conn, 200)
771 refute Activity.get_by_id(activity.id)
774 test "when you didn't create it", %{conn: conn} do
775 activity = insert(:note_activity)
780 |> assign(:user, user)
781 |> delete("/api/v1/statuses/#{activity.id}")
783 assert %{"error" => _} = json_response(conn, 403)
785 assert Activity.get_by_id(activity.id) == activity
788 test "when you're an admin or moderator", %{conn: conn} do
789 activity1 = insert(:note_activity)
790 activity2 = insert(:note_activity)
791 admin = insert(:user, info: %{is_admin: true})
792 moderator = insert(:user, info: %{is_moderator: true})
796 |> assign(:user, admin)
797 |> delete("/api/v1/statuses/#{activity1.id}")
799 assert %{} = json_response(res_conn, 200)
803 |> assign(:user, moderator)
804 |> delete("/api/v1/statuses/#{activity2.id}")
806 assert %{} = json_response(res_conn, 200)
808 refute Activity.get_by_id(activity1.id)
809 refute Activity.get_by_id(activity2.id)
813 describe "filters" do
814 test "creating a filter", %{conn: conn} do
817 filter = %Pleroma.Filter{
824 |> assign(:user, user)
825 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
827 assert response = json_response(conn, 200)
828 assert response["phrase"] == filter.phrase
829 assert response["context"] == filter.context
830 assert response["irreversible"] == false
831 assert response["id"] != nil
832 assert response["id"] != ""
835 test "fetching a list of filters", %{conn: conn} do
838 query_one = %Pleroma.Filter{
845 query_two = %Pleroma.Filter{
852 {:ok, filter_one} = Pleroma.Filter.create(query_one)
853 {:ok, filter_two} = Pleroma.Filter.create(query_two)
857 |> assign(:user, user)
858 |> get("/api/v1/filters")
859 |> json_response(200)
865 filters: [filter_two, filter_one]
869 test "get a filter", %{conn: conn} do
872 query = %Pleroma.Filter{
879 {:ok, filter} = Pleroma.Filter.create(query)
883 |> assign(:user, user)
884 |> get("/api/v1/filters/#{filter.filter_id}")
886 assert _response = json_response(conn, 200)
889 test "update a filter", %{conn: conn} do
892 query = %Pleroma.Filter{
899 {:ok, _filter} = Pleroma.Filter.create(query)
901 new = %Pleroma.Filter{
908 |> assign(:user, user)
909 |> put("/api/v1/filters/#{query.filter_id}", %{
914 assert response = json_response(conn, 200)
915 assert response["phrase"] == new.phrase
916 assert response["context"] == new.context
919 test "delete a filter", %{conn: conn} do
922 query = %Pleroma.Filter{
929 {:ok, filter} = Pleroma.Filter.create(query)
933 |> assign(:user, user)
934 |> delete("/api/v1/filters/#{filter.filter_id}")
936 assert response = json_response(conn, 200)
937 assert response == %{}
941 describe "list timelines" do
942 test "list timeline", %{conn: conn} do
944 other_user = insert(:user)
945 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
946 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
947 {:ok, list} = Pleroma.List.create("name", user)
948 {:ok, list} = Pleroma.List.follow(list, other_user)
952 |> assign(:user, user)
953 |> get("/api/v1/timelines/list/#{list.id}")
955 assert [%{"id" => id}] = json_response(conn, 200)
957 assert id == to_string(activity_two.id)
960 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
962 other_user = insert(:user)
963 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
965 {:ok, _activity_two} =
966 CommonAPI.post(other_user, %{
967 "status" => "Marisa is cute.",
968 "visibility" => "private"
971 {:ok, list} = Pleroma.List.create("name", user)
972 {:ok, list} = Pleroma.List.follow(list, other_user)
976 |> assign(:user, user)
977 |> get("/api/v1/timelines/list/#{list.id}")
979 assert [%{"id" => id}] = json_response(conn, 200)
981 assert id == to_string(activity_one.id)
985 describe "notifications" do
986 test "list of notifications", %{conn: conn} do
988 other_user = insert(:user)
990 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
992 {:ok, [_notification]} = Notification.create_notifications(activity)
996 |> assign(:user, user)
997 |> get("/api/v1/notifications")
1000 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1002 }\">@<span>#{user.nickname}</span></a></span>"
1004 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1005 assert response == expected_response
1008 test "getting a single notification", %{conn: conn} do
1009 user = insert(:user)
1010 other_user = insert(:user)
1012 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1014 {:ok, [notification]} = Notification.create_notifications(activity)
1018 |> assign(:user, user)
1019 |> get("/api/v1/notifications/#{notification.id}")
1022 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1024 }\">@<span>#{user.nickname}</span></a></span>"
1026 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1027 assert response == expected_response
1030 test "dismissing a single notification", %{conn: conn} do
1031 user = insert(:user)
1032 other_user = insert(:user)
1034 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1036 {:ok, [notification]} = Notification.create_notifications(activity)
1040 |> assign(:user, user)
1041 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1043 assert %{} = json_response(conn, 200)
1046 test "clearing all notifications", %{conn: conn} do
1047 user = insert(:user)
1048 other_user = insert(:user)
1050 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1052 {:ok, [_notification]} = Notification.create_notifications(activity)
1056 |> assign(:user, user)
1057 |> post("/api/v1/notifications/clear")
1059 assert %{} = json_response(conn, 200)
1063 |> assign(:user, user)
1064 |> get("/api/v1/notifications")
1066 assert all = json_response(conn, 200)
1070 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1071 user = insert(:user)
1072 other_user = insert(:user)
1074 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1075 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1076 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1077 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1079 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1080 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1081 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1082 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1086 |> assign(:user, user)
1091 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1093 result = json_response(conn_res, 200)
1094 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1099 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1101 result = json_response(conn_res, 200)
1102 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1107 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1109 result = json_response(conn_res, 200)
1110 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1113 test "filters notifications using exclude_types", %{conn: conn} do
1114 user = insert(:user)
1115 other_user = insert(:user)
1117 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1118 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1119 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1120 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1121 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1123 mention_notification_id =
1124 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1126 favorite_notification_id =
1127 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1129 reblog_notification_id =
1130 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1132 follow_notification_id =
1133 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1137 |> assign(:user, user)
1140 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1142 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1145 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1147 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1150 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1152 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1155 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1157 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1160 test "destroy multiple", %{conn: conn} do
1161 user = insert(:user)
1162 other_user = insert(:user)
1164 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1165 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1166 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1167 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1169 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1170 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1171 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1172 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1176 |> assign(:user, user)
1180 |> get("/api/v1/notifications")
1182 result = json_response(conn_res, 200)
1183 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1187 |> assign(:user, other_user)
1191 |> get("/api/v1/notifications")
1193 result = json_response(conn_res, 200)
1194 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1198 |> delete("/api/v1/notifications/destroy_multiple", %{
1199 "ids" => [notification1_id, notification2_id]
1202 assert json_response(conn_destroy, 200) == %{}
1206 |> get("/api/v1/notifications")
1208 result = json_response(conn_res, 200)
1209 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1212 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1213 user = insert(:user)
1214 user2 = insert(:user)
1216 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1217 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1219 conn = assign(conn, :user, user)
1221 conn = get(conn, "/api/v1/notifications")
1223 assert length(json_response(conn, 200)) == 1
1225 {:ok, user} = User.mute(user, user2)
1227 conn = assign(build_conn(), :user, user)
1228 conn = get(conn, "/api/v1/notifications")
1230 assert json_response(conn, 200) == []
1233 test "see notifications after muting user without notifications", %{conn: conn} do
1234 user = insert(:user)
1235 user2 = insert(:user)
1237 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1238 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1240 conn = assign(conn, :user, user)
1242 conn = get(conn, "/api/v1/notifications")
1244 assert length(json_response(conn, 200)) == 1
1246 {:ok, user} = User.mute(user, user2, false)
1248 conn = assign(build_conn(), :user, user)
1249 conn = get(conn, "/api/v1/notifications")
1251 assert length(json_response(conn, 200)) == 1
1254 test "see notifications after muting user with notifications and with_muted parameter", %{
1257 user = insert(:user)
1258 user2 = insert(:user)
1260 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1261 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1263 conn = assign(conn, :user, user)
1265 conn = get(conn, "/api/v1/notifications")
1267 assert length(json_response(conn, 200)) == 1
1269 {:ok, user} = User.mute(user, user2)
1271 conn = assign(build_conn(), :user, user)
1272 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1274 assert length(json_response(conn, 200)) == 1
1278 describe "subscription_notifications" do
1280 user = insert(:user)
1281 subscriber = insert(:user)
1283 User.subscribe(subscriber, user)
1285 {:ok, %{user: user, subscriber: subscriber}}
1288 test "list of notifications", %{conn: conn, user: user, subscriber: subscriber} do
1289 status_text = "Hello"
1290 {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text})
1294 |> assign(:user, subscriber)
1295 |> get("/api/v1/notifications/subscription")
1297 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1298 assert response == status_text
1301 test "getting a single notification", %{conn: conn, user: user, subscriber: subscriber} do
1302 status_text = "Hello"
1304 {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text})
1305 [notification] = Repo.all(SubscriptionNotification)
1309 |> assign(:user, subscriber)
1310 |> get("/api/v1/notifications/subscription/#{notification.id}")
1312 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1313 assert response == status_text
1316 test "dismissing a single notification also deletes it", %{
1319 subscriber: subscriber
1321 status_text = "Hello"
1322 {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text})
1324 [notification] = Repo.all(SubscriptionNotification)
1328 |> assign(:user, subscriber)
1329 |> post("/api/v1/notifications/subscription/dismiss", %{"id" => notification.id})
1331 assert %{} = json_response(conn, 200)
1333 assert Repo.all(SubscriptionNotification) == []
1336 test "clearing all notifications also deletes them", %{
1339 subscriber: subscriber
1341 status_text1 = "Hello"
1342 status_text2 = "Hello again"
1343 {:ok, _activity1} = CommonAPI.post(user, %{"status" => status_text1})
1344 {:ok, _activity2} = CommonAPI.post(user, %{"status" => status_text2})
1348 |> assign(:user, subscriber)
1349 |> post("/api/v1/notifications/subscription/clear")
1351 assert %{} = json_response(conn, 200)
1355 |> assign(:user, subscriber)
1356 |> get("/api/v1/notifications/subscription")
1358 assert json_response(conn, 200) == []
1360 assert Repo.all(SubscriptionNotification) == []
1363 test "paginates notifications using min_id, since_id, max_id, and limit", %{
1366 subscriber: subscriber
1368 {:ok, activity1} = CommonAPI.post(user, %{"status" => "Hello 1"})
1369 {:ok, activity2} = CommonAPI.post(user, %{"status" => "Hello 2"})
1370 {:ok, activity3} = CommonAPI.post(user, %{"status" => "Hello 3"})
1371 {:ok, activity4} = CommonAPI.post(user, %{"status" => "Hello 4"})
1374 Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string()
1377 Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string()
1380 Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string()
1383 Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string()
1385 conn = assign(conn, :user, subscriber)
1389 get(conn, "/api/v1/notifications/subscription?limit=2&min_id=#{notification1_id}")
1391 result = json_response(conn_res, 200)
1392 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1396 get(conn, "/api/v1/notifications/subscription?limit=2&since_id=#{notification1_id}")
1398 result = json_response(conn_res, 200)
1399 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1403 get(conn, "/api/v1/notifications/subscription?limit=2&max_id=#{notification4_id}")
1405 result = json_response(conn_res, 200)
1406 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1409 test "destroy multiple", %{conn: conn, user: user1, subscriber: user2} do
1410 # mutual subscription
1411 User.subscribe(user1, user2)
1413 {:ok, activity1} = CommonAPI.post(user1, %{"status" => "Hello 1"})
1414 {:ok, activity2} = CommonAPI.post(user1, %{"status" => "World 1"})
1415 {:ok, activity3} = CommonAPI.post(user2, %{"status" => "Hello 2"})
1416 {:ok, activity4} = CommonAPI.post(user2, %{"status" => "World 2"})
1419 Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string()
1422 Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string()
1425 Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string()
1428 Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string()
1430 conn = assign(conn, :user, user1)
1432 conn_res = get(conn, "/api/v1/notifications/subscription")
1434 result = json_response(conn_res, 200)
1436 Enum.each(result, fn %{"id" => id} ->
1437 assert id in [notification3_id, notification4_id]
1440 conn2 = assign(conn, :user, user2)
1442 conn_res = get(conn2, "/api/v1/notifications/subscription")
1444 result = json_response(conn_res, 200)
1446 Enum.each(result, fn %{"id" => id} ->
1447 assert id in [notification1_id, notification2_id]
1451 delete(conn, "/api/v1/notifications/subscription/destroy_multiple", %{
1452 "ids" => [notification3_id, notification4_id]
1455 assert json_response(conn_destroy, 200) == %{}
1457 conn_res = get(conn2, "/api/v1/notifications/subscription")
1459 result = json_response(conn_res, 200)
1461 Enum.each(result, fn %{"id" => id} ->
1462 assert id in [notification1_id, notification2_id]
1465 assert length(Repo.all(SubscriptionNotification)) == 2
1469 describe "reblogging" do
1470 test "reblogs and returns the reblogged status", %{conn: conn} do
1471 activity = insert(:note_activity)
1472 user = insert(:user)
1476 |> assign(:user, user)
1477 |> post("/api/v1/statuses/#{activity.id}/reblog")
1480 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1482 } = json_response(conn, 200)
1484 assert to_string(activity.id) == id
1487 test "reblogged status for another user", %{conn: conn} do
1488 activity = insert(:note_activity)
1489 user1 = insert(:user)
1490 user2 = insert(:user)
1491 user3 = insert(:user)
1492 CommonAPI.favorite(activity.id, user2)
1493 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1494 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1495 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1499 |> assign(:user, user3)
1500 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1503 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1504 "reblogged" => false,
1505 "favourited" => false,
1506 "bookmarked" => false
1507 } = json_response(conn_res, 200)
1511 |> assign(:user, user2)
1512 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1515 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1516 "reblogged" => true,
1517 "favourited" => true,
1518 "bookmarked" => true
1519 } = json_response(conn_res, 200)
1521 assert to_string(activity.id) == id
1524 test "returns 400 error when activity is not exist", %{conn: conn} do
1525 user = insert(:user)
1529 |> assign(:user, user)
1530 |> post("/api/v1/statuses/foo/reblog")
1532 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1536 describe "unreblogging" do
1537 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1538 activity = insert(:note_activity)
1539 user = insert(:user)
1541 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1545 |> assign(:user, user)
1546 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1548 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1550 assert to_string(activity.id) == id
1553 test "returns 400 error when activity is not exist", %{conn: conn} do
1554 user = insert(:user)
1558 |> assign(:user, user)
1559 |> post("/api/v1/statuses/foo/unreblog")
1561 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1565 describe "favoriting" do
1566 test "favs a status and returns it", %{conn: conn} do
1567 activity = insert(:note_activity)
1568 user = insert(:user)
1572 |> assign(:user, user)
1573 |> post("/api/v1/statuses/#{activity.id}/favourite")
1575 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1576 json_response(conn, 200)
1578 assert to_string(activity.id) == id
1581 test "returns 400 error for a wrong id", %{conn: conn} do
1582 user = insert(:user)
1586 |> assign(:user, user)
1587 |> post("/api/v1/statuses/1/favourite")
1589 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1593 describe "unfavoriting" do
1594 test "unfavorites a status and returns it", %{conn: conn} do
1595 activity = insert(:note_activity)
1596 user = insert(:user)
1598 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1602 |> assign(:user, user)
1603 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1605 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1606 json_response(conn, 200)
1608 assert to_string(activity.id) == id
1611 test "returns 400 error for a wrong id", %{conn: conn} do
1612 user = insert(:user)
1616 |> assign(:user, user)
1617 |> post("/api/v1/statuses/1/unfavourite")
1619 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1623 describe "user timelines" do
1624 test "gets a users statuses", %{conn: conn} do
1625 user_one = insert(:user)
1626 user_two = insert(:user)
1627 user_three = insert(:user)
1629 {:ok, user_three} = User.follow(user_three, user_one)
1631 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1633 {:ok, direct_activity} =
1634 CommonAPI.post(user_one, %{
1635 "status" => "Hi, @#{user_two.nickname}.",
1636 "visibility" => "direct"
1639 {:ok, private_activity} =
1640 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1644 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1646 assert [%{"id" => id}] = json_response(resp, 200)
1647 assert id == to_string(activity.id)
1651 |> assign(:user, user_two)
1652 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1654 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1655 assert id_one == to_string(direct_activity.id)
1656 assert id_two == to_string(activity.id)
1660 |> assign(:user, user_three)
1661 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1663 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1664 assert id_one == to_string(private_activity.id)
1665 assert id_two == to_string(activity.id)
1668 test "unimplemented pinned statuses feature", %{conn: conn} do
1669 note = insert(:note_activity)
1670 user = User.get_cached_by_ap_id(note.data["actor"])
1674 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1676 assert json_response(conn, 200) == []
1679 test "gets an users media", %{conn: conn} do
1680 note = insert(:note_activity)
1681 user = User.get_cached_by_ap_id(note.data["actor"])
1683 file = %Plug.Upload{
1684 content_type: "image/jpg",
1685 path: Path.absname("test/fixtures/image.jpg"),
1686 filename: "an_image.jpg"
1689 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1691 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1695 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1697 assert [%{"id" => id}] = json_response(conn, 200)
1698 assert id == to_string(image_post.id)
1702 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1704 assert [%{"id" => id}] = json_response(conn, 200)
1705 assert id == to_string(image_post.id)
1708 test "gets a user's statuses without reblogs", %{conn: conn} do
1709 user = insert(:user)
1710 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1711 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1715 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1717 assert [%{"id" => id}] = json_response(conn, 200)
1718 assert id == to_string(post.id)
1722 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1724 assert [%{"id" => id}] = json_response(conn, 200)
1725 assert id == to_string(post.id)
1728 test "filters user's statuses by a hashtag", %{conn: conn} do
1729 user = insert(:user)
1730 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1731 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1735 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1737 assert [%{"id" => id}] = json_response(conn, 200)
1738 assert id == to_string(post.id)
1742 describe "user relationships" do
1743 test "returns the relationships for the current user", %{conn: conn} do
1744 user = insert(:user)
1745 other_user = insert(:user)
1746 {:ok, user} = User.follow(user, other_user)
1750 |> assign(:user, user)
1751 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1753 assert [relationship] = json_response(conn, 200)
1755 assert to_string(other_user.id) == relationship["id"]
1759 describe "media upload" do
1761 user = insert(:user)
1765 |> assign(:user, user)
1767 image = %Plug.Upload{
1768 content_type: "image/jpg",
1769 path: Path.absname("test/fixtures/image.jpg"),
1770 filename: "an_image.jpg"
1773 [conn: conn, image: image]
1776 clear_config([:media_proxy])
1777 clear_config([Pleroma.Upload])
1779 test "returns uploaded image", %{conn: conn, image: image} do
1780 desc = "Description of the image"
1784 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1785 |> json_response(:ok)
1787 assert media["type"] == "image"
1788 assert media["description"] == desc
1791 object = Repo.get(Object, media["id"])
1792 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1796 describe "locked accounts" do
1797 test "/api/v1/follow_requests works" do
1798 user = insert(:user, %{info: %User.Info{locked: true}})
1799 other_user = insert(:user)
1801 {:ok, _activity} = ActivityPub.follow(other_user, user)
1803 user = User.get_cached_by_id(user.id)
1804 other_user = User.get_cached_by_id(other_user.id)
1806 assert User.following?(other_user, user) == false
1810 |> assign(:user, user)
1811 |> get("/api/v1/follow_requests")
1813 assert [relationship] = json_response(conn, 200)
1814 assert to_string(other_user.id) == relationship["id"]
1817 test "/api/v1/follow_requests/:id/authorize works" do
1818 user = insert(:user, %{info: %User.Info{locked: true}})
1819 other_user = insert(:user)
1821 {:ok, _activity} = ActivityPub.follow(other_user, user)
1823 user = User.get_cached_by_id(user.id)
1824 other_user = User.get_cached_by_id(other_user.id)
1826 assert User.following?(other_user, user) == false
1830 |> assign(:user, user)
1831 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1833 assert relationship = json_response(conn, 200)
1834 assert to_string(other_user.id) == relationship["id"]
1836 user = User.get_cached_by_id(user.id)
1837 other_user = User.get_cached_by_id(other_user.id)
1839 assert User.following?(other_user, user) == true
1842 test "verify_credentials", %{conn: conn} do
1843 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1847 |> assign(:user, user)
1848 |> get("/api/v1/accounts/verify_credentials")
1850 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1851 assert id == to_string(user.id)
1854 test "/api/v1/follow_requests/:id/reject works" do
1855 user = insert(:user, %{info: %User.Info{locked: true}})
1856 other_user = insert(:user)
1858 {:ok, _activity} = ActivityPub.follow(other_user, user)
1860 user = User.get_cached_by_id(user.id)
1864 |> assign(:user, user)
1865 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1867 assert relationship = json_response(conn, 200)
1868 assert to_string(other_user.id) == relationship["id"]
1870 user = User.get_cached_by_id(user.id)
1871 other_user = User.get_cached_by_id(other_user.id)
1873 assert User.following?(other_user, user) == false
1877 describe "account fetching" do
1878 test "works by id" do
1879 user = insert(:user)
1883 |> get("/api/v1/accounts/#{user.id}")
1885 assert %{"id" => id} = json_response(conn, 200)
1886 assert id == to_string(user.id)
1890 |> get("/api/v1/accounts/-1")
1892 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1895 test "works by nickname" do
1896 user = insert(:user)
1900 |> get("/api/v1/accounts/#{user.nickname}")
1902 assert %{"id" => id} = json_response(conn, 200)
1903 assert id == user.id
1906 test "works by nickname for remote users" do
1907 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1908 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1909 user = insert(:user, nickname: "user@example.com", local: false)
1913 |> get("/api/v1/accounts/#{user.nickname}")
1915 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1916 assert %{"id" => id} = json_response(conn, 200)
1917 assert id == user.id
1920 test "respects limit_to_local_content == :all for remote user nicknames" do
1921 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1922 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1924 user = insert(:user, nickname: "user@example.com", local: false)
1928 |> get("/api/v1/accounts/#{user.nickname}")
1930 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1931 assert json_response(conn, 404)
1934 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1935 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1936 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1938 user = insert(:user, nickname: "user@example.com", local: false)
1939 reading_user = insert(:user)
1943 |> get("/api/v1/accounts/#{user.nickname}")
1945 assert json_response(conn, 404)
1949 |> assign(:user, reading_user)
1950 |> get("/api/v1/accounts/#{user.nickname}")
1952 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1953 assert %{"id" => id} = json_response(conn, 200)
1954 assert id == user.id
1958 test "mascot upload", %{conn: conn} do
1959 user = insert(:user)
1961 non_image_file = %Plug.Upload{
1962 content_type: "audio/mpeg",
1963 path: Path.absname("test/fixtures/sound.mp3"),
1964 filename: "sound.mp3"
1969 |> assign(:user, user)
1970 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1972 assert json_response(conn, 415)
1974 file = %Plug.Upload{
1975 content_type: "image/jpg",
1976 path: Path.absname("test/fixtures/image.jpg"),
1977 filename: "an_image.jpg"
1982 |> assign(:user, user)
1983 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1985 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1988 test "mascot retrieving", %{conn: conn} do
1989 user = insert(:user)
1990 # When user hasn't set a mascot, we should just get pleroma tan back
1993 |> assign(:user, user)
1994 |> get("/api/v1/pleroma/mascot")
1996 assert %{"url" => url} = json_response(conn, 200)
1997 assert url =~ "pleroma-fox-tan-smol"
1999 # When a user sets their mascot, we should get that back
2000 file = %Plug.Upload{
2001 content_type: "image/jpg",
2002 path: Path.absname("test/fixtures/image.jpg"),
2003 filename: "an_image.jpg"
2008 |> assign(:user, user)
2009 |> put("/api/v1/pleroma/mascot", %{"file" => file})
2011 assert json_response(conn, 200)
2013 user = User.get_cached_by_id(user.id)
2017 |> assign(:user, user)
2018 |> get("/api/v1/pleroma/mascot")
2020 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
2021 assert url =~ "an_image"
2024 test "hashtag timeline", %{conn: conn} do
2025 following = insert(:user)
2028 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
2030 {:ok, [_activity]} =
2031 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
2035 |> get("/api/v1/timelines/tag/2hu")
2037 assert [%{"id" => id}] = json_response(nconn, 200)
2039 assert id == to_string(activity.id)
2041 # works for different capitalization too
2044 |> get("/api/v1/timelines/tag/2HU")
2046 assert [%{"id" => id}] = json_response(nconn, 200)
2048 assert id == to_string(activity.id)
2052 test "multi-hashtag timeline", %{conn: conn} do
2053 user = insert(:user)
2055 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
2056 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
2057 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
2061 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
2063 [status_none, status_test1, status_test] = json_response(any_test, 200)
2065 assert to_string(activity_test.id) == status_test["id"]
2066 assert to_string(activity_test1.id) == status_test1["id"]
2067 assert to_string(activity_none.id) == status_none["id"]
2071 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
2073 assert [status_test1] == json_response(restricted_test, 200)
2075 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
2077 assert [status_none] == json_response(all_test, 200)
2080 test "getting followers", %{conn: conn} do
2081 user = insert(:user)
2082 other_user = insert(:user)
2083 {:ok, user} = User.follow(user, other_user)
2087 |> get("/api/v1/accounts/#{other_user.id}/followers")
2089 assert [%{"id" => id}] = json_response(conn, 200)
2090 assert id == to_string(user.id)
2093 test "getting followers, hide_followers", %{conn: conn} do
2094 user = insert(:user)
2095 other_user = insert(:user, %{info: %{hide_followers: true}})
2096 {:ok, _user} = User.follow(user, other_user)
2100 |> get("/api/v1/accounts/#{other_user.id}/followers")
2102 assert [] == json_response(conn, 200)
2105 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
2106 user = insert(:user)
2107 other_user = insert(:user, %{info: %{hide_followers: true}})
2108 {:ok, _user} = User.follow(user, other_user)
2112 |> assign(:user, other_user)
2113 |> get("/api/v1/accounts/#{other_user.id}/followers")
2115 refute [] == json_response(conn, 200)
2118 test "getting followers, pagination", %{conn: conn} do
2119 user = insert(:user)
2120 follower1 = insert(:user)
2121 follower2 = insert(:user)
2122 follower3 = insert(:user)
2123 {:ok, _} = User.follow(follower1, user)
2124 {:ok, _} = User.follow(follower2, user)
2125 {:ok, _} = User.follow(follower3, user)
2129 |> assign(:user, user)
2133 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
2135 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2136 assert id3 == follower3.id
2137 assert id2 == follower2.id
2141 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
2143 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2144 assert id2 == follower2.id
2145 assert id1 == follower1.id
2149 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
2151 assert [%{"id" => id2}] = json_response(res_conn, 200)
2152 assert id2 == follower2.id
2154 assert [link_header] = get_resp_header(res_conn, "link")
2155 assert link_header =~ ~r/min_id=#{follower2.id}/
2156 assert link_header =~ ~r/max_id=#{follower2.id}/
2159 test "getting following", %{conn: conn} do
2160 user = insert(:user)
2161 other_user = insert(:user)
2162 {:ok, user} = User.follow(user, other_user)
2166 |> get("/api/v1/accounts/#{user.id}/following")
2168 assert [%{"id" => id}] = json_response(conn, 200)
2169 assert id == to_string(other_user.id)
2172 test "getting following, hide_follows", %{conn: conn} do
2173 user = insert(:user, %{info: %{hide_follows: true}})
2174 other_user = insert(:user)
2175 {:ok, user} = User.follow(user, other_user)
2179 |> get("/api/v1/accounts/#{user.id}/following")
2181 assert [] == json_response(conn, 200)
2184 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2185 user = insert(:user, %{info: %{hide_follows: true}})
2186 other_user = insert(:user)
2187 {:ok, user} = User.follow(user, other_user)
2191 |> assign(:user, user)
2192 |> get("/api/v1/accounts/#{user.id}/following")
2194 refute [] == json_response(conn, 200)
2197 test "getting following, pagination", %{conn: conn} do
2198 user = insert(:user)
2199 following1 = insert(:user)
2200 following2 = insert(:user)
2201 following3 = insert(:user)
2202 {:ok, _} = User.follow(user, following1)
2203 {:ok, _} = User.follow(user, following2)
2204 {:ok, _} = User.follow(user, following3)
2208 |> assign(:user, user)
2212 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2214 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2215 assert id3 == following3.id
2216 assert id2 == following2.id
2220 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2222 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2223 assert id2 == following2.id
2224 assert id1 == following1.id
2228 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2230 assert [%{"id" => id2}] = json_response(res_conn, 200)
2231 assert id2 == following2.id
2233 assert [link_header] = get_resp_header(res_conn, "link")
2234 assert link_header =~ ~r/min_id=#{following2.id}/
2235 assert link_header =~ ~r/max_id=#{following2.id}/
2238 test "following / unfollowing a user", %{conn: conn} do
2239 user = insert(:user)
2240 other_user = insert(:user)
2244 |> assign(:user, user)
2245 |> post("/api/v1/accounts/#{other_user.id}/follow")
2247 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2249 user = User.get_cached_by_id(user.id)
2253 |> assign(:user, user)
2254 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2256 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2258 user = User.get_cached_by_id(user.id)
2262 |> assign(:user, user)
2263 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2265 assert %{"id" => id} = json_response(conn, 200)
2266 assert id == to_string(other_user.id)
2269 test "following without reblogs" do
2270 follower = insert(:user)
2271 followed = insert(:user)
2272 other_user = insert(:user)
2276 |> assign(:user, follower)
2277 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2279 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2281 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2282 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2286 |> assign(:user, User.get_cached_by_id(follower.id))
2287 |> get("/api/v1/timelines/home")
2289 assert [] == json_response(conn, 200)
2293 |> assign(:user, follower)
2294 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2296 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2300 |> assign(:user, User.get_cached_by_id(follower.id))
2301 |> get("/api/v1/timelines/home")
2303 expected_activity_id = reblog.id
2304 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2307 test "following / unfollowing errors" do
2308 user = insert(:user)
2312 |> assign(:user, user)
2315 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2316 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2319 user = User.get_cached_by_id(user.id)
2320 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2321 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2323 # self follow via uri
2324 user = User.get_cached_by_id(user.id)
2325 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2326 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2328 # follow non existing user
2329 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2330 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2332 # follow non existing user via uri
2333 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2334 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2336 # unfollow non existing user
2337 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2338 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2341 describe "mute/unmute" do
2342 test "with notifications", %{conn: conn} do
2343 user = insert(:user)
2344 other_user = insert(:user)
2348 |> assign(:user, user)
2349 |> post("/api/v1/accounts/#{other_user.id}/mute")
2351 response = json_response(conn, 200)
2353 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2354 user = User.get_cached_by_id(user.id)
2358 |> assign(:user, user)
2359 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2361 response = json_response(conn, 200)
2362 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2365 test "without notifications", %{conn: conn} do
2366 user = insert(:user)
2367 other_user = insert(:user)
2371 |> assign(:user, user)
2372 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2374 response = json_response(conn, 200)
2376 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2377 user = User.get_cached_by_id(user.id)
2381 |> assign(:user, user)
2382 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2384 response = json_response(conn, 200)
2385 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2389 test "subscribing / unsubscribing to a user", %{conn: conn} do
2390 user = insert(:user)
2391 subscription_target = insert(:user)
2395 |> assign(:user, user)
2396 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2398 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2402 |> assign(:user, user)
2403 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2405 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2408 test "getting a list of mutes", %{conn: conn} do
2409 user = insert(:user)
2410 other_user = insert(:user)
2412 {:ok, user} = User.mute(user, other_user)
2416 |> assign(:user, user)
2417 |> get("/api/v1/mutes")
2419 other_user_id = to_string(other_user.id)
2420 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2423 test "blocking / unblocking a user", %{conn: conn} do
2424 user = insert(:user)
2425 other_user = insert(:user)
2429 |> assign(:user, user)
2430 |> post("/api/v1/accounts/#{other_user.id}/block")
2432 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2434 user = User.get_cached_by_id(user.id)
2438 |> assign(:user, user)
2439 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2441 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2444 test "getting a list of blocks", %{conn: conn} do
2445 user = insert(:user)
2446 other_user = insert(:user)
2448 {:ok, user} = User.block(user, other_user)
2452 |> assign(:user, user)
2453 |> get("/api/v1/blocks")
2455 other_user_id = to_string(other_user.id)
2456 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2459 test "blocking / unblocking a domain", %{conn: conn} do
2460 user = insert(:user)
2461 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2465 |> assign(:user, user)
2466 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2468 assert %{} = json_response(conn, 200)
2469 user = User.get_cached_by_ap_id(user.ap_id)
2470 assert User.blocks?(user, other_user)
2474 |> assign(:user, user)
2475 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2477 assert %{} = json_response(conn, 200)
2478 user = User.get_cached_by_ap_id(user.ap_id)
2479 refute User.blocks?(user, other_user)
2482 test "getting a list of domain blocks", %{conn: conn} do
2483 user = insert(:user)
2485 {:ok, user} = User.block_domain(user, "bad.site")
2486 {:ok, user} = User.block_domain(user, "even.worse.site")
2490 |> assign(:user, user)
2491 |> get("/api/v1/domain_blocks")
2493 domain_blocks = json_response(conn, 200)
2495 assert "bad.site" in domain_blocks
2496 assert "even.worse.site" in domain_blocks
2499 test "unimplemented follow_requests, blocks, domain blocks" do
2500 user = insert(:user)
2502 ["blocks", "domain_blocks", "follow_requests"]
2503 |> Enum.each(fn endpoint ->
2506 |> assign(:user, user)
2507 |> get("/api/v1/#{endpoint}")
2509 assert [] = json_response(conn, 200)
2513 test "returns the favorites of a user", %{conn: conn} do
2514 user = insert(:user)
2515 other_user = insert(:user)
2517 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2518 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2520 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2524 |> assign(:user, user)
2525 |> get("/api/v1/favourites")
2527 assert [status] = json_response(first_conn, 200)
2528 assert status["id"] == to_string(activity.id)
2530 assert [{"link", _link_header}] =
2531 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2533 # Honours query params
2534 {:ok, second_activity} =
2535 CommonAPI.post(other_user, %{
2537 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2540 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2542 last_like = status["id"]
2546 |> assign(:user, user)
2547 |> get("/api/v1/favourites?since_id=#{last_like}")
2549 assert [second_status] = json_response(second_conn, 200)
2550 assert second_status["id"] == to_string(second_activity.id)
2554 |> assign(:user, user)
2555 |> get("/api/v1/favourites?limit=0")
2557 assert [] = json_response(third_conn, 200)
2560 describe "getting favorites timeline of specified user" do
2562 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2563 [current_user: current_user, user: user]
2566 test "returns list of statuses favorited by specified user", %{
2568 current_user: current_user,
2571 [activity | _] = insert_pair(:note_activity)
2572 CommonAPI.favorite(activity.id, user)
2576 |> assign(:user, current_user)
2577 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2578 |> json_response(:ok)
2582 assert length(response) == 1
2583 assert like["id"] == activity.id
2586 test "returns favorites for specified user_id when user is not logged in", %{
2590 activity = insert(:note_activity)
2591 CommonAPI.favorite(activity.id, user)
2595 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2596 |> json_response(:ok)
2598 assert length(response) == 1
2601 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2603 current_user: current_user,
2607 CommonAPI.post(current_user, %{
2608 "status" => "Hi @#{user.nickname}!",
2609 "visibility" => "direct"
2612 CommonAPI.favorite(direct.id, user)
2616 |> assign(:user, current_user)
2617 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2618 |> json_response(:ok)
2620 assert length(response) == 1
2622 anonymous_response =
2624 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2625 |> json_response(:ok)
2627 assert Enum.empty?(anonymous_response)
2630 test "does not return others' favorited DM when user is not one of recipients", %{
2632 current_user: current_user,
2635 user_two = insert(:user)
2638 CommonAPI.post(user_two, %{
2639 "status" => "Hi @#{user.nickname}!",
2640 "visibility" => "direct"
2643 CommonAPI.favorite(direct.id, user)
2647 |> assign(:user, current_user)
2648 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2649 |> json_response(:ok)
2651 assert Enum.empty?(response)
2654 test "paginates favorites using since_id and max_id", %{
2656 current_user: current_user,
2659 activities = insert_list(10, :note_activity)
2661 Enum.each(activities, fn activity ->
2662 CommonAPI.favorite(activity.id, user)
2665 third_activity = Enum.at(activities, 2)
2666 seventh_activity = Enum.at(activities, 6)
2670 |> assign(:user, current_user)
2671 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2672 since_id: third_activity.id,
2673 max_id: seventh_activity.id
2675 |> json_response(:ok)
2677 assert length(response) == 3
2678 refute third_activity in response
2679 refute seventh_activity in response
2682 test "limits favorites using limit parameter", %{
2684 current_user: current_user,
2688 |> insert_list(:note_activity)
2689 |> Enum.each(fn activity ->
2690 CommonAPI.favorite(activity.id, user)
2695 |> assign(:user, current_user)
2696 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2697 |> json_response(:ok)
2699 assert length(response) == 3
2702 test "returns empty response when user does not have any favorited statuses", %{
2704 current_user: current_user,
2709 |> assign(:user, current_user)
2710 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2711 |> json_response(:ok)
2713 assert Enum.empty?(response)
2716 test "returns 404 error when specified user is not exist", %{conn: conn} do
2717 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2719 assert json_response(conn, 404) == %{"error" => "Record not found"}
2722 test "returns 403 error when user has hidden own favorites", %{
2724 current_user: current_user
2726 user = insert(:user, %{info: %{hide_favorites: true}})
2727 activity = insert(:note_activity)
2728 CommonAPI.favorite(activity.id, user)
2732 |> assign(:user, current_user)
2733 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2735 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2738 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2739 user = insert(:user)
2740 activity = insert(:note_activity)
2741 CommonAPI.favorite(activity.id, user)
2745 |> assign(:user, current_user)
2746 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2748 assert user.info.hide_favorites
2749 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2753 test "get instance information", %{conn: conn} do
2754 conn = get(conn, "/api/v1/instance")
2755 assert result = json_response(conn, 200)
2757 email = Config.get([:instance, :email])
2758 # Note: not checking for "max_toot_chars" since it's optional
2764 "email" => from_config_email,
2766 "streaming_api" => _
2771 "registrations" => _,
2775 assert email == from_config_email
2778 test "get instance stats", %{conn: conn} do
2779 user = insert(:user, %{local: true})
2781 user2 = insert(:user, %{local: true})
2782 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2784 insert(:user, %{local: false, nickname: "u@peer1.com"})
2785 insert(:user, %{local: false, nickname: "u@peer2.com"})
2787 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2789 # Stats should count users with missing or nil `info.deactivated` value
2790 user = User.get_cached_by_id(user.id)
2791 info_change = Changeset.change(user.info, %{deactivated: nil})
2795 |> Changeset.change()
2796 |> Changeset.put_embed(:info, info_change)
2797 |> User.update_and_set_cache()
2799 Pleroma.Stats.force_update()
2801 conn = get(conn, "/api/v1/instance")
2803 assert result = json_response(conn, 200)
2805 stats = result["stats"]
2808 assert stats["user_count"] == 1
2809 assert stats["status_count"] == 1
2810 assert stats["domain_count"] == 2
2813 test "get peers", %{conn: conn} do
2814 insert(:user, %{local: false, nickname: "u@peer1.com"})
2815 insert(:user, %{local: false, nickname: "u@peer2.com"})
2817 Pleroma.Stats.force_update()
2819 conn = get(conn, "/api/v1/instance/peers")
2821 assert result = json_response(conn, 200)
2823 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2826 test "put settings", %{conn: conn} do
2827 user = insert(:user)
2831 |> assign(:user, user)
2832 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2834 assert _result = json_response(conn, 200)
2836 user = User.get_cached_by_ap_id(user.ap_id)
2837 assert user.info.settings == %{"programming" => "socks"}
2840 describe "pinned statuses" do
2842 user = insert(:user)
2843 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2845 [user: user, activity: activity]
2848 clear_config([:instance, :max_pinned_statuses]) do
2849 Config.put([:instance, :max_pinned_statuses], 1)
2852 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2853 {:ok, _} = CommonAPI.pin(activity.id, user)
2857 |> assign(:user, user)
2858 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2859 |> json_response(200)
2861 id_str = to_string(activity.id)
2863 assert [%{"id" => ^id_str, "pinned" => true}] = result
2866 test "pin status", %{conn: conn, user: user, activity: activity} do
2867 id_str = to_string(activity.id)
2869 assert %{"id" => ^id_str, "pinned" => true} =
2871 |> assign(:user, user)
2872 |> post("/api/v1/statuses/#{activity.id}/pin")
2873 |> json_response(200)
2875 assert [%{"id" => ^id_str, "pinned" => true}] =
2877 |> assign(:user, user)
2878 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2879 |> json_response(200)
2882 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2883 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2887 |> assign(:user, user)
2888 |> post("/api/v1/statuses/#{dm.id}/pin")
2890 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2893 test "unpin status", %{conn: conn, user: user, activity: activity} do
2894 {:ok, _} = CommonAPI.pin(activity.id, user)
2896 id_str = to_string(activity.id)
2897 user = refresh_record(user)
2899 assert %{"id" => ^id_str, "pinned" => false} =
2901 |> assign(:user, user)
2902 |> post("/api/v1/statuses/#{activity.id}/unpin")
2903 |> json_response(200)
2907 |> assign(:user, user)
2908 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2909 |> json_response(200)
2912 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2915 |> assign(:user, user)
2916 |> post("/api/v1/statuses/1/unpin")
2918 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2921 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2922 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2924 id_str_one = to_string(activity_one.id)
2926 assert %{"id" => ^id_str_one, "pinned" => true} =
2928 |> assign(:user, user)
2929 |> post("/api/v1/statuses/#{id_str_one}/pin")
2930 |> json_response(200)
2932 user = refresh_record(user)
2934 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2936 |> assign(:user, user)
2937 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2938 |> json_response(400)
2944 Config.put([:rich_media, :enabled], true)
2946 user = insert(:user)
2950 test "returns rich-media card", %{conn: conn, user: user} do
2951 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2954 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2955 "provider_name" => "example.com",
2956 "provider_url" => "https://example.com",
2957 "title" => "The Rock",
2959 "url" => "https://example.com/ogp",
2961 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2964 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2965 "title" => "The Rock",
2966 "type" => "video.movie",
2967 "url" => "https://example.com/ogp",
2969 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2976 |> get("/api/v1/statuses/#{activity.id}/card")
2977 |> json_response(200)
2979 assert response == card_data
2981 # works with private posts
2983 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2987 |> assign(:user, user)
2988 |> get("/api/v1/statuses/#{activity.id}/card")
2989 |> json_response(200)
2991 assert response_two == card_data
2994 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2996 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
3000 |> get("/api/v1/statuses/#{activity.id}/card")
3001 |> json_response(:ok)
3003 assert response == %{
3005 "title" => "Pleroma",
3006 "description" => "",
3008 "provider_name" => "example.com",
3009 "provider_url" => "https://example.com",
3010 "url" => "https://example.com/ogp-missing-data",
3013 "title" => "Pleroma",
3014 "type" => "website",
3015 "url" => "https://example.com/ogp-missing-data"
3023 user = insert(:user)
3024 for_user = insert(:user)
3027 CommonAPI.post(user, %{
3028 "status" => "heweoo?"
3032 CommonAPI.post(user, %{
3033 "status" => "heweoo!"
3038 |> assign(:user, for_user)
3039 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
3041 assert json_response(response1, 200)["bookmarked"] == true
3045 |> assign(:user, for_user)
3046 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
3048 assert json_response(response2, 200)["bookmarked"] == true
3052 |> assign(:user, for_user)
3053 |> get("/api/v1/bookmarks")
3055 assert [json_response(response2, 200), json_response(response1, 200)] ==
3056 json_response(bookmarks, 200)
3060 |> assign(:user, for_user)
3061 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
3063 assert json_response(response1, 200)["bookmarked"] == false
3067 |> assign(:user, for_user)
3068 |> get("/api/v1/bookmarks")
3070 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
3073 describe "conversation muting" do
3075 post_user = insert(:user)
3076 user = insert(:user)
3078 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
3080 [user: user, activity: activity]
3083 test "mute conversation", %{conn: conn, user: user, activity: activity} do
3084 id_str = to_string(activity.id)
3086 assert %{"id" => ^id_str, "muted" => true} =
3088 |> assign(:user, user)
3089 |> post("/api/v1/statuses/#{activity.id}/mute")
3090 |> json_response(200)
3093 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
3094 {:ok, _} = CommonAPI.add_mute(user, activity)
3098 |> assign(:user, user)
3099 |> post("/api/v1/statuses/#{activity.id}/mute")
3101 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
3104 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
3105 {:ok, _} = CommonAPI.add_mute(user, activity)
3107 id_str = to_string(activity.id)
3108 user = refresh_record(user)
3110 assert %{"id" => ^id_str, "muted" => false} =
3112 |> assign(:user, user)
3113 |> post("/api/v1/statuses/#{activity.id}/unmute")
3114 |> json_response(200)
3118 describe "reports" do
3120 reporter = insert(:user)
3121 target_user = insert(:user)
3123 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
3125 [reporter: reporter, target_user: target_user, activity: activity]
3128 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
3129 assert %{"action_taken" => false, "id" => _} =
3131 |> assign(:user, reporter)
3132 |> post("/api/v1/reports", %{"account_id" => target_user.id})
3133 |> json_response(200)
3136 test "submit a report with statuses and comment", %{
3139 target_user: target_user,
3142 assert %{"action_taken" => false, "id" => _} =
3144 |> assign(:user, reporter)
3145 |> post("/api/v1/reports", %{
3146 "account_id" => target_user.id,
3147 "status_ids" => [activity.id],
3148 "comment" => "bad status!",
3149 "forward" => "false"
3151 |> json_response(200)
3154 test "account_id is required", %{
3159 assert %{"error" => "Valid `account_id` required"} =
3161 |> assign(:user, reporter)
3162 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3163 |> json_response(400)
3166 test "comment must be up to the size specified in the config", %{
3169 target_user: target_user
3171 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3172 comment = String.pad_trailing("a", max_size + 1, "a")
3174 error = %{"error" => "Comment must be up to #{max_size} characters"}
3178 |> assign(:user, reporter)
3179 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3180 |> json_response(400)
3183 test "returns error when account is not exist", %{
3190 |> assign(:user, reporter)
3191 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3193 assert json_response(conn, 400) == %{"error" => "Account not found"}
3197 describe "link headers" do
3198 test "preserves parameters in link headers", %{conn: conn} do
3199 user = insert(:user)
3200 other_user = insert(:user)
3203 CommonAPI.post(other_user, %{
3204 "status" => "hi @#{user.nickname}",
3205 "visibility" => "public"
3209 CommonAPI.post(other_user, %{
3210 "status" => "hi @#{user.nickname}",
3211 "visibility" => "public"
3214 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3215 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3219 |> assign(:user, user)
3220 |> get("/api/v1/notifications", %{media_only: true})
3222 assert [link_header] = get_resp_header(conn, "link")
3223 assert link_header =~ ~r/media_only=true/
3224 assert link_header =~ ~r/min_id=#{notification2.id}/
3225 assert link_header =~ ~r/max_id=#{notification1.id}/
3229 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3230 # Need to set an old-style integer ID to reproduce the problem
3231 # (these are no longer assigned to new accounts but were preserved
3232 # for existing accounts during the migration to flakeIDs)
3233 user_one = insert(:user, %{id: 1212})
3234 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3238 |> get("/api/v1/accounts/#{user_one.id}")
3242 |> get("/api/v1/accounts/#{user_two.nickname}")
3246 |> get("/api/v1/accounts/#{user_two.id}")
3248 acc_one = json_response(resp_one, 200)
3249 acc_two = json_response(resp_two, 200)
3250 acc_three = json_response(resp_three, 200)
3251 refute acc_one == acc_two
3252 assert acc_two == acc_three
3255 describe "custom emoji" do
3256 test "with tags", %{conn: conn} do
3259 |> get("/api/v1/custom_emojis")
3260 |> json_response(200)
3262 assert Map.has_key?(emoji, "shortcode")
3263 assert Map.has_key?(emoji, "static_url")
3264 assert Map.has_key?(emoji, "tags")
3265 assert is_list(emoji["tags"])
3266 assert Map.has_key?(emoji, "category")
3267 assert Map.has_key?(emoji, "url")
3268 assert Map.has_key?(emoji, "visible_in_picker")
3272 describe "index/2 redirections" do
3273 setup %{conn: conn} do
3277 signing_salt: "cooldude"
3282 |> Plug.Session.call(Plug.Session.init(session_opts))
3285 test_path = "/web/statuses/test"
3286 %{conn: conn, path: test_path}
3289 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3290 conn = get(conn, path)
3292 assert conn.status == 302
3293 assert redirected_to(conn) == "/web/login"
3296 test "redirects not logged-in users to the login page on private instances", %{
3300 Config.put([:instance, :public], false)
3302 conn = get(conn, path)
3304 assert conn.status == 302
3305 assert redirected_to(conn) == "/web/login"
3308 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3309 token = insert(:oauth_token)
3313 |> assign(:user, token.user)
3314 |> put_session(:oauth_token, token.token)
3317 assert conn.status == 200
3320 test "saves referer path to session", %{conn: conn, path: path} do
3321 conn = get(conn, path)
3322 return_to = Plug.Conn.get_session(conn, :return_to)
3324 assert return_to == path
3327 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3328 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3329 auth = insert(:oauth_authorization, app: app)
3333 |> put_session(:return_to, path)
3334 |> get("/web/login", %{code: auth.token})
3336 assert conn.status == 302
3337 assert redirected_to(conn) == path
3340 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3341 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3342 auth = insert(:oauth_authorization, app: app)
3344 conn = get(conn, "/web/login", %{code: auth.token})
3346 assert conn.status == 302
3347 assert redirected_to(conn) == "/web/getting-started"
3351 describe "scheduled activities" do
3352 test "creates a scheduled activity", %{conn: conn} do
3353 user = insert(:user)
3354 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3358 |> assign(:user, user)
3359 |> post("/api/v1/statuses", %{
3360 "status" => "scheduled",
3361 "scheduled_at" => scheduled_at
3364 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3365 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3366 assert [] == Repo.all(Activity)
3369 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3370 user = insert(:user)
3371 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3373 file = %Plug.Upload{
3374 content_type: "image/jpg",
3375 path: Path.absname("test/fixtures/image.jpg"),
3376 filename: "an_image.jpg"
3379 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3383 |> assign(:user, user)
3384 |> post("/api/v1/statuses", %{
3385 "media_ids" => [to_string(upload.id)],
3386 "status" => "scheduled",
3387 "scheduled_at" => scheduled_at
3390 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3391 assert %{"type" => "image"} = media_attachment
3394 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3396 user = insert(:user)
3399 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3403 |> assign(:user, user)
3404 |> post("/api/v1/statuses", %{
3405 "status" => "not scheduled",
3406 "scheduled_at" => scheduled_at
3409 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3410 assert [] == Repo.all(ScheduledActivity)
3413 test "returns error when daily user limit is exceeded", %{conn: conn} do
3414 user = insert(:user)
3417 NaiveDateTime.utc_now()
3418 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3419 |> NaiveDateTime.to_iso8601()
3421 attrs = %{params: %{}, scheduled_at: today}
3422 {:ok, _} = ScheduledActivity.create(user, attrs)
3423 {:ok, _} = ScheduledActivity.create(user, attrs)
3427 |> assign(:user, user)
3428 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3430 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3433 test "returns error when total user limit is exceeded", %{conn: conn} do
3434 user = insert(:user)
3437 NaiveDateTime.utc_now()
3438 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3439 |> NaiveDateTime.to_iso8601()
3442 NaiveDateTime.utc_now()
3443 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3444 |> NaiveDateTime.to_iso8601()
3446 attrs = %{params: %{}, scheduled_at: today}
3447 {:ok, _} = ScheduledActivity.create(user, attrs)
3448 {:ok, _} = ScheduledActivity.create(user, attrs)
3449 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3453 |> assign(:user, user)
3454 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3456 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3459 test "shows scheduled activities", %{conn: conn} do
3460 user = insert(:user)
3461 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3462 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3463 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3464 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3468 |> assign(:user, user)
3473 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3475 result = json_response(conn_res, 200)
3476 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3481 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3483 result = json_response(conn_res, 200)
3484 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3489 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3491 result = json_response(conn_res, 200)
3492 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3495 test "shows a scheduled activity", %{conn: conn} do
3496 user = insert(:user)
3497 scheduled_activity = insert(:scheduled_activity, user: user)
3501 |> assign(:user, user)
3502 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3504 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3505 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3509 |> assign(:user, user)
3510 |> get("/api/v1/scheduled_statuses/404")
3512 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3515 test "updates a scheduled activity", %{conn: conn} do
3516 user = insert(:user)
3517 scheduled_activity = insert(:scheduled_activity, user: user)
3520 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3524 |> assign(:user, user)
3525 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3526 scheduled_at: new_scheduled_at
3529 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3530 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3534 |> assign(:user, user)
3535 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3537 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3540 test "deletes a scheduled activity", %{conn: conn} do
3541 user = insert(:user)
3542 scheduled_activity = insert(:scheduled_activity, user: user)
3546 |> assign(:user, user)
3547 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3549 assert %{} = json_response(res_conn, 200)
3550 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3554 |> assign(:user, user)
3555 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3557 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3561 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3562 user1 = insert(:user)
3563 user2 = insert(:user)
3564 user3 = insert(:user)
3566 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3568 # Reply to status from another user
3571 |> assign(:user, user2)
3572 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3574 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3576 activity = Activity.get_by_id_with_object(id)
3578 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3579 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3581 # Reblog from the third user
3584 |> assign(:user, user3)
3585 |> post("/api/v1/statuses/#{activity.id}/reblog")
3587 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3588 json_response(conn2, 200)
3590 assert to_string(activity.id) == id
3592 # Getting third user status
3595 |> assign(:user, user3)
3596 |> get("api/v1/timelines/home")
3598 [reblogged_activity] = json_response(conn3, 200)
3600 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3602 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3603 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3606 describe "create account by app" do
3607 test "Account registration via Application", %{conn: conn} do
3610 |> post("/api/v1/apps", %{
3611 client_name: "client_name",
3612 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3613 scopes: "read, write, follow"
3617 "client_id" => client_id,
3618 "client_secret" => client_secret,
3620 "name" => "client_name",
3621 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3624 } = json_response(conn, 200)
3628 |> post("/oauth/token", %{
3629 grant_type: "client_credentials",
3630 client_id: client_id,
3631 client_secret: client_secret
3634 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3635 json_response(conn, 200)
3638 token_from_db = Repo.get_by(Token, token: token)
3639 assert token_from_db
3641 assert scope == "read write follow"
3645 |> put_req_header("authorization", "Bearer " <> token)
3646 |> post("/api/v1/accounts", %{
3648 email: "lain@example.org",
3649 password: "PlzDontHackLain",
3654 "access_token" => token,
3655 "created_at" => _created_at,
3657 "token_type" => "Bearer"
3658 } = json_response(conn, 200)
3660 token_from_db = Repo.get_by(Token, token: token)
3661 assert token_from_db
3662 token_from_db = Repo.preload(token_from_db, :user)
3663 assert token_from_db.user
3665 assert token_from_db.user.info.confirmation_pending
3668 test "rate limit", %{conn: conn} do
3669 app_token = insert(:oauth_token, user: nil)
3672 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3673 |> Map.put(:remote_ip, {15, 15, 15, 15})
3678 |> post("/api/v1/accounts", %{
3679 username: "#{i}lain",
3680 email: "#{i}lain@example.org",
3681 password: "PlzDontHackLain",
3686 "access_token" => token,
3687 "created_at" => _created_at,
3689 "token_type" => "Bearer"
3690 } = json_response(conn, 200)
3692 token_from_db = Repo.get_by(Token, token: token)
3693 assert token_from_db
3694 token_from_db = Repo.preload(token_from_db, :user)
3695 assert token_from_db.user
3697 assert token_from_db.user.info.confirmation_pending
3702 |> post("/api/v1/accounts", %{
3704 email: "6lain@example.org",
3705 password: "PlzDontHackLain",
3709 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3713 describe "GET /api/v1/polls/:id" do
3714 test "returns poll entity for object id", %{conn: conn} do
3715 user = insert(:user)
3718 CommonAPI.post(user, %{
3719 "status" => "Pleroma does",
3720 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3723 object = Object.normalize(activity)
3727 |> assign(:user, user)
3728 |> get("/api/v1/polls/#{object.id}")
3730 response = json_response(conn, 200)
3731 id = to_string(object.id)
3732 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3735 test "does not expose polls for private statuses", %{conn: conn} do
3736 user = insert(:user)
3737 other_user = insert(:user)
3740 CommonAPI.post(user, %{
3741 "status" => "Pleroma does",
3742 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3743 "visibility" => "private"
3746 object = Object.normalize(activity)
3750 |> assign(:user, other_user)
3751 |> get("/api/v1/polls/#{object.id}")
3753 assert json_response(conn, 404)
3757 describe "POST /api/v1/polls/:id/votes" do
3758 test "votes are added to the poll", %{conn: conn} do
3759 user = insert(:user)
3760 other_user = insert(:user)
3763 CommonAPI.post(user, %{
3764 "status" => "A very delicious sandwich",
3766 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3772 object = Object.normalize(activity)
3776 |> assign(:user, other_user)
3777 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3779 assert json_response(conn, 200)
3780 object = Object.get_by_id(object.id)
3782 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3787 test "author can't vote", %{conn: conn} do
3788 user = insert(:user)
3791 CommonAPI.post(user, %{
3792 "status" => "Am I cute?",
3793 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3796 object = Object.normalize(activity)
3799 |> assign(:user, user)
3800 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3801 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3803 object = Object.get_by_id(object.id)
3805 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3808 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3809 user = insert(:user)
3810 other_user = insert(:user)
3813 CommonAPI.post(user, %{
3814 "status" => "The glass is",
3815 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3818 object = Object.normalize(activity)
3821 |> assign(:user, other_user)
3822 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3823 |> json_response(422) == %{"error" => "Too many choices"}
3825 object = Object.get_by_id(object.id)
3827 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3832 test "does not allow choice index to be greater than options count", %{conn: conn} do
3833 user = insert(:user)
3834 other_user = insert(:user)
3837 CommonAPI.post(user, %{
3838 "status" => "Am I cute?",
3839 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3842 object = Object.normalize(activity)
3846 |> assign(:user, other_user)
3847 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3849 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3852 test "returns 404 error when object is not exist", %{conn: conn} do
3853 user = insert(:user)
3857 |> assign(:user, user)
3858 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3860 assert json_response(conn, 404) == %{"error" => "Record not found"}
3863 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3864 user = insert(:user)
3865 other_user = insert(:user)
3868 CommonAPI.post(user, %{
3869 "status" => "Am I cute?",
3870 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3871 "visibility" => "private"
3874 object = Object.normalize(activity)
3878 |> assign(:user, other_user)
3879 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3881 assert json_response(conn, 404) == %{"error" => "Record not found"}
3885 describe "GET /api/v1/statuses/:id/favourited_by" do
3887 user = insert(:user)
3888 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3892 |> assign(:user, user)
3894 [conn: conn, activity: activity, user: user]
3897 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3898 other_user = insert(:user)
3899 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3903 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3904 |> json_response(:ok)
3906 [%{"id" => id}] = response
3908 assert id == other_user.id
3911 test "returns empty array when status has not been favorited yet", %{
3917 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3918 |> json_response(:ok)
3920 assert Enum.empty?(response)
3923 test "does not return users who have favorited the status but are blocked", %{
3924 conn: %{assigns: %{user: user}} = conn,
3927 other_user = insert(:user)
3928 {:ok, user} = User.block(user, other_user)
3930 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3934 |> assign(:user, user)
3935 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3936 |> json_response(:ok)
3938 assert Enum.empty?(response)
3941 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3942 other_user = insert(:user)
3943 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3947 |> assign(:user, nil)
3948 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3949 |> json_response(:ok)
3951 [%{"id" => id}] = response
3952 assert id == other_user.id
3955 test "requires authentification for private posts", %{conn: conn, user: user} do
3956 other_user = insert(:user)
3959 CommonAPI.post(user, %{
3960 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3961 "visibility" => "direct"
3964 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3967 |> assign(:user, nil)
3968 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3969 |> json_response(404)
3973 |> assign(:user, other_user)
3974 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3975 |> json_response(200)
3977 [%{"id" => id}] = response
3978 assert id == other_user.id
3982 describe "GET /api/v1/statuses/:id/reblogged_by" do
3984 user = insert(:user)
3985 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3989 |> assign(:user, user)
3991 [conn: conn, activity: activity, user: user]
3994 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3995 other_user = insert(:user)
3996 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
4000 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4001 |> json_response(:ok)
4003 [%{"id" => id}] = response
4005 assert id == other_user.id
4008 test "returns empty array when status has not been reblogged yet", %{
4014 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4015 |> json_response(:ok)
4017 assert Enum.empty?(response)
4020 test "does not return users who have reblogged the status but are blocked", %{
4021 conn: %{assigns: %{user: user}} = conn,
4024 other_user = insert(:user)
4025 {:ok, user} = User.block(user, other_user)
4027 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
4031 |> assign(:user, user)
4032 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4033 |> json_response(:ok)
4035 assert Enum.empty?(response)
4038 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
4039 other_user = insert(:user)
4040 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
4044 |> assign(:user, nil)
4045 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4046 |> json_response(:ok)
4048 [%{"id" => id}] = response
4049 assert id == other_user.id
4052 test "requires authentification for private posts", %{conn: conn, user: user} do
4053 other_user = insert(:user)
4056 CommonAPI.post(user, %{
4057 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
4058 "visibility" => "direct"
4062 |> assign(:user, nil)
4063 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4064 |> json_response(404)
4068 |> assign(:user, other_user)
4069 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4070 |> json_response(200)
4072 assert [] == response
4076 describe "POST /auth/password, with valid parameters" do
4077 setup %{conn: conn} do
4078 user = insert(:user)
4079 conn = post(conn, "/auth/password?email=#{user.email}")
4080 %{conn: conn, user: user}
4083 test "it returns 204", %{conn: conn} do
4084 assert json_response(conn, :no_content)
4087 test "it creates a PasswordResetToken record for user", %{user: user} do
4088 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
4092 test "it sends an email to user", %{user: user} do
4093 ObanHelpers.perform_all()
4094 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
4096 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
4097 notify_email = Config.get([:instance, :notify_email])
4098 instance_name = Config.get([:instance, :name])
4101 from: {instance_name, notify_email},
4102 to: {user.name, user.email},
4103 html_body: email.html_body
4108 describe "POST /auth/password, with invalid parameters" do
4110 user = insert(:user)
4114 test "it returns 404 when user is not found", %{conn: conn, user: user} do
4115 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
4116 assert conn.status == 404
4117 assert conn.resp_body == ""
4120 test "it returns 400 when user is not local", %{conn: conn, user: user} do
4121 {:ok, user} = Repo.update(Changeset.change(user, local: false))
4122 conn = post(conn, "/auth/password?email=#{user.email}")
4123 assert conn.status == 400
4124 assert conn.resp_body == ""
4128 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
4130 user = insert(:user)
4131 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
4135 |> Changeset.change()
4136 |> Changeset.put_embed(:info, info_change)
4139 assert user.info.confirmation_pending
4144 clear_config([:instance, :account_activation_required]) do
4145 Config.put([:instance, :account_activation_required], true)
4148 test "resend account confirmation email", %{conn: conn, user: user} do
4150 |> assign(:user, user)
4151 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
4152 |> json_response(:no_content)
4154 ObanHelpers.perform_all()
4156 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
4157 notify_email = Config.get([:instance, :notify_email])
4158 instance_name = Config.get([:instance, :name])
4161 from: {instance_name, notify_email},
4162 to: {user.name, user.email},
4163 html_body: email.html_body
4168 describe "GET /api/v1/suggestions" do
4170 user = insert(:user)
4171 other_user = insert(:user)
4172 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
4173 url500 = "http://test500?#{host}&#{user.nickname}"
4174 url200 = "http://test200?#{host}&#{user.nickname}"
4177 %{method: :get, url: ^url500} ->
4178 %Tesla.Env{status: 500, body: "bad request"}
4180 %{method: :get, url: ^url200} ->
4184 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4186 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4190 [user: user, other_user: other_user]
4193 clear_config(:suggestions)
4195 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4196 Config.put([:suggestions, :enabled], false)
4200 |> assign(:user, user)
4201 |> get("/api/v1/suggestions")
4202 |> json_response(200)
4207 test "returns error", %{conn: conn, user: user} do
4208 Config.put([:suggestions, :enabled], true)
4209 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4211 assert capture_log(fn ->
4214 |> assign(:user, user)
4215 |> get("/api/v1/suggestions")
4216 |> json_response(500)
4218 assert res == "Something went wrong"
4219 end) =~ "Could not retrieve suggestions"
4222 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4223 Config.put([:suggestions, :enabled], true)
4224 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4228 |> assign(:user, user)
4229 |> get("/api/v1/suggestions")
4230 |> json_response(200)
4235 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4236 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4240 "acct" => other_user.ap_id,
4241 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4242 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4243 "id" => other_user.id