1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.ActivityExpiration
12 alias Pleroma.Notification
15 alias Pleroma.ScheduledActivity
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.MastodonAPI.FilterView
20 alias Pleroma.Web.OAuth.App
21 alias Pleroma.Web.OAuth.Token
22 alias Pleroma.Web.OStatus
23 alias Pleroma.Web.Push
24 import Pleroma.Factory
25 import ExUnit.CaptureLog
27 import Swoosh.TestAssertions
29 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
32 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
36 clear_config([:instance, :public])
37 clear_config([:rich_media, :enabled])
39 test "the home timeline", %{conn: conn} do
41 following = insert(:user)
43 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
47 |> assign(:user, user)
48 |> get("/api/v1/timelines/home")
50 assert Enum.empty?(json_response(conn, 200))
52 {:ok, user} = User.follow(user, following)
56 |> assign(:user, user)
57 |> get("/api/v1/timelines/home")
59 assert [%{"content" => "test"}] = json_response(conn, 200)
62 test "the public timeline", %{conn: conn} do
63 following = insert(:user)
66 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
69 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
73 |> get("/api/v1/timelines/public", %{"local" => "False"})
75 assert length(json_response(conn, 200)) == 2
79 |> get("/api/v1/timelines/public", %{"local" => "True"})
81 assert [%{"content" => "test"}] = json_response(conn, 200)
85 |> get("/api/v1/timelines/public", %{"local" => "1"})
87 assert [%{"content" => "test"}] = json_response(conn, 200)
91 test "the public timeline when public is set to false", %{conn: conn} do
92 Config.put([:instance, :public], false)
95 |> get("/api/v1/timelines/public", %{"local" => "False"})
96 |> json_response(403) == %{"error" => "This resource requires authentication."}
99 describe "posting statuses" do
105 |> assign(:user, user)
110 test "posting a status", %{conn: conn} do
111 idempotency_key = "Pikachu rocks!"
115 |> put_req_header("idempotency-key", idempotency_key)
116 |> post("/api/v1/statuses", %{
118 "spoiler_text" => "2hu",
119 "sensitive" => "false"
122 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
124 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
126 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
127 json_response(conn_one, 200)
129 assert Activity.get_by_id(id)
133 |> put_req_header("idempotency-key", idempotency_key)
134 |> post("/api/v1/statuses", %{
136 "spoiler_text" => "2hu",
137 "sensitive" => "false"
140 assert %{"id" => second_id} = json_response(conn_two, 200)
141 assert id == second_id
145 |> post("/api/v1/statuses", %{
147 "spoiler_text" => "2hu",
148 "sensitive" => "false"
151 assert %{"id" => third_id} = json_response(conn_three, 200)
152 refute id == third_id
154 # An activity that will expire:
156 expires_in = 120 * 60
160 |> post("api/v1/statuses", %{
161 "status" => "oolong",
162 "expires_in" => expires_in
165 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
166 assert activity = Activity.get_by_id(fourth_id)
167 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
169 estimated_expires_at =
170 NaiveDateTime.utc_now()
171 |> NaiveDateTime.add(expires_in)
172 |> NaiveDateTime.truncate(:second)
174 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
175 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
177 assert fourth_response["pleroma"]["expires_at"] ==
178 NaiveDateTime.to_iso8601(expiration.scheduled_at)
181 test "replying to a status", %{conn: conn} do
183 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
187 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
189 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
191 activity = Activity.get_by_id(id)
193 assert activity.data["context"] == replied_to.data["context"]
194 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
197 test "replying to a direct message with visibility other than direct", %{conn: conn} do
199 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
201 Enum.each(["public", "private", "unlisted"], fn visibility ->
204 |> post("/api/v1/statuses", %{
205 "status" => "@#{user.nickname} hey",
206 "in_reply_to_id" => replied_to.id,
207 "visibility" => visibility
210 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
214 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
217 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
219 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
220 assert Activity.get_by_id(id)
223 test "posting a sensitive status", %{conn: conn} do
226 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
228 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
229 assert Activity.get_by_id(id)
232 test "posting a fake status", %{conn: conn} do
235 |> post("/api/v1/statuses", %{
237 "\"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"
240 real_status = json_response(real_conn, 200)
243 assert Object.get_by_ap_id(real_status["uri"])
247 |> Map.put("id", nil)
248 |> Map.put("url", nil)
249 |> Map.put("uri", nil)
250 |> Map.put("created_at", nil)
251 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
255 |> post("/api/v1/statuses", %{
257 "\"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",
261 fake_status = json_response(fake_conn, 200)
264 refute Object.get_by_ap_id(fake_status["uri"])
268 |> Map.put("id", nil)
269 |> Map.put("url", nil)
270 |> Map.put("uri", nil)
271 |> Map.put("created_at", nil)
272 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
274 assert real_status == fake_status
277 test "posting a status with OGP link preview", %{conn: conn} do
278 Config.put([:rich_media, :enabled], true)
282 |> post("/api/v1/statuses", %{
283 "status" => "https://example.com/ogp"
286 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
287 assert Activity.get_by_id(id)
290 test "posting a direct status", %{conn: conn} do
291 user2 = insert(:user)
292 content = "direct cofe @#{user2.nickname}"
296 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
298 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
299 assert activity = Activity.get_by_id(id)
300 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
301 assert activity.data["to"] == [user2.ap_id]
302 assert activity.data["cc"] == []
306 describe "posting polls" do
307 test "posting a poll", %{conn: conn} do
309 time = NaiveDateTime.utc_now()
313 |> assign(:user, user)
314 |> post("/api/v1/statuses", %{
315 "status" => "Who is the #bestgrill?",
316 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
319 response = json_response(conn, 200)
321 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
322 title in ["Rei", "Asuka", "Misato"]
325 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
326 refute response["poll"]["expred"]
329 test "option limit is enforced", %{conn: conn} do
331 limit = Config.get([:instance, :poll_limits, :max_options])
335 |> assign(:user, user)
336 |> post("/api/v1/statuses", %{
338 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
341 %{"error" => error} = json_response(conn, 422)
342 assert error == "Poll can't contain more than #{limit} options"
345 test "option character limit is enforced", %{conn: conn} do
347 limit = Config.get([:instance, :poll_limits, :max_option_chars])
351 |> assign(:user, user)
352 |> post("/api/v1/statuses", %{
355 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
360 %{"error" => error} = json_response(conn, 422)
361 assert error == "Poll options cannot be longer than #{limit} characters each"
364 test "minimal date limit is enforced", %{conn: conn} do
366 limit = Config.get([:instance, :poll_limits, :min_expiration])
370 |> assign(:user, user)
371 |> post("/api/v1/statuses", %{
372 "status" => "imagine arbitrary limits",
374 "options" => ["this post was made by pleroma gang"],
375 "expires_in" => limit - 1
379 %{"error" => error} = json_response(conn, 422)
380 assert error == "Expiration date is too soon"
383 test "maximum date limit is enforced", %{conn: conn} do
385 limit = Config.get([:instance, :poll_limits, :max_expiration])
389 |> assign(:user, user)
390 |> post("/api/v1/statuses", %{
391 "status" => "imagine arbitrary limits",
393 "options" => ["this post was made by pleroma gang"],
394 "expires_in" => limit + 1
398 %{"error" => error} = json_response(conn, 422)
399 assert error == "Expiration date is too far in the future"
403 test "direct timeline", %{conn: conn} do
404 user_one = insert(:user)
405 user_two = insert(:user)
407 {:ok, user_two} = User.follow(user_two, user_one)
410 CommonAPI.post(user_one, %{
411 "status" => "Hi @#{user_two.nickname}!",
412 "visibility" => "direct"
415 {:ok, _follower_only} =
416 CommonAPI.post(user_one, %{
417 "status" => "Hi @#{user_two.nickname}!",
418 "visibility" => "private"
421 # Only direct should be visible here
424 |> assign(:user, user_two)
425 |> get("api/v1/timelines/direct")
427 [status] = json_response(res_conn, 200)
429 assert %{"visibility" => "direct"} = status
430 assert status["url"] != direct.data["id"]
432 # User should be able to see their own direct message
435 |> assign(:user, user_one)
436 |> get("api/v1/timelines/direct")
438 [status] = json_response(res_conn, 200)
440 assert %{"visibility" => "direct"} = status
442 # Both should be visible here
445 |> assign(:user, user_two)
446 |> get("api/v1/timelines/home")
448 [_s1, _s2] = json_response(res_conn, 200)
451 Enum.each(1..20, fn _ ->
453 CommonAPI.post(user_one, %{
454 "status" => "Hi @#{user_two.nickname}!",
455 "visibility" => "direct"
461 |> assign(:user, user_two)
462 |> get("api/v1/timelines/direct")
464 statuses = json_response(res_conn, 200)
465 assert length(statuses) == 20
469 |> assign(:user, user_two)
470 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
472 [status] = json_response(res_conn, 200)
474 assert status["url"] != direct.data["id"]
477 test "Conversations", %{conn: conn} do
478 user_one = insert(:user)
479 user_two = insert(:user)
480 user_three = insert(:user)
482 {:ok, user_two} = User.follow(user_two, user_one)
485 CommonAPI.post(user_one, %{
486 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
487 "visibility" => "direct"
490 {:ok, _follower_only} =
491 CommonAPI.post(user_one, %{
492 "status" => "Hi @#{user_two.nickname}!",
493 "visibility" => "private"
498 |> assign(:user, user_one)
499 |> get("/api/v1/conversations")
501 assert response = json_response(res_conn, 200)
506 "accounts" => res_accounts,
507 "last_status" => res_last_status,
512 account_ids = Enum.map(res_accounts, & &1["id"])
513 assert length(res_accounts) == 2
514 assert user_two.id in account_ids
515 assert user_three.id in account_ids
516 assert is_binary(res_id)
517 assert unread == true
518 assert res_last_status["id"] == direct.id
520 # Apparently undocumented API endpoint
523 |> assign(:user, user_one)
524 |> post("/api/v1/conversations/#{res_id}/read")
526 assert response = json_response(res_conn, 200)
527 assert length(response["accounts"]) == 2
528 assert response["last_status"]["id"] == direct.id
529 assert response["unread"] == false
531 # (vanilla) Mastodon frontend behaviour
534 |> assign(:user, user_one)
535 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
537 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
540 test "doesn't include DMs from blocked users", %{conn: conn} do
541 blocker = insert(:user)
542 blocked = insert(:user)
544 {:ok, blocker} = User.block(blocker, blocked)
546 {:ok, _blocked_direct} =
547 CommonAPI.post(blocked, %{
548 "status" => "Hi @#{blocker.nickname}!",
549 "visibility" => "direct"
553 CommonAPI.post(user, %{
554 "status" => "Hi @#{blocker.nickname}!",
555 "visibility" => "direct"
560 |> assign(:user, user)
561 |> get("api/v1/timelines/direct")
563 [status] = json_response(res_conn, 200)
564 assert status["id"] == direct.id
567 test "verify_credentials", %{conn: conn} do
572 |> assign(:user, user)
573 |> get("/api/v1/accounts/verify_credentials")
575 response = json_response(conn, 200)
577 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
578 assert response["pleroma"]["chat_token"]
579 assert id == to_string(user.id)
582 test "verify_credentials default scope unlisted", %{conn: conn} do
583 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
587 |> assign(:user, user)
588 |> get("/api/v1/accounts/verify_credentials")
590 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
591 assert id == to_string(user.id)
594 test "apps/verify_credentials", %{conn: conn} do
595 token = insert(:oauth_token)
599 |> assign(:user, token.user)
600 |> assign(:token, token)
601 |> get("/api/v1/apps/verify_credentials")
603 app = Repo.preload(token, :app).app
606 "name" => app.client_name,
607 "website" => app.website,
608 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
611 assert expected == json_response(conn, 200)
614 test "user avatar can be set", %{conn: conn} do
616 avatar_image = File.read!("test/fixtures/avatar_data_uri")
620 |> assign(:user, user)
621 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
623 user = refresh_record(user)
637 assert %{"url" => _} = json_response(conn, 200)
640 test "user avatar can be reset", %{conn: conn} do
645 |> assign(:user, user)
646 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
648 user = User.get_cached_by_id(user.id)
650 assert user.avatar == nil
652 assert %{"url" => nil} = json_response(conn, 200)
655 test "can set profile banner", %{conn: conn} do
660 |> assign(:user, user)
661 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
663 user = refresh_record(user)
664 assert user.info.banner["type"] == "Image"
666 assert %{"url" => _} = json_response(conn, 200)
669 test "can reset profile banner", %{conn: conn} do
674 |> assign(:user, user)
675 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
677 user = refresh_record(user)
678 assert user.info.banner == %{}
680 assert %{"url" => nil} = json_response(conn, 200)
683 test "background image can be set", %{conn: conn} do
688 |> assign(:user, user)
689 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
691 user = refresh_record(user)
692 assert user.info.background["type"] == "Image"
693 assert %{"url" => _} = json_response(conn, 200)
696 test "background image can be reset", %{conn: conn} do
701 |> assign(:user, user)
702 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
704 user = refresh_record(user)
705 assert user.info.background == %{}
706 assert %{"url" => nil} = json_response(conn, 200)
709 test "creates an oauth app", %{conn: conn} do
711 app_attrs = build(:oauth_app)
715 |> assign(:user, user)
716 |> post("/api/v1/apps", %{
717 client_name: app_attrs.client_name,
718 redirect_uris: app_attrs.redirect_uris
721 [app] = Repo.all(App)
724 "name" => app.client_name,
725 "website" => app.website,
726 "client_id" => app.client_id,
727 "client_secret" => app.client_secret,
728 "id" => app.id |> to_string(),
729 "redirect_uri" => app.redirect_uris,
730 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
733 assert expected == json_response(conn, 200)
736 test "get a status", %{conn: conn} do
737 activity = insert(:note_activity)
741 |> get("/api/v1/statuses/#{activity.id}")
743 assert %{"id" => id} = json_response(conn, 200)
744 assert id == to_string(activity.id)
747 test "get statuses by IDs", %{conn: conn} do
748 %{id: id1} = insert(:note_activity)
749 %{id: id2} = insert(:note_activity)
751 query_string = "ids[]=#{id1}&ids[]=#{id2}"
752 conn = get(conn, "/api/v1/statuses/?#{query_string}")
754 assert [%{"id" => ^id1}, %{"id" => ^id2}] = json_response(conn, :ok)
757 describe "deleting a status" do
758 test "when you created it", %{conn: conn} do
759 activity = insert(:note_activity)
760 author = User.get_cached_by_ap_id(activity.data["actor"])
764 |> assign(:user, author)
765 |> delete("/api/v1/statuses/#{activity.id}")
767 assert %{} = json_response(conn, 200)
769 refute Activity.get_by_id(activity.id)
772 test "when you didn't create it", %{conn: conn} do
773 activity = insert(:note_activity)
778 |> assign(:user, user)
779 |> delete("/api/v1/statuses/#{activity.id}")
781 assert %{"error" => _} = json_response(conn, 403)
783 assert Activity.get_by_id(activity.id) == activity
786 test "when you're an admin or moderator", %{conn: conn} do
787 activity1 = insert(:note_activity)
788 activity2 = insert(:note_activity)
789 admin = insert(:user, info: %{is_admin: true})
790 moderator = insert(:user, info: %{is_moderator: true})
794 |> assign(:user, admin)
795 |> delete("/api/v1/statuses/#{activity1.id}")
797 assert %{} = json_response(res_conn, 200)
801 |> assign(:user, moderator)
802 |> delete("/api/v1/statuses/#{activity2.id}")
804 assert %{} = json_response(res_conn, 200)
806 refute Activity.get_by_id(activity1.id)
807 refute Activity.get_by_id(activity2.id)
811 describe "filters" do
812 test "creating a filter", %{conn: conn} do
815 filter = %Pleroma.Filter{
822 |> assign(:user, user)
823 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
825 assert response = json_response(conn, 200)
826 assert response["phrase"] == filter.phrase
827 assert response["context"] == filter.context
828 assert response["irreversible"] == false
829 assert response["id"] != nil
830 assert response["id"] != ""
833 test "fetching a list of filters", %{conn: conn} do
836 query_one = %Pleroma.Filter{
843 query_two = %Pleroma.Filter{
850 {:ok, filter_one} = Pleroma.Filter.create(query_one)
851 {:ok, filter_two} = Pleroma.Filter.create(query_two)
855 |> assign(:user, user)
856 |> get("/api/v1/filters")
857 |> json_response(200)
863 filters: [filter_two, filter_one]
867 test "get a filter", %{conn: conn} do
870 query = %Pleroma.Filter{
877 {:ok, filter} = Pleroma.Filter.create(query)
881 |> assign(:user, user)
882 |> get("/api/v1/filters/#{filter.filter_id}")
884 assert _response = json_response(conn, 200)
887 test "update a filter", %{conn: conn} do
890 query = %Pleroma.Filter{
897 {:ok, _filter} = Pleroma.Filter.create(query)
899 new = %Pleroma.Filter{
906 |> assign(:user, user)
907 |> put("/api/v1/filters/#{query.filter_id}", %{
912 assert response = json_response(conn, 200)
913 assert response["phrase"] == new.phrase
914 assert response["context"] == new.context
917 test "delete a filter", %{conn: conn} do
920 query = %Pleroma.Filter{
927 {:ok, filter} = Pleroma.Filter.create(query)
931 |> assign(:user, user)
932 |> delete("/api/v1/filters/#{filter.filter_id}")
934 assert response = json_response(conn, 200)
935 assert response == %{}
939 describe "list timelines" do
940 test "list timeline", %{conn: conn} do
942 other_user = insert(:user)
943 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
944 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
945 {:ok, list} = Pleroma.List.create("name", user)
946 {:ok, list} = Pleroma.List.follow(list, other_user)
950 |> assign(:user, user)
951 |> get("/api/v1/timelines/list/#{list.id}")
953 assert [%{"id" => id}] = json_response(conn, 200)
955 assert id == to_string(activity_two.id)
958 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
960 other_user = insert(:user)
961 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
963 {:ok, _activity_two} =
964 CommonAPI.post(other_user, %{
965 "status" => "Marisa is cute.",
966 "visibility" => "private"
969 {:ok, list} = Pleroma.List.create("name", user)
970 {:ok, list} = Pleroma.List.follow(list, other_user)
974 |> assign(:user, user)
975 |> get("/api/v1/timelines/list/#{list.id}")
977 assert [%{"id" => id}] = json_response(conn, 200)
979 assert id == to_string(activity_one.id)
983 describe "notifications" do
984 test "list of notifications", %{conn: conn} do
986 other_user = insert(:user)
988 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
990 {:ok, [_notification]} = Notification.create_notifications(activity)
994 |> assign(:user, user)
995 |> get("/api/v1/notifications")
998 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1000 }\">@<span>#{user.nickname}</span></a></span>"
1002 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1003 assert response == expected_response
1006 test "getting a single notification", %{conn: conn} do
1007 user = insert(:user)
1008 other_user = insert(:user)
1010 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1012 {:ok, [notification]} = Notification.create_notifications(activity)
1016 |> assign(:user, user)
1017 |> get("/api/v1/notifications/#{notification.id}")
1020 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1022 }\">@<span>#{user.nickname}</span></a></span>"
1024 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1025 assert response == expected_response
1028 test "dismissing a single notification", %{conn: conn} do
1029 user = insert(:user)
1030 other_user = insert(:user)
1032 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1034 {:ok, [notification]} = Notification.create_notifications(activity)
1038 |> assign(:user, user)
1039 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1041 assert %{} = json_response(conn, 200)
1044 test "clearing all notifications", %{conn: conn} do
1045 user = insert(:user)
1046 other_user = insert(:user)
1048 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1050 {:ok, [_notification]} = Notification.create_notifications(activity)
1054 |> assign(:user, user)
1055 |> post("/api/v1/notifications/clear")
1057 assert %{} = json_response(conn, 200)
1061 |> assign(:user, user)
1062 |> get("/api/v1/notifications")
1064 assert all = json_response(conn, 200)
1068 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1069 user = insert(:user)
1070 other_user = insert(:user)
1072 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1073 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1074 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1075 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1077 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1078 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1079 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1080 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1084 |> assign(:user, user)
1089 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1091 result = json_response(conn_res, 200)
1092 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1097 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1099 result = json_response(conn_res, 200)
1100 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1105 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1107 result = json_response(conn_res, 200)
1108 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1111 test "filters notifications using exclude_types", %{conn: conn} do
1112 user = insert(:user)
1113 other_user = insert(:user)
1115 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1116 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1117 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1118 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1119 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1121 mention_notification_id =
1122 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1124 favorite_notification_id =
1125 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1127 reblog_notification_id =
1128 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1130 follow_notification_id =
1131 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1135 |> assign(:user, user)
1138 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1140 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1143 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1145 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1148 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1150 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1153 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1155 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1158 test "destroy multiple", %{conn: conn} do
1159 user = insert(:user)
1160 other_user = insert(:user)
1162 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1163 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1164 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1165 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1167 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1168 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1169 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1170 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1174 |> assign(:user, user)
1178 |> get("/api/v1/notifications")
1180 result = json_response(conn_res, 200)
1181 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1185 |> assign(:user, other_user)
1189 |> get("/api/v1/notifications")
1191 result = json_response(conn_res, 200)
1192 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1196 |> delete("/api/v1/notifications/destroy_multiple", %{
1197 "ids" => [notification1_id, notification2_id]
1200 assert json_response(conn_destroy, 200) == %{}
1204 |> get("/api/v1/notifications")
1206 result = json_response(conn_res, 200)
1207 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1210 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1211 user = insert(:user)
1212 user2 = insert(:user)
1214 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1215 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1217 conn = assign(conn, :user, user)
1219 conn = get(conn, "/api/v1/notifications")
1221 assert length(json_response(conn, 200)) == 1
1223 {:ok, user} = User.mute(user, user2)
1225 conn = assign(build_conn(), :user, user)
1226 conn = get(conn, "/api/v1/notifications")
1228 assert json_response(conn, 200) == []
1231 test "see notifications after muting user without notifications", %{conn: conn} do
1232 user = insert(:user)
1233 user2 = insert(:user)
1235 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1236 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1238 conn = assign(conn, :user, user)
1240 conn = get(conn, "/api/v1/notifications")
1242 assert length(json_response(conn, 200)) == 1
1244 {:ok, user} = User.mute(user, user2, false)
1246 conn = assign(build_conn(), :user, user)
1247 conn = get(conn, "/api/v1/notifications")
1249 assert length(json_response(conn, 200)) == 1
1252 test "see notifications after muting user with notifications and with_muted parameter", %{
1255 user = insert(:user)
1256 user2 = insert(:user)
1258 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1259 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1261 conn = assign(conn, :user, user)
1263 conn = get(conn, "/api/v1/notifications")
1265 assert length(json_response(conn, 200)) == 1
1267 {:ok, user} = User.mute(user, user2)
1269 conn = assign(build_conn(), :user, user)
1270 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1272 assert length(json_response(conn, 200)) == 1
1276 describe "reblogging" do
1277 test "reblogs and returns the reblogged status", %{conn: conn} do
1278 activity = insert(:note_activity)
1279 user = insert(:user)
1283 |> assign(:user, user)
1284 |> post("/api/v1/statuses/#{activity.id}/reblog")
1287 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1289 } = json_response(conn, 200)
1291 assert to_string(activity.id) == id
1294 test "reblogged status for another user", %{conn: conn} do
1295 activity = insert(:note_activity)
1296 user1 = insert(:user)
1297 user2 = insert(:user)
1298 user3 = insert(:user)
1299 CommonAPI.favorite(activity.id, user2)
1300 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1301 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1302 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1306 |> assign(:user, user3)
1307 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1310 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1311 "reblogged" => false,
1312 "favourited" => false,
1313 "bookmarked" => false
1314 } = json_response(conn_res, 200)
1318 |> assign(:user, user2)
1319 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1322 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1323 "reblogged" => true,
1324 "favourited" => true,
1325 "bookmarked" => true
1326 } = json_response(conn_res, 200)
1328 assert to_string(activity.id) == id
1331 test "returns 400 error when activity is not exist", %{conn: conn} do
1332 user = insert(:user)
1336 |> assign(:user, user)
1337 |> post("/api/v1/statuses/foo/reblog")
1339 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1343 describe "unreblogging" do
1344 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1345 activity = insert(:note_activity)
1346 user = insert(:user)
1348 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1352 |> assign(:user, user)
1353 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1355 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1357 assert to_string(activity.id) == id
1360 test "returns 400 error when activity is not exist", %{conn: conn} do
1361 user = insert(:user)
1365 |> assign(:user, user)
1366 |> post("/api/v1/statuses/foo/unreblog")
1368 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1372 describe "favoriting" do
1373 test "favs a status and returns it", %{conn: conn} do
1374 activity = insert(:note_activity)
1375 user = insert(:user)
1379 |> assign(:user, user)
1380 |> post("/api/v1/statuses/#{activity.id}/favourite")
1382 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1383 json_response(conn, 200)
1385 assert to_string(activity.id) == id
1388 test "returns 400 error for a wrong id", %{conn: conn} do
1389 user = insert(:user)
1393 |> assign(:user, user)
1394 |> post("/api/v1/statuses/1/favourite")
1396 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1400 describe "unfavoriting" do
1401 test "unfavorites a status and returns it", %{conn: conn} do
1402 activity = insert(:note_activity)
1403 user = insert(:user)
1405 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1409 |> assign(:user, user)
1410 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1412 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1413 json_response(conn, 200)
1415 assert to_string(activity.id) == id
1418 test "returns 400 error for a wrong id", %{conn: conn} do
1419 user = insert(:user)
1423 |> assign(:user, user)
1424 |> post("/api/v1/statuses/1/unfavourite")
1426 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1430 describe "user timelines" do
1431 test "gets a users statuses", %{conn: conn} do
1432 user_one = insert(:user)
1433 user_two = insert(:user)
1434 user_three = insert(:user)
1436 {:ok, user_three} = User.follow(user_three, user_one)
1438 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1440 {:ok, direct_activity} =
1441 CommonAPI.post(user_one, %{
1442 "status" => "Hi, @#{user_two.nickname}.",
1443 "visibility" => "direct"
1446 {:ok, private_activity} =
1447 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1451 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1453 assert [%{"id" => id}] = json_response(resp, 200)
1454 assert id == to_string(activity.id)
1458 |> assign(:user, user_two)
1459 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1461 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1462 assert id_one == to_string(direct_activity.id)
1463 assert id_two == to_string(activity.id)
1467 |> assign(:user, user_three)
1468 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1470 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1471 assert id_one == to_string(private_activity.id)
1472 assert id_two == to_string(activity.id)
1475 test "unimplemented pinned statuses feature", %{conn: conn} do
1476 note = insert(:note_activity)
1477 user = User.get_cached_by_ap_id(note.data["actor"])
1481 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1483 assert json_response(conn, 200) == []
1486 test "gets an users media", %{conn: conn} do
1487 note = insert(:note_activity)
1488 user = User.get_cached_by_ap_id(note.data["actor"])
1490 file = %Plug.Upload{
1491 content_type: "image/jpg",
1492 path: Path.absname("test/fixtures/image.jpg"),
1493 filename: "an_image.jpg"
1496 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1498 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1502 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1504 assert [%{"id" => id}] = json_response(conn, 200)
1505 assert id == to_string(image_post.id)
1509 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1511 assert [%{"id" => id}] = json_response(conn, 200)
1512 assert id == to_string(image_post.id)
1515 test "gets a user's statuses without reblogs", %{conn: conn} do
1516 user = insert(:user)
1517 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1518 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1522 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1524 assert [%{"id" => id}] = json_response(conn, 200)
1525 assert id == to_string(post.id)
1529 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1531 assert [%{"id" => id}] = json_response(conn, 200)
1532 assert id == to_string(post.id)
1535 test "filters user's statuses by a hashtag", %{conn: conn} do
1536 user = insert(:user)
1537 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1538 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1542 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1544 assert [%{"id" => id}] = json_response(conn, 200)
1545 assert id == to_string(post.id)
1549 describe "user relationships" do
1550 test "returns the relationships for the current user", %{conn: conn} do
1551 user = insert(:user)
1552 other_user = insert(:user)
1553 {:ok, user} = User.follow(user, other_user)
1557 |> assign(:user, user)
1558 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1560 assert [relationship] = json_response(conn, 200)
1562 assert to_string(other_user.id) == relationship["id"]
1566 describe "media upload" do
1568 user = insert(:user)
1572 |> assign(:user, user)
1574 image = %Plug.Upload{
1575 content_type: "image/jpg",
1576 path: Path.absname("test/fixtures/image.jpg"),
1577 filename: "an_image.jpg"
1580 [conn: conn, image: image]
1583 clear_config([:media_proxy])
1584 clear_config([Pleroma.Upload])
1586 test "returns uploaded image", %{conn: conn, image: image} do
1587 desc = "Description of the image"
1591 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1592 |> json_response(:ok)
1594 assert media["type"] == "image"
1595 assert media["description"] == desc
1598 object = Repo.get(Object, media["id"])
1599 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1603 describe "locked accounts" do
1604 test "/api/v1/follow_requests works" do
1605 user = insert(:user, %{info: %User.Info{locked: true}})
1606 other_user = insert(:user)
1608 {:ok, _activity} = ActivityPub.follow(other_user, user)
1610 user = User.get_cached_by_id(user.id)
1611 other_user = User.get_cached_by_id(other_user.id)
1613 assert User.following?(other_user, user) == false
1617 |> assign(:user, user)
1618 |> get("/api/v1/follow_requests")
1620 assert [relationship] = json_response(conn, 200)
1621 assert to_string(other_user.id) == relationship["id"]
1624 test "/api/v1/follow_requests/:id/authorize works" do
1625 user = insert(:user, %{info: %User.Info{locked: true}})
1626 other_user = insert(:user)
1628 {:ok, _activity} = ActivityPub.follow(other_user, user)
1630 user = User.get_cached_by_id(user.id)
1631 other_user = User.get_cached_by_id(other_user.id)
1633 assert User.following?(other_user, user) == false
1637 |> assign(:user, user)
1638 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1640 assert relationship = json_response(conn, 200)
1641 assert to_string(other_user.id) == relationship["id"]
1643 user = User.get_cached_by_id(user.id)
1644 other_user = User.get_cached_by_id(other_user.id)
1646 assert User.following?(other_user, user) == true
1649 test "verify_credentials", %{conn: conn} do
1650 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1654 |> assign(:user, user)
1655 |> get("/api/v1/accounts/verify_credentials")
1657 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1658 assert id == to_string(user.id)
1661 test "/api/v1/follow_requests/:id/reject works" do
1662 user = insert(:user, %{info: %User.Info{locked: true}})
1663 other_user = insert(:user)
1665 {:ok, _activity} = ActivityPub.follow(other_user, user)
1667 user = User.get_cached_by_id(user.id)
1671 |> assign(:user, user)
1672 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1674 assert relationship = json_response(conn, 200)
1675 assert to_string(other_user.id) == relationship["id"]
1677 user = User.get_cached_by_id(user.id)
1678 other_user = User.get_cached_by_id(other_user.id)
1680 assert User.following?(other_user, user) == false
1684 describe "account fetching" do
1685 test "works by id" do
1686 user = insert(:user)
1690 |> get("/api/v1/accounts/#{user.id}")
1692 assert %{"id" => id} = json_response(conn, 200)
1693 assert id == to_string(user.id)
1697 |> get("/api/v1/accounts/-1")
1699 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1702 test "works by nickname" do
1703 user = insert(:user)
1707 |> get("/api/v1/accounts/#{user.nickname}")
1709 assert %{"id" => id} = json_response(conn, 200)
1710 assert id == user.id
1713 test "works by nickname for remote users" do
1714 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1715 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1716 user = insert(:user, nickname: "user@example.com", local: false)
1720 |> get("/api/v1/accounts/#{user.nickname}")
1722 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1723 assert %{"id" => id} = json_response(conn, 200)
1724 assert id == user.id
1727 test "respects limit_to_local_content == :all for remote user nicknames" do
1728 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1729 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1731 user = insert(:user, nickname: "user@example.com", local: false)
1735 |> get("/api/v1/accounts/#{user.nickname}")
1737 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1738 assert json_response(conn, 404)
1741 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1742 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1743 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1745 user = insert(:user, nickname: "user@example.com", local: false)
1746 reading_user = insert(:user)
1750 |> get("/api/v1/accounts/#{user.nickname}")
1752 assert json_response(conn, 404)
1756 |> assign(:user, reading_user)
1757 |> get("/api/v1/accounts/#{user.nickname}")
1759 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1760 assert %{"id" => id} = json_response(conn, 200)
1761 assert id == user.id
1765 test "mascot upload", %{conn: conn} do
1766 user = insert(:user)
1768 non_image_file = %Plug.Upload{
1769 content_type: "audio/mpeg",
1770 path: Path.absname("test/fixtures/sound.mp3"),
1771 filename: "sound.mp3"
1776 |> assign(:user, user)
1777 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1779 assert json_response(conn, 415)
1781 file = %Plug.Upload{
1782 content_type: "image/jpg",
1783 path: Path.absname("test/fixtures/image.jpg"),
1784 filename: "an_image.jpg"
1789 |> assign(:user, user)
1790 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1792 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1795 test "mascot retrieving", %{conn: conn} do
1796 user = insert(:user)
1797 # When user hasn't set a mascot, we should just get pleroma tan back
1800 |> assign(:user, user)
1801 |> get("/api/v1/pleroma/mascot")
1803 assert %{"url" => url} = json_response(conn, 200)
1804 assert url =~ "pleroma-fox-tan-smol"
1806 # When a user sets their mascot, we should get that back
1807 file = %Plug.Upload{
1808 content_type: "image/jpg",
1809 path: Path.absname("test/fixtures/image.jpg"),
1810 filename: "an_image.jpg"
1815 |> assign(:user, user)
1816 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1818 assert json_response(conn, 200)
1820 user = User.get_cached_by_id(user.id)
1824 |> assign(:user, user)
1825 |> get("/api/v1/pleroma/mascot")
1827 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1828 assert url =~ "an_image"
1831 test "hashtag timeline", %{conn: conn} do
1832 following = insert(:user)
1835 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1837 {:ok, [_activity]} =
1838 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1842 |> get("/api/v1/timelines/tag/2hu")
1844 assert [%{"id" => id}] = json_response(nconn, 200)
1846 assert id == to_string(activity.id)
1848 # works for different capitalization too
1851 |> get("/api/v1/timelines/tag/2HU")
1853 assert [%{"id" => id}] = json_response(nconn, 200)
1855 assert id == to_string(activity.id)
1859 test "multi-hashtag timeline", %{conn: conn} do
1860 user = insert(:user)
1862 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1863 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1864 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1868 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1870 [status_none, status_test1, status_test] = json_response(any_test, 200)
1872 assert to_string(activity_test.id) == status_test["id"]
1873 assert to_string(activity_test1.id) == status_test1["id"]
1874 assert to_string(activity_none.id) == status_none["id"]
1878 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1880 assert [status_test1] == json_response(restricted_test, 200)
1882 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1884 assert [status_none] == json_response(all_test, 200)
1887 test "getting followers", %{conn: conn} do
1888 user = insert(:user)
1889 other_user = insert(:user)
1890 {:ok, user} = User.follow(user, other_user)
1894 |> get("/api/v1/accounts/#{other_user.id}/followers")
1896 assert [%{"id" => id}] = json_response(conn, 200)
1897 assert id == to_string(user.id)
1900 test "getting followers, hide_followers", %{conn: conn} do
1901 user = insert(:user)
1902 other_user = insert(:user, %{info: %{hide_followers: true}})
1903 {:ok, _user} = User.follow(user, other_user)
1907 |> get("/api/v1/accounts/#{other_user.id}/followers")
1909 assert [] == json_response(conn, 200)
1912 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1913 user = insert(:user)
1914 other_user = insert(:user, %{info: %{hide_followers: true}})
1915 {:ok, _user} = User.follow(user, other_user)
1919 |> assign(:user, other_user)
1920 |> get("/api/v1/accounts/#{other_user.id}/followers")
1922 refute [] == json_response(conn, 200)
1925 test "getting followers, pagination", %{conn: conn} do
1926 user = insert(:user)
1927 follower1 = insert(:user)
1928 follower2 = insert(:user)
1929 follower3 = insert(:user)
1930 {:ok, _} = User.follow(follower1, user)
1931 {:ok, _} = User.follow(follower2, user)
1932 {:ok, _} = User.follow(follower3, user)
1936 |> assign(:user, user)
1940 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1942 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1943 assert id3 == follower3.id
1944 assert id2 == follower2.id
1948 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1950 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1951 assert id2 == follower2.id
1952 assert id1 == follower1.id
1956 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1958 assert [%{"id" => id2}] = json_response(res_conn, 200)
1959 assert id2 == follower2.id
1961 assert [link_header] = get_resp_header(res_conn, "link")
1962 assert link_header =~ ~r/min_id=#{follower2.id}/
1963 assert link_header =~ ~r/max_id=#{follower2.id}/
1966 test "getting following", %{conn: conn} do
1967 user = insert(:user)
1968 other_user = insert(:user)
1969 {:ok, user} = User.follow(user, other_user)
1973 |> get("/api/v1/accounts/#{user.id}/following")
1975 assert [%{"id" => id}] = json_response(conn, 200)
1976 assert id == to_string(other_user.id)
1979 test "getting following, hide_follows", %{conn: conn} do
1980 user = insert(:user, %{info: %{hide_follows: true}})
1981 other_user = insert(:user)
1982 {:ok, user} = User.follow(user, other_user)
1986 |> get("/api/v1/accounts/#{user.id}/following")
1988 assert [] == json_response(conn, 200)
1991 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1992 user = insert(:user, %{info: %{hide_follows: true}})
1993 other_user = insert(:user)
1994 {:ok, user} = User.follow(user, other_user)
1998 |> assign(:user, user)
1999 |> get("/api/v1/accounts/#{user.id}/following")
2001 refute [] == json_response(conn, 200)
2004 test "getting following, pagination", %{conn: conn} do
2005 user = insert(:user)
2006 following1 = insert(:user)
2007 following2 = insert(:user)
2008 following3 = insert(:user)
2009 {:ok, _} = User.follow(user, following1)
2010 {:ok, _} = User.follow(user, following2)
2011 {:ok, _} = User.follow(user, following3)
2015 |> assign(:user, user)
2019 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2021 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2022 assert id3 == following3.id
2023 assert id2 == following2.id
2027 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2029 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2030 assert id2 == following2.id
2031 assert id1 == following1.id
2035 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2037 assert [%{"id" => id2}] = json_response(res_conn, 200)
2038 assert id2 == following2.id
2040 assert [link_header] = get_resp_header(res_conn, "link")
2041 assert link_header =~ ~r/min_id=#{following2.id}/
2042 assert link_header =~ ~r/max_id=#{following2.id}/
2045 test "following / unfollowing a user", %{conn: conn} do
2046 user = insert(:user)
2047 other_user = insert(:user)
2051 |> assign(:user, user)
2052 |> post("/api/v1/accounts/#{other_user.id}/follow")
2054 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2056 user = User.get_cached_by_id(user.id)
2060 |> assign(:user, user)
2061 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2063 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2065 user = User.get_cached_by_id(user.id)
2069 |> assign(:user, user)
2070 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2072 assert %{"id" => id} = json_response(conn, 200)
2073 assert id == to_string(other_user.id)
2076 test "following without reblogs" do
2077 follower = insert(:user)
2078 followed = insert(:user)
2079 other_user = insert(:user)
2083 |> assign(:user, follower)
2084 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2086 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2088 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2089 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2093 |> assign(:user, User.get_cached_by_id(follower.id))
2094 |> get("/api/v1/timelines/home")
2096 assert [] == json_response(conn, 200)
2100 |> assign(:user, follower)
2101 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2103 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2107 |> assign(:user, User.get_cached_by_id(follower.id))
2108 |> get("/api/v1/timelines/home")
2110 expected_activity_id = reblog.id
2111 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2114 test "following / unfollowing errors" do
2115 user = insert(:user)
2119 |> assign(:user, user)
2122 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2123 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2126 user = User.get_cached_by_id(user.id)
2127 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2128 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2130 # self follow via uri
2131 user = User.get_cached_by_id(user.id)
2132 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2133 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2135 # follow non existing user
2136 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2137 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2139 # follow non existing user via uri
2140 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2141 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2143 # unfollow non existing user
2144 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2145 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2148 describe "mute/unmute" do
2149 test "with notifications", %{conn: conn} do
2150 user = insert(:user)
2151 other_user = insert(:user)
2155 |> assign(:user, user)
2156 |> post("/api/v1/accounts/#{other_user.id}/mute")
2158 response = json_response(conn, 200)
2160 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2161 user = User.get_cached_by_id(user.id)
2165 |> assign(:user, user)
2166 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2168 response = json_response(conn, 200)
2169 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2172 test "without notifications", %{conn: conn} do
2173 user = insert(:user)
2174 other_user = insert(:user)
2178 |> assign(:user, user)
2179 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2181 response = json_response(conn, 200)
2183 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2184 user = User.get_cached_by_id(user.id)
2188 |> assign(:user, user)
2189 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2191 response = json_response(conn, 200)
2192 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2196 test "subscribing / unsubscribing to a user", %{conn: conn} do
2197 user = insert(:user)
2198 subscription_target = insert(:user)
2202 |> assign(:user, user)
2203 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2205 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2209 |> assign(:user, user)
2210 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2212 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2215 test "getting a list of mutes", %{conn: conn} do
2216 user = insert(:user)
2217 other_user = insert(:user)
2219 {:ok, user} = User.mute(user, other_user)
2223 |> assign(:user, user)
2224 |> get("/api/v1/mutes")
2226 other_user_id = to_string(other_user.id)
2227 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2230 test "blocking / unblocking a user", %{conn: conn} do
2231 user = insert(:user)
2232 other_user = insert(:user)
2236 |> assign(:user, user)
2237 |> post("/api/v1/accounts/#{other_user.id}/block")
2239 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2241 user = User.get_cached_by_id(user.id)
2245 |> assign(:user, user)
2246 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2248 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2251 test "getting a list of blocks", %{conn: conn} do
2252 user = insert(:user)
2253 other_user = insert(:user)
2255 {:ok, user} = User.block(user, other_user)
2259 |> assign(:user, user)
2260 |> get("/api/v1/blocks")
2262 other_user_id = to_string(other_user.id)
2263 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2266 test "blocking / unblocking a domain", %{conn: conn} do
2267 user = insert(:user)
2268 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2272 |> assign(:user, user)
2273 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2275 assert %{} = json_response(conn, 200)
2276 user = User.get_cached_by_ap_id(user.ap_id)
2277 assert User.blocks?(user, other_user)
2281 |> assign(:user, user)
2282 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2284 assert %{} = json_response(conn, 200)
2285 user = User.get_cached_by_ap_id(user.ap_id)
2286 refute User.blocks?(user, other_user)
2289 test "getting a list of domain blocks", %{conn: conn} do
2290 user = insert(:user)
2292 {:ok, user} = User.block_domain(user, "bad.site")
2293 {:ok, user} = User.block_domain(user, "even.worse.site")
2297 |> assign(:user, user)
2298 |> get("/api/v1/domain_blocks")
2300 domain_blocks = json_response(conn, 200)
2302 assert "bad.site" in domain_blocks
2303 assert "even.worse.site" in domain_blocks
2306 test "unimplemented follow_requests, blocks, domain blocks" do
2307 user = insert(:user)
2309 ["blocks", "domain_blocks", "follow_requests"]
2310 |> Enum.each(fn endpoint ->
2313 |> assign(:user, user)
2314 |> get("/api/v1/#{endpoint}")
2316 assert [] = json_response(conn, 200)
2320 test "returns the favorites of a user", %{conn: conn} do
2321 user = insert(:user)
2322 other_user = insert(:user)
2324 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2325 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2327 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2331 |> assign(:user, user)
2332 |> get("/api/v1/favourites")
2334 assert [status] = json_response(first_conn, 200)
2335 assert status["id"] == to_string(activity.id)
2337 assert [{"link", _link_header}] =
2338 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2340 # Honours query params
2341 {:ok, second_activity} =
2342 CommonAPI.post(other_user, %{
2344 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2347 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2349 last_like = status["id"]
2353 |> assign(:user, user)
2354 |> get("/api/v1/favourites?since_id=#{last_like}")
2356 assert [second_status] = json_response(second_conn, 200)
2357 assert second_status["id"] == to_string(second_activity.id)
2361 |> assign(:user, user)
2362 |> get("/api/v1/favourites?limit=0")
2364 assert [] = json_response(third_conn, 200)
2367 describe "getting favorites timeline of specified user" do
2369 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2370 [current_user: current_user, user: user]
2373 test "returns list of statuses favorited by specified user", %{
2375 current_user: current_user,
2378 [activity | _] = insert_pair(:note_activity)
2379 CommonAPI.favorite(activity.id, user)
2383 |> assign(:user, current_user)
2384 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2385 |> json_response(:ok)
2389 assert length(response) == 1
2390 assert like["id"] == activity.id
2393 test "returns favorites for specified user_id when user is not logged in", %{
2397 activity = insert(:note_activity)
2398 CommonAPI.favorite(activity.id, user)
2402 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2403 |> json_response(:ok)
2405 assert length(response) == 1
2408 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2410 current_user: current_user,
2414 CommonAPI.post(current_user, %{
2415 "status" => "Hi @#{user.nickname}!",
2416 "visibility" => "direct"
2419 CommonAPI.favorite(direct.id, user)
2423 |> assign(:user, current_user)
2424 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2425 |> json_response(:ok)
2427 assert length(response) == 1
2429 anonymous_response =
2431 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2432 |> json_response(:ok)
2434 assert Enum.empty?(anonymous_response)
2437 test "does not return others' favorited DM when user is not one of recipients", %{
2439 current_user: current_user,
2442 user_two = insert(:user)
2445 CommonAPI.post(user_two, %{
2446 "status" => "Hi @#{user.nickname}!",
2447 "visibility" => "direct"
2450 CommonAPI.favorite(direct.id, user)
2454 |> assign(:user, current_user)
2455 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2456 |> json_response(:ok)
2458 assert Enum.empty?(response)
2461 test "paginates favorites using since_id and max_id", %{
2463 current_user: current_user,
2466 activities = insert_list(10, :note_activity)
2468 Enum.each(activities, fn activity ->
2469 CommonAPI.favorite(activity.id, user)
2472 third_activity = Enum.at(activities, 2)
2473 seventh_activity = Enum.at(activities, 6)
2477 |> assign(:user, current_user)
2478 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2479 since_id: third_activity.id,
2480 max_id: seventh_activity.id
2482 |> json_response(:ok)
2484 assert length(response) == 3
2485 refute third_activity in response
2486 refute seventh_activity in response
2489 test "limits favorites using limit parameter", %{
2491 current_user: current_user,
2495 |> insert_list(:note_activity)
2496 |> Enum.each(fn activity ->
2497 CommonAPI.favorite(activity.id, user)
2502 |> assign(:user, current_user)
2503 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2504 |> json_response(:ok)
2506 assert length(response) == 3
2509 test "returns empty response when user does not have any favorited statuses", %{
2511 current_user: current_user,
2516 |> assign(:user, current_user)
2517 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2518 |> json_response(:ok)
2520 assert Enum.empty?(response)
2523 test "returns 404 error when specified user is not exist", %{conn: conn} do
2524 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2526 assert json_response(conn, 404) == %{"error" => "Record not found"}
2529 test "returns 403 error when user has hidden own favorites", %{
2531 current_user: current_user
2533 user = insert(:user, %{info: %{hide_favorites: true}})
2534 activity = insert(:note_activity)
2535 CommonAPI.favorite(activity.id, user)
2539 |> assign(:user, current_user)
2540 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2542 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2545 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2546 user = insert(:user)
2547 activity = insert(:note_activity)
2548 CommonAPI.favorite(activity.id, user)
2552 |> assign(:user, current_user)
2553 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2555 assert user.info.hide_favorites
2556 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2560 test "get instance information", %{conn: conn} do
2561 conn = get(conn, "/api/v1/instance")
2562 assert result = json_response(conn, 200)
2564 email = Config.get([:instance, :email])
2565 # Note: not checking for "max_toot_chars" since it's optional
2571 "email" => from_config_email,
2573 "streaming_api" => _
2578 "registrations" => _,
2582 assert email == from_config_email
2585 test "get instance stats", %{conn: conn} do
2586 user = insert(:user, %{local: true})
2588 user2 = insert(:user, %{local: true})
2589 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2591 insert(:user, %{local: false, nickname: "u@peer1.com"})
2592 insert(:user, %{local: false, nickname: "u@peer2.com"})
2594 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2596 # Stats should count users with missing or nil `info.deactivated` value
2597 user = User.get_cached_by_id(user.id)
2598 info_change = Changeset.change(user.info, %{deactivated: nil})
2602 |> Changeset.change()
2603 |> Changeset.put_embed(:info, info_change)
2604 |> User.update_and_set_cache()
2606 Pleroma.Stats.force_update()
2608 conn = get(conn, "/api/v1/instance")
2610 assert result = json_response(conn, 200)
2612 stats = result["stats"]
2615 assert stats["user_count"] == 1
2616 assert stats["status_count"] == 1
2617 assert stats["domain_count"] == 2
2620 test "get peers", %{conn: conn} do
2621 insert(:user, %{local: false, nickname: "u@peer1.com"})
2622 insert(:user, %{local: false, nickname: "u@peer2.com"})
2624 Pleroma.Stats.force_update()
2626 conn = get(conn, "/api/v1/instance/peers")
2628 assert result = json_response(conn, 200)
2630 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2633 test "put settings", %{conn: conn} do
2634 user = insert(:user)
2638 |> assign(:user, user)
2639 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2641 assert _result = json_response(conn, 200)
2643 user = User.get_cached_by_ap_id(user.ap_id)
2644 assert user.info.settings == %{"programming" => "socks"}
2647 describe "pinned statuses" do
2649 user = insert(:user)
2650 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2652 [user: user, activity: activity]
2655 clear_config([:instance, :max_pinned_statuses]) do
2656 Config.put([:instance, :max_pinned_statuses], 1)
2659 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2660 {:ok, _} = CommonAPI.pin(activity.id, user)
2664 |> assign(:user, user)
2665 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2666 |> json_response(200)
2668 id_str = to_string(activity.id)
2670 assert [%{"id" => ^id_str, "pinned" => true}] = result
2673 test "pin status", %{conn: conn, user: user, activity: activity} do
2674 id_str = to_string(activity.id)
2676 assert %{"id" => ^id_str, "pinned" => true} =
2678 |> assign(:user, user)
2679 |> post("/api/v1/statuses/#{activity.id}/pin")
2680 |> json_response(200)
2682 assert [%{"id" => ^id_str, "pinned" => true}] =
2684 |> assign(:user, user)
2685 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2686 |> json_response(200)
2689 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2690 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2694 |> assign(:user, user)
2695 |> post("/api/v1/statuses/#{dm.id}/pin")
2697 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2700 test "unpin status", %{conn: conn, user: user, activity: activity} do
2701 {:ok, _} = CommonAPI.pin(activity.id, user)
2703 id_str = to_string(activity.id)
2704 user = refresh_record(user)
2706 assert %{"id" => ^id_str, "pinned" => false} =
2708 |> assign(:user, user)
2709 |> post("/api/v1/statuses/#{activity.id}/unpin")
2710 |> json_response(200)
2714 |> assign(:user, user)
2715 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2716 |> json_response(200)
2719 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2722 |> assign(:user, user)
2723 |> post("/api/v1/statuses/1/unpin")
2725 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2728 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2729 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2731 id_str_one = to_string(activity_one.id)
2733 assert %{"id" => ^id_str_one, "pinned" => true} =
2735 |> assign(:user, user)
2736 |> post("/api/v1/statuses/#{id_str_one}/pin")
2737 |> json_response(200)
2739 user = refresh_record(user)
2741 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2743 |> assign(:user, user)
2744 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2745 |> json_response(400)
2751 Config.put([:rich_media, :enabled], true)
2753 user = insert(:user)
2757 test "returns rich-media card", %{conn: conn, user: user} do
2758 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2761 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2762 "provider_name" => "example.com",
2763 "provider_url" => "https://example.com",
2764 "title" => "The Rock",
2766 "url" => "https://example.com/ogp",
2768 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2771 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2772 "title" => "The Rock",
2773 "type" => "video.movie",
2774 "url" => "https://example.com/ogp",
2776 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2783 |> get("/api/v1/statuses/#{activity.id}/card")
2784 |> json_response(200)
2786 assert response == card_data
2788 # works with private posts
2790 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2794 |> assign(:user, user)
2795 |> get("/api/v1/statuses/#{activity.id}/card")
2796 |> json_response(200)
2798 assert response_two == card_data
2801 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2803 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2807 |> get("/api/v1/statuses/#{activity.id}/card")
2808 |> json_response(:ok)
2810 assert response == %{
2812 "title" => "Pleroma",
2813 "description" => "",
2815 "provider_name" => "example.com",
2816 "provider_url" => "https://example.com",
2817 "url" => "https://example.com/ogp-missing-data",
2820 "title" => "Pleroma",
2821 "type" => "website",
2822 "url" => "https://example.com/ogp-missing-data"
2830 user = insert(:user)
2831 for_user = insert(:user)
2834 CommonAPI.post(user, %{
2835 "status" => "heweoo?"
2839 CommonAPI.post(user, %{
2840 "status" => "heweoo!"
2845 |> assign(:user, for_user)
2846 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2848 assert json_response(response1, 200)["bookmarked"] == true
2852 |> assign(:user, for_user)
2853 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2855 assert json_response(response2, 200)["bookmarked"] == true
2859 |> assign(:user, for_user)
2860 |> get("/api/v1/bookmarks")
2862 assert [json_response(response2, 200), json_response(response1, 200)] ==
2863 json_response(bookmarks, 200)
2867 |> assign(:user, for_user)
2868 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2870 assert json_response(response1, 200)["bookmarked"] == false
2874 |> assign(:user, for_user)
2875 |> get("/api/v1/bookmarks")
2877 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2880 describe "conversation muting" do
2882 post_user = insert(:user)
2883 user = insert(:user)
2885 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2887 [user: user, activity: activity]
2890 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2891 id_str = to_string(activity.id)
2893 assert %{"id" => ^id_str, "muted" => true} =
2895 |> assign(:user, user)
2896 |> post("/api/v1/statuses/#{activity.id}/mute")
2897 |> json_response(200)
2900 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2901 {:ok, _} = CommonAPI.add_mute(user, activity)
2905 |> assign(:user, user)
2906 |> post("/api/v1/statuses/#{activity.id}/mute")
2908 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2911 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2912 {:ok, _} = CommonAPI.add_mute(user, activity)
2914 id_str = to_string(activity.id)
2915 user = refresh_record(user)
2917 assert %{"id" => ^id_str, "muted" => false} =
2919 |> assign(:user, user)
2920 |> post("/api/v1/statuses/#{activity.id}/unmute")
2921 |> json_response(200)
2925 describe "reports" do
2927 reporter = insert(:user)
2928 target_user = insert(:user)
2930 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2932 [reporter: reporter, target_user: target_user, activity: activity]
2935 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2936 assert %{"action_taken" => false, "id" => _} =
2938 |> assign(:user, reporter)
2939 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2940 |> json_response(200)
2943 test "submit a report with statuses and comment", %{
2946 target_user: target_user,
2949 assert %{"action_taken" => false, "id" => _} =
2951 |> assign(:user, reporter)
2952 |> post("/api/v1/reports", %{
2953 "account_id" => target_user.id,
2954 "status_ids" => [activity.id],
2955 "comment" => "bad status!",
2956 "forward" => "false"
2958 |> json_response(200)
2961 test "account_id is required", %{
2966 assert %{"error" => "Valid `account_id` required"} =
2968 |> assign(:user, reporter)
2969 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2970 |> json_response(400)
2973 test "comment must be up to the size specified in the config", %{
2976 target_user: target_user
2978 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2979 comment = String.pad_trailing("a", max_size + 1, "a")
2981 error = %{"error" => "Comment must be up to #{max_size} characters"}
2985 |> assign(:user, reporter)
2986 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2987 |> json_response(400)
2990 test "returns error when account is not exist", %{
2997 |> assign(:user, reporter)
2998 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3000 assert json_response(conn, 400) == %{"error" => "Account not found"}
3004 describe "link headers" do
3005 test "preserves parameters in link headers", %{conn: conn} do
3006 user = insert(:user)
3007 other_user = insert(:user)
3010 CommonAPI.post(other_user, %{
3011 "status" => "hi @#{user.nickname}",
3012 "visibility" => "public"
3016 CommonAPI.post(other_user, %{
3017 "status" => "hi @#{user.nickname}",
3018 "visibility" => "public"
3021 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3022 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3026 |> assign(:user, user)
3027 |> get("/api/v1/notifications", %{media_only: true})
3029 assert [link_header] = get_resp_header(conn, "link")
3030 assert link_header =~ ~r/media_only=true/
3031 assert link_header =~ ~r/min_id=#{notification2.id}/
3032 assert link_header =~ ~r/max_id=#{notification1.id}/
3036 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3037 # Need to set an old-style integer ID to reproduce the problem
3038 # (these are no longer assigned to new accounts but were preserved
3039 # for existing accounts during the migration to flakeIDs)
3040 user_one = insert(:user, %{id: 1212})
3041 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3045 |> get("/api/v1/accounts/#{user_one.id}")
3049 |> get("/api/v1/accounts/#{user_two.nickname}")
3053 |> get("/api/v1/accounts/#{user_two.id}")
3055 acc_one = json_response(resp_one, 200)
3056 acc_two = json_response(resp_two, 200)
3057 acc_three = json_response(resp_three, 200)
3058 refute acc_one == acc_two
3059 assert acc_two == acc_three
3062 describe "custom emoji" do
3063 test "with tags", %{conn: conn} do
3066 |> get("/api/v1/custom_emojis")
3067 |> json_response(200)
3069 assert Map.has_key?(emoji, "shortcode")
3070 assert Map.has_key?(emoji, "static_url")
3071 assert Map.has_key?(emoji, "tags")
3072 assert is_list(emoji["tags"])
3073 assert Map.has_key?(emoji, "category")
3074 assert Map.has_key?(emoji, "url")
3075 assert Map.has_key?(emoji, "visible_in_picker")
3079 describe "index/2 redirections" do
3080 setup %{conn: conn} do
3084 signing_salt: "cooldude"
3089 |> Plug.Session.call(Plug.Session.init(session_opts))
3092 test_path = "/web/statuses/test"
3093 %{conn: conn, path: test_path}
3096 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3097 conn = get(conn, path)
3099 assert conn.status == 302
3100 assert redirected_to(conn) == "/web/login"
3103 test "redirects not logged-in users to the login page on private instances", %{
3107 Config.put([:instance, :public], false)
3109 conn = get(conn, path)
3111 assert conn.status == 302
3112 assert redirected_to(conn) == "/web/login"
3115 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3116 token = insert(:oauth_token)
3120 |> assign(:user, token.user)
3121 |> put_session(:oauth_token, token.token)
3124 assert conn.status == 200
3127 test "saves referer path to session", %{conn: conn, path: path} do
3128 conn = get(conn, path)
3129 return_to = Plug.Conn.get_session(conn, :return_to)
3131 assert return_to == path
3134 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3135 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3136 auth = insert(:oauth_authorization, app: app)
3140 |> put_session(:return_to, path)
3141 |> get("/web/login", %{code: auth.token})
3143 assert conn.status == 302
3144 assert redirected_to(conn) == path
3147 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3148 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3149 auth = insert(:oauth_authorization, app: app)
3151 conn = get(conn, "/web/login", %{code: auth.token})
3153 assert conn.status == 302
3154 assert redirected_to(conn) == "/web/getting-started"
3158 describe "scheduled activities" do
3159 test "creates a scheduled activity", %{conn: conn} do
3160 user = insert(:user)
3161 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3165 |> assign(:user, user)
3166 |> post("/api/v1/statuses", %{
3167 "status" => "scheduled",
3168 "scheduled_at" => scheduled_at
3171 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3172 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3173 assert [] == Repo.all(Activity)
3176 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3177 user = insert(:user)
3178 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3180 file = %Plug.Upload{
3181 content_type: "image/jpg",
3182 path: Path.absname("test/fixtures/image.jpg"),
3183 filename: "an_image.jpg"
3186 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3190 |> assign(:user, user)
3191 |> post("/api/v1/statuses", %{
3192 "media_ids" => [to_string(upload.id)],
3193 "status" => "scheduled",
3194 "scheduled_at" => scheduled_at
3197 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3198 assert %{"type" => "image"} = media_attachment
3201 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3203 user = insert(:user)
3206 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3210 |> assign(:user, user)
3211 |> post("/api/v1/statuses", %{
3212 "status" => "not scheduled",
3213 "scheduled_at" => scheduled_at
3216 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3217 assert [] == Repo.all(ScheduledActivity)
3220 test "returns error when daily user limit is exceeded", %{conn: conn} do
3221 user = insert(:user)
3224 NaiveDateTime.utc_now()
3225 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3226 |> NaiveDateTime.to_iso8601()
3228 attrs = %{params: %{}, scheduled_at: today}
3229 {:ok, _} = ScheduledActivity.create(user, attrs)
3230 {:ok, _} = ScheduledActivity.create(user, attrs)
3234 |> assign(:user, user)
3235 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3237 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3240 test "returns error when total user limit is exceeded", %{conn: conn} do
3241 user = insert(:user)
3244 NaiveDateTime.utc_now()
3245 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3246 |> NaiveDateTime.to_iso8601()
3249 NaiveDateTime.utc_now()
3250 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3251 |> NaiveDateTime.to_iso8601()
3253 attrs = %{params: %{}, scheduled_at: today}
3254 {:ok, _} = ScheduledActivity.create(user, attrs)
3255 {:ok, _} = ScheduledActivity.create(user, attrs)
3256 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3260 |> assign(:user, user)
3261 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3263 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3266 test "shows scheduled activities", %{conn: conn} do
3267 user = insert(:user)
3268 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3269 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3270 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3271 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3275 |> assign(:user, user)
3280 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3282 result = json_response(conn_res, 200)
3283 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3288 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3290 result = json_response(conn_res, 200)
3291 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3296 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3298 result = json_response(conn_res, 200)
3299 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3302 test "shows a scheduled activity", %{conn: conn} do
3303 user = insert(:user)
3304 scheduled_activity = insert(:scheduled_activity, user: user)
3308 |> assign(:user, user)
3309 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3311 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3312 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3316 |> assign(:user, user)
3317 |> get("/api/v1/scheduled_statuses/404")
3319 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3322 test "updates a scheduled activity", %{conn: conn} do
3323 user = insert(:user)
3324 scheduled_activity = insert(:scheduled_activity, user: user)
3327 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3331 |> assign(:user, user)
3332 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3333 scheduled_at: new_scheduled_at
3336 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3337 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3341 |> assign(:user, user)
3342 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3344 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3347 test "deletes a scheduled activity", %{conn: conn} do
3348 user = insert(:user)
3349 scheduled_activity = insert(:scheduled_activity, user: user)
3353 |> assign(:user, user)
3354 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3356 assert %{} = json_response(res_conn, 200)
3357 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3361 |> assign(:user, user)
3362 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3364 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3368 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3369 user1 = insert(:user)
3370 user2 = insert(:user)
3371 user3 = insert(:user)
3373 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3375 # Reply to status from another user
3378 |> assign(:user, user2)
3379 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3381 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3383 activity = Activity.get_by_id_with_object(id)
3385 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3386 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3388 # Reblog from the third user
3391 |> assign(:user, user3)
3392 |> post("/api/v1/statuses/#{activity.id}/reblog")
3394 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3395 json_response(conn2, 200)
3397 assert to_string(activity.id) == id
3399 # Getting third user status
3402 |> assign(:user, user3)
3403 |> get("api/v1/timelines/home")
3405 [reblogged_activity] = json_response(conn3, 200)
3407 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3409 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3410 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3413 describe "create account by app" do
3414 test "Account registration via Application", %{conn: conn} do
3417 |> post("/api/v1/apps", %{
3418 client_name: "client_name",
3419 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3420 scopes: "read, write, follow"
3424 "client_id" => client_id,
3425 "client_secret" => client_secret,
3427 "name" => "client_name",
3428 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3431 } = json_response(conn, 200)
3435 |> post("/oauth/token", %{
3436 grant_type: "client_credentials",
3437 client_id: client_id,
3438 client_secret: client_secret
3441 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3442 json_response(conn, 200)
3445 token_from_db = Repo.get_by(Token, token: token)
3446 assert token_from_db
3448 assert scope == "read write follow"
3452 |> put_req_header("authorization", "Bearer " <> token)
3453 |> post("/api/v1/accounts", %{
3455 email: "lain@example.org",
3456 password: "PlzDontHackLain",
3461 "access_token" => token,
3462 "created_at" => _created_at,
3464 "token_type" => "Bearer"
3465 } = json_response(conn, 200)
3467 token_from_db = Repo.get_by(Token, token: token)
3468 assert token_from_db
3469 token_from_db = Repo.preload(token_from_db, :user)
3470 assert token_from_db.user
3472 assert token_from_db.user.info.confirmation_pending
3475 test "rate limit", %{conn: conn} do
3476 app_token = insert(:oauth_token, user: nil)
3479 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3480 |> Map.put(:remote_ip, {15, 15, 15, 15})
3485 |> post("/api/v1/accounts", %{
3486 username: "#{i}lain",
3487 email: "#{i}lain@example.org",
3488 password: "PlzDontHackLain",
3493 "access_token" => token,
3494 "created_at" => _created_at,
3496 "token_type" => "Bearer"
3497 } = json_response(conn, 200)
3499 token_from_db = Repo.get_by(Token, token: token)
3500 assert token_from_db
3501 token_from_db = Repo.preload(token_from_db, :user)
3502 assert token_from_db.user
3504 assert token_from_db.user.info.confirmation_pending
3509 |> post("/api/v1/accounts", %{
3511 email: "6lain@example.org",
3512 password: "PlzDontHackLain",
3516 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3520 describe "GET /api/v1/polls/:id" do
3521 test "returns poll entity for object id", %{conn: conn} do
3522 user = insert(:user)
3525 CommonAPI.post(user, %{
3526 "status" => "Pleroma does",
3527 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3530 object = Object.normalize(activity)
3534 |> assign(:user, user)
3535 |> get("/api/v1/polls/#{object.id}")
3537 response = json_response(conn, 200)
3538 id = to_string(object.id)
3539 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3542 test "does not expose polls for private statuses", %{conn: conn} do
3543 user = insert(:user)
3544 other_user = insert(:user)
3547 CommonAPI.post(user, %{
3548 "status" => "Pleroma does",
3549 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3550 "visibility" => "private"
3553 object = Object.normalize(activity)
3557 |> assign(:user, other_user)
3558 |> get("/api/v1/polls/#{object.id}")
3560 assert json_response(conn, 404)
3564 describe "POST /api/v1/polls/:id/votes" do
3565 test "votes are added to the poll", %{conn: conn} do
3566 user = insert(:user)
3567 other_user = insert(:user)
3570 CommonAPI.post(user, %{
3571 "status" => "A very delicious sandwich",
3573 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3579 object = Object.normalize(activity)
3583 |> assign(:user, other_user)
3584 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3586 assert json_response(conn, 200)
3587 object = Object.get_by_id(object.id)
3589 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3594 test "author can't vote", %{conn: conn} do
3595 user = insert(:user)
3598 CommonAPI.post(user, %{
3599 "status" => "Am I cute?",
3600 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3603 object = Object.normalize(activity)
3606 |> assign(:user, user)
3607 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3608 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3610 object = Object.get_by_id(object.id)
3612 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3615 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3616 user = insert(:user)
3617 other_user = insert(:user)
3620 CommonAPI.post(user, %{
3621 "status" => "The glass is",
3622 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3625 object = Object.normalize(activity)
3628 |> assign(:user, other_user)
3629 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3630 |> json_response(422) == %{"error" => "Too many choices"}
3632 object = Object.get_by_id(object.id)
3634 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3639 test "does not allow choice index to be greater than options count", %{conn: conn} do
3640 user = insert(:user)
3641 other_user = insert(:user)
3644 CommonAPI.post(user, %{
3645 "status" => "Am I cute?",
3646 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3649 object = Object.normalize(activity)
3653 |> assign(:user, other_user)
3654 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3656 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3659 test "returns 404 error when object is not exist", %{conn: conn} do
3660 user = insert(:user)
3664 |> assign(:user, user)
3665 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3667 assert json_response(conn, 404) == %{"error" => "Record not found"}
3670 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3671 user = insert(:user)
3672 other_user = insert(:user)
3675 CommonAPI.post(user, %{
3676 "status" => "Am I cute?",
3677 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3678 "visibility" => "private"
3681 object = Object.normalize(activity)
3685 |> assign(:user, other_user)
3686 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3688 assert json_response(conn, 404) == %{"error" => "Record not found"}
3692 describe "GET /api/v1/statuses/:id/favourited_by" do
3694 user = insert(:user)
3695 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3699 |> assign(:user, user)
3701 [conn: conn, activity: activity]
3704 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3705 other_user = insert(:user)
3706 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3710 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3711 |> json_response(:ok)
3713 [%{"id" => id}] = response
3715 assert id == other_user.id
3718 test "returns empty array when status has not been favorited yet", %{
3724 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3725 |> json_response(:ok)
3727 assert Enum.empty?(response)
3730 test "does not return users who have favorited the status but are blocked", %{
3731 conn: %{assigns: %{user: user}} = conn,
3734 other_user = insert(:user)
3735 {:ok, user} = User.block(user, other_user)
3737 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3741 |> assign(:user, user)
3742 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3743 |> json_response(:ok)
3745 assert Enum.empty?(response)
3748 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3749 other_user = insert(:user)
3750 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3754 |> assign(:user, nil)
3755 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3756 |> json_response(:ok)
3758 [%{"id" => id}] = response
3759 assert id == other_user.id
3763 describe "GET /api/v1/statuses/:id/reblogged_by" do
3765 user = insert(:user)
3766 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3770 |> assign(:user, user)
3772 [conn: conn, activity: activity]
3775 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3776 other_user = insert(:user)
3777 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3781 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3782 |> json_response(:ok)
3784 [%{"id" => id}] = response
3786 assert id == other_user.id
3789 test "returns empty array when status has not been reblogged yet", %{
3795 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3796 |> json_response(:ok)
3798 assert Enum.empty?(response)
3801 test "does not return users who have reblogged the status but are blocked", %{
3802 conn: %{assigns: %{user: user}} = conn,
3805 other_user = insert(:user)
3806 {:ok, user} = User.block(user, other_user)
3808 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3812 |> assign(:user, user)
3813 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3814 |> json_response(:ok)
3816 assert Enum.empty?(response)
3819 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3820 other_user = insert(:user)
3821 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3825 |> assign(:user, nil)
3826 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3827 |> json_response(:ok)
3829 [%{"id" => id}] = response
3830 assert id == other_user.id
3834 describe "POST /auth/password, with valid parameters" do
3835 setup %{conn: conn} do
3836 user = insert(:user)
3837 conn = post(conn, "/auth/password?email=#{user.email}")
3838 %{conn: conn, user: user}
3841 test "it returns 204", %{conn: conn} do
3842 assert json_response(conn, :no_content)
3845 test "it creates a PasswordResetToken record for user", %{user: user} do
3846 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3850 test "it sends an email to user", %{user: user} do
3851 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3853 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3854 notify_email = Config.get([:instance, :notify_email])
3855 instance_name = Config.get([:instance, :name])
3858 from: {instance_name, notify_email},
3859 to: {user.name, user.email},
3860 html_body: email.html_body
3865 describe "POST /auth/password, with invalid parameters" do
3867 user = insert(:user)
3871 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3872 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3873 assert conn.status == 404
3874 assert conn.resp_body == ""
3877 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3878 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3879 conn = post(conn, "/auth/password?email=#{user.email}")
3880 assert conn.status == 400
3881 assert conn.resp_body == ""
3885 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3887 user = insert(:user)
3888 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3892 |> Changeset.change()
3893 |> Changeset.put_embed(:info, info_change)
3896 assert user.info.confirmation_pending
3901 clear_config([:instance, :account_activation_required]) do
3902 Config.put([:instance, :account_activation_required], true)
3905 test "resend account confirmation email", %{conn: conn, user: user} do
3907 |> assign(:user, user)
3908 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3909 |> json_response(:no_content)
3911 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3912 notify_email = Config.get([:instance, :notify_email])
3913 instance_name = Config.get([:instance, :name])
3916 from: {instance_name, notify_email},
3917 to: {user.name, user.email},
3918 html_body: email.html_body
3923 describe "GET /api/v1/suggestions" do
3925 user = insert(:user)
3926 other_user = insert(:user)
3927 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3928 url500 = "http://test500?#{host}&#{user.nickname}"
3929 url200 = "http://test200?#{host}&#{user.nickname}"
3932 %{method: :get, url: ^url500} ->
3933 %Tesla.Env{status: 500, body: "bad request"}
3935 %{method: :get, url: ^url200} ->
3939 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3941 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3945 [user: user, other_user: other_user]
3948 clear_config(:suggestions)
3950 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3951 Config.put([:suggestions, :enabled], false)
3955 |> assign(:user, user)
3956 |> get("/api/v1/suggestions")
3957 |> json_response(200)
3962 test "returns error", %{conn: conn, user: user} do
3963 Config.put([:suggestions, :enabled], true)
3964 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
3966 assert capture_log(fn ->
3969 |> assign(:user, user)
3970 |> get("/api/v1/suggestions")
3971 |> json_response(500)
3973 assert res == "Something went wrong"
3974 end) =~ "Could not retrieve suggestions"
3977 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
3978 Config.put([:suggestions, :enabled], true)
3979 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
3983 |> assign(:user, user)
3984 |> get("/api/v1/suggestions")
3985 |> json_response(200)
3990 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
3991 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
3995 "acct" => other_user.ap_id,
3996 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
3997 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
3998 "id" => other_user.id