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 describe "deleting a status" do
748 test "when you created it", %{conn: conn} do
749 activity = insert(:note_activity)
750 author = User.get_cached_by_ap_id(activity.data["actor"])
754 |> assign(:user, author)
755 |> delete("/api/v1/statuses/#{activity.id}")
757 assert %{} = json_response(conn, 200)
759 refute Activity.get_by_id(activity.id)
762 test "when you didn't create it", %{conn: conn} do
763 activity = insert(:note_activity)
768 |> assign(:user, user)
769 |> delete("/api/v1/statuses/#{activity.id}")
771 assert %{"error" => _} = json_response(conn, 403)
773 assert Activity.get_by_id(activity.id) == activity
776 test "when you're an admin or moderator", %{conn: conn} do
777 activity1 = insert(:note_activity)
778 activity2 = insert(:note_activity)
779 admin = insert(:user, info: %{is_admin: true})
780 moderator = insert(:user, info: %{is_moderator: true})
784 |> assign(:user, admin)
785 |> delete("/api/v1/statuses/#{activity1.id}")
787 assert %{} = json_response(res_conn, 200)
791 |> assign(:user, moderator)
792 |> delete("/api/v1/statuses/#{activity2.id}")
794 assert %{} = json_response(res_conn, 200)
796 refute Activity.get_by_id(activity1.id)
797 refute Activity.get_by_id(activity2.id)
801 describe "filters" do
802 test "creating a filter", %{conn: conn} do
805 filter = %Pleroma.Filter{
812 |> assign(:user, user)
813 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
815 assert response = json_response(conn, 200)
816 assert response["phrase"] == filter.phrase
817 assert response["context"] == filter.context
818 assert response["irreversible"] == false
819 assert response["id"] != nil
820 assert response["id"] != ""
823 test "fetching a list of filters", %{conn: conn} do
826 query_one = %Pleroma.Filter{
833 query_two = %Pleroma.Filter{
840 {:ok, filter_one} = Pleroma.Filter.create(query_one)
841 {:ok, filter_two} = Pleroma.Filter.create(query_two)
845 |> assign(:user, user)
846 |> get("/api/v1/filters")
847 |> json_response(200)
853 filters: [filter_two, filter_one]
857 test "get a filter", %{conn: conn} do
860 query = %Pleroma.Filter{
867 {:ok, filter} = Pleroma.Filter.create(query)
871 |> assign(:user, user)
872 |> get("/api/v1/filters/#{filter.filter_id}")
874 assert _response = json_response(conn, 200)
877 test "update a filter", %{conn: conn} do
880 query = %Pleroma.Filter{
887 {:ok, _filter} = Pleroma.Filter.create(query)
889 new = %Pleroma.Filter{
896 |> assign(:user, user)
897 |> put("/api/v1/filters/#{query.filter_id}", %{
902 assert response = json_response(conn, 200)
903 assert response["phrase"] == new.phrase
904 assert response["context"] == new.context
907 test "delete a filter", %{conn: conn} do
910 query = %Pleroma.Filter{
917 {:ok, filter} = Pleroma.Filter.create(query)
921 |> assign(:user, user)
922 |> delete("/api/v1/filters/#{filter.filter_id}")
924 assert response = json_response(conn, 200)
925 assert response == %{}
929 describe "list timelines" do
930 test "list timeline", %{conn: conn} do
932 other_user = insert(:user)
933 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
934 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
935 {:ok, list} = Pleroma.List.create("name", user)
936 {:ok, list} = Pleroma.List.follow(list, other_user)
940 |> assign(:user, user)
941 |> get("/api/v1/timelines/list/#{list.id}")
943 assert [%{"id" => id}] = json_response(conn, 200)
945 assert id == to_string(activity_two.id)
948 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
950 other_user = insert(:user)
951 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
953 {:ok, _activity_two} =
954 CommonAPI.post(other_user, %{
955 "status" => "Marisa is cute.",
956 "visibility" => "private"
959 {:ok, list} = Pleroma.List.create("name", user)
960 {:ok, list} = Pleroma.List.follow(list, other_user)
964 |> assign(:user, user)
965 |> get("/api/v1/timelines/list/#{list.id}")
967 assert [%{"id" => id}] = json_response(conn, 200)
969 assert id == to_string(activity_one.id)
973 describe "notifications" do
974 test "list of notifications", %{conn: conn} do
976 other_user = insert(:user)
978 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
980 {:ok, [_notification]} = Notification.create_notifications(activity)
984 |> assign(:user, user)
985 |> get("/api/v1/notifications")
988 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
990 }\">@<span>#{user.nickname}</span></a></span>"
992 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
993 assert response == expected_response
996 test "getting a single notification", %{conn: conn} do
998 other_user = insert(:user)
1000 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1002 {:ok, [notification]} = Notification.create_notifications(activity)
1006 |> assign(:user, user)
1007 |> get("/api/v1/notifications/#{notification.id}")
1010 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1012 }\">@<span>#{user.nickname}</span></a></span>"
1014 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1015 assert response == expected_response
1018 test "dismissing a single notification", %{conn: conn} do
1019 user = insert(:user)
1020 other_user = insert(:user)
1022 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1024 {:ok, [notification]} = Notification.create_notifications(activity)
1028 |> assign(:user, user)
1029 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1031 assert %{} = json_response(conn, 200)
1034 test "clearing all notifications", %{conn: conn} do
1035 user = insert(:user)
1036 other_user = insert(:user)
1038 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1040 {:ok, [_notification]} = Notification.create_notifications(activity)
1044 |> assign(:user, user)
1045 |> post("/api/v1/notifications/clear")
1047 assert %{} = json_response(conn, 200)
1051 |> assign(:user, user)
1052 |> get("/api/v1/notifications")
1054 assert all = json_response(conn, 200)
1058 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1059 user = insert(:user)
1060 other_user = insert(:user)
1062 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1063 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1064 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1065 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1067 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1068 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1069 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1070 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1074 |> assign(:user, user)
1079 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1081 result = json_response(conn_res, 200)
1082 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1087 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1089 result = json_response(conn_res, 200)
1090 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1095 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1097 result = json_response(conn_res, 200)
1098 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1101 test "filters notifications using exclude_types", %{conn: conn} do
1102 user = insert(:user)
1103 other_user = insert(:user)
1105 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1106 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1107 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1108 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1109 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1111 mention_notification_id =
1112 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1114 favorite_notification_id =
1115 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1117 reblog_notification_id =
1118 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1120 follow_notification_id =
1121 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1125 |> assign(:user, user)
1128 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1130 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1133 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1135 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1138 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1140 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1143 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1145 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1148 test "destroy multiple", %{conn: conn} do
1149 user = insert(:user)
1150 other_user = insert(:user)
1152 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1153 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1154 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1155 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1157 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1158 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1159 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1160 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1164 |> assign(:user, user)
1168 |> get("/api/v1/notifications")
1170 result = json_response(conn_res, 200)
1171 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1175 |> assign(:user, other_user)
1179 |> get("/api/v1/notifications")
1181 result = json_response(conn_res, 200)
1182 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1186 |> delete("/api/v1/notifications/destroy_multiple", %{
1187 "ids" => [notification1_id, notification2_id]
1190 assert json_response(conn_destroy, 200) == %{}
1194 |> get("/api/v1/notifications")
1196 result = json_response(conn_res, 200)
1197 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1200 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1201 user = insert(:user)
1202 user2 = insert(:user)
1204 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1205 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1207 conn = assign(conn, :user, user)
1209 conn = get(conn, "/api/v1/notifications")
1211 assert length(json_response(conn, 200)) == 1
1213 {:ok, user} = User.mute(user, user2)
1215 conn = assign(build_conn(), :user, user)
1216 conn = get(conn, "/api/v1/notifications")
1218 assert json_response(conn, 200) == []
1221 test "see notifications after muting user without notifications", %{conn: conn} do
1222 user = insert(:user)
1223 user2 = insert(:user)
1225 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1226 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1228 conn = assign(conn, :user, user)
1230 conn = get(conn, "/api/v1/notifications")
1232 assert length(json_response(conn, 200)) == 1
1234 {:ok, user} = User.mute(user, user2, false)
1236 conn = assign(build_conn(), :user, user)
1237 conn = get(conn, "/api/v1/notifications")
1239 assert length(json_response(conn, 200)) == 1
1242 test "see notifications after muting user with notifications and with_muted parameter", %{
1245 user = insert(:user)
1246 user2 = insert(:user)
1248 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1249 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1251 conn = assign(conn, :user, user)
1253 conn = get(conn, "/api/v1/notifications")
1255 assert length(json_response(conn, 200)) == 1
1257 {:ok, user} = User.mute(user, user2)
1259 conn = assign(build_conn(), :user, user)
1260 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1262 assert length(json_response(conn, 200)) == 1
1266 describe "reblogging" do
1267 test "reblogs and returns the reblogged status", %{conn: conn} do
1268 activity = insert(:note_activity)
1269 user = insert(:user)
1273 |> assign(:user, user)
1274 |> post("/api/v1/statuses/#{activity.id}/reblog")
1277 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1279 } = json_response(conn, 200)
1281 assert to_string(activity.id) == id
1284 test "reblogged status for another user", %{conn: conn} do
1285 activity = insert(:note_activity)
1286 user1 = insert(:user)
1287 user2 = insert(:user)
1288 user3 = insert(:user)
1289 CommonAPI.favorite(activity.id, user2)
1290 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1291 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1292 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1296 |> assign(:user, user3)
1297 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1300 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1301 "reblogged" => false,
1302 "favourited" => false,
1303 "bookmarked" => false
1304 } = json_response(conn_res, 200)
1308 |> assign(:user, user2)
1309 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1312 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1313 "reblogged" => true,
1314 "favourited" => true,
1315 "bookmarked" => true
1316 } = json_response(conn_res, 200)
1318 assert to_string(activity.id) == id
1321 test "returns 400 error when activity is not exist", %{conn: conn} do
1322 user = insert(:user)
1326 |> assign(:user, user)
1327 |> post("/api/v1/statuses/foo/reblog")
1329 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1333 describe "unreblogging" do
1334 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1335 activity = insert(:note_activity)
1336 user = insert(:user)
1338 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1342 |> assign(:user, user)
1343 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1345 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1347 assert to_string(activity.id) == id
1350 test "returns 400 error when activity is not exist", %{conn: conn} do
1351 user = insert(:user)
1355 |> assign(:user, user)
1356 |> post("/api/v1/statuses/foo/unreblog")
1358 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1362 describe "favoriting" do
1363 test "favs a status and returns it", %{conn: conn} do
1364 activity = insert(:note_activity)
1365 user = insert(:user)
1369 |> assign(:user, user)
1370 |> post("/api/v1/statuses/#{activity.id}/favourite")
1372 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1373 json_response(conn, 200)
1375 assert to_string(activity.id) == id
1378 test "returns 400 error for a wrong id", %{conn: conn} do
1379 user = insert(:user)
1383 |> assign(:user, user)
1384 |> post("/api/v1/statuses/1/favourite")
1386 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1390 describe "unfavoriting" do
1391 test "unfavorites a status and returns it", %{conn: conn} do
1392 activity = insert(:note_activity)
1393 user = insert(:user)
1395 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1399 |> assign(:user, user)
1400 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1402 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1403 json_response(conn, 200)
1405 assert to_string(activity.id) == id
1408 test "returns 400 error for a wrong id", %{conn: conn} do
1409 user = insert(:user)
1413 |> assign(:user, user)
1414 |> post("/api/v1/statuses/1/unfavourite")
1416 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1420 describe "user timelines" do
1421 test "gets a users statuses", %{conn: conn} do
1422 user_one = insert(:user)
1423 user_two = insert(:user)
1424 user_three = insert(:user)
1426 {:ok, user_three} = User.follow(user_three, user_one)
1428 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1430 {:ok, direct_activity} =
1431 CommonAPI.post(user_one, %{
1432 "status" => "Hi, @#{user_two.nickname}.",
1433 "visibility" => "direct"
1436 {:ok, private_activity} =
1437 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1441 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1443 assert [%{"id" => id}] = json_response(resp, 200)
1444 assert id == to_string(activity.id)
1448 |> assign(:user, user_two)
1449 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1451 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1452 assert id_one == to_string(direct_activity.id)
1453 assert id_two == to_string(activity.id)
1457 |> assign(:user, user_three)
1458 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1460 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1461 assert id_one == to_string(private_activity.id)
1462 assert id_two == to_string(activity.id)
1465 test "unimplemented pinned statuses feature", %{conn: conn} do
1466 note = insert(:note_activity)
1467 user = User.get_cached_by_ap_id(note.data["actor"])
1471 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1473 assert json_response(conn, 200) == []
1476 test "gets an users media", %{conn: conn} do
1477 note = insert(:note_activity)
1478 user = User.get_cached_by_ap_id(note.data["actor"])
1480 file = %Plug.Upload{
1481 content_type: "image/jpg",
1482 path: Path.absname("test/fixtures/image.jpg"),
1483 filename: "an_image.jpg"
1486 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1488 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1492 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1494 assert [%{"id" => id}] = json_response(conn, 200)
1495 assert id == to_string(image_post.id)
1499 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1501 assert [%{"id" => id}] = json_response(conn, 200)
1502 assert id == to_string(image_post.id)
1505 test "gets a user's statuses without reblogs", %{conn: conn} do
1506 user = insert(:user)
1507 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1508 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1512 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1514 assert [%{"id" => id}] = json_response(conn, 200)
1515 assert id == to_string(post.id)
1519 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1521 assert [%{"id" => id}] = json_response(conn, 200)
1522 assert id == to_string(post.id)
1525 test "filters user's statuses by a hashtag", %{conn: conn} do
1526 user = insert(:user)
1527 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1528 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1532 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1534 assert [%{"id" => id}] = json_response(conn, 200)
1535 assert id == to_string(post.id)
1539 describe "user relationships" do
1540 test "returns the relationships for the current user", %{conn: conn} do
1541 user = insert(:user)
1542 other_user = insert(:user)
1543 {:ok, user} = User.follow(user, other_user)
1547 |> assign(:user, user)
1548 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1550 assert [relationship] = json_response(conn, 200)
1552 assert to_string(other_user.id) == relationship["id"]
1556 describe "media upload" do
1558 user = insert(:user)
1562 |> assign(:user, user)
1564 image = %Plug.Upload{
1565 content_type: "image/jpg",
1566 path: Path.absname("test/fixtures/image.jpg"),
1567 filename: "an_image.jpg"
1570 [conn: conn, image: image]
1573 clear_config([:media_proxy])
1574 clear_config([Pleroma.Upload])
1576 test "returns uploaded image", %{conn: conn, image: image} do
1577 desc = "Description of the image"
1581 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1582 |> json_response(:ok)
1584 assert media["type"] == "image"
1585 assert media["description"] == desc
1588 object = Repo.get(Object, media["id"])
1589 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1593 describe "locked accounts" do
1594 test "/api/v1/follow_requests works" do
1595 user = insert(:user, %{info: %User.Info{locked: true}})
1596 other_user = insert(:user)
1598 {:ok, _activity} = ActivityPub.follow(other_user, user)
1600 user = User.get_cached_by_id(user.id)
1601 other_user = User.get_cached_by_id(other_user.id)
1603 assert User.following?(other_user, user) == false
1607 |> assign(:user, user)
1608 |> get("/api/v1/follow_requests")
1610 assert [relationship] = json_response(conn, 200)
1611 assert to_string(other_user.id) == relationship["id"]
1614 test "/api/v1/follow_requests/:id/authorize works" do
1615 user = insert(:user, %{info: %User.Info{locked: true}})
1616 other_user = insert(:user)
1618 {:ok, _activity} = ActivityPub.follow(other_user, user)
1620 user = User.get_cached_by_id(user.id)
1621 other_user = User.get_cached_by_id(other_user.id)
1623 assert User.following?(other_user, user) == false
1627 |> assign(:user, user)
1628 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1630 assert relationship = json_response(conn, 200)
1631 assert to_string(other_user.id) == relationship["id"]
1633 user = User.get_cached_by_id(user.id)
1634 other_user = User.get_cached_by_id(other_user.id)
1636 assert User.following?(other_user, user) == true
1639 test "verify_credentials", %{conn: conn} do
1640 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1644 |> assign(:user, user)
1645 |> get("/api/v1/accounts/verify_credentials")
1647 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1648 assert id == to_string(user.id)
1651 test "/api/v1/follow_requests/:id/reject works" do
1652 user = insert(:user, %{info: %User.Info{locked: true}})
1653 other_user = insert(:user)
1655 {:ok, _activity} = ActivityPub.follow(other_user, user)
1657 user = User.get_cached_by_id(user.id)
1661 |> assign(:user, user)
1662 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1664 assert relationship = json_response(conn, 200)
1665 assert to_string(other_user.id) == relationship["id"]
1667 user = User.get_cached_by_id(user.id)
1668 other_user = User.get_cached_by_id(other_user.id)
1670 assert User.following?(other_user, user) == false
1674 describe "account fetching" do
1675 test "works by id" do
1676 user = insert(:user)
1680 |> get("/api/v1/accounts/#{user.id}")
1682 assert %{"id" => id} = json_response(conn, 200)
1683 assert id == to_string(user.id)
1687 |> get("/api/v1/accounts/-1")
1689 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1692 test "works by nickname" do
1693 user = insert(:user)
1697 |> get("/api/v1/accounts/#{user.nickname}")
1699 assert %{"id" => id} = json_response(conn, 200)
1700 assert id == user.id
1703 test "works by nickname for remote users" do
1704 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1705 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1706 user = insert(:user, nickname: "user@example.com", local: false)
1710 |> get("/api/v1/accounts/#{user.nickname}")
1712 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1713 assert %{"id" => id} = json_response(conn, 200)
1714 assert id == user.id
1717 test "respects limit_to_local_content == :all for remote user nicknames" do
1718 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1719 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1721 user = insert(:user, nickname: "user@example.com", local: false)
1725 |> get("/api/v1/accounts/#{user.nickname}")
1727 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1728 assert json_response(conn, 404)
1731 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1732 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1733 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1735 user = insert(:user, nickname: "user@example.com", local: false)
1736 reading_user = insert(:user)
1740 |> get("/api/v1/accounts/#{user.nickname}")
1742 assert json_response(conn, 404)
1746 |> assign(:user, reading_user)
1747 |> get("/api/v1/accounts/#{user.nickname}")
1749 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1750 assert %{"id" => id} = json_response(conn, 200)
1751 assert id == user.id
1755 test "mascot upload", %{conn: conn} do
1756 user = insert(:user)
1758 non_image_file = %Plug.Upload{
1759 content_type: "audio/mpeg",
1760 path: Path.absname("test/fixtures/sound.mp3"),
1761 filename: "sound.mp3"
1766 |> assign(:user, user)
1767 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1769 assert json_response(conn, 415)
1771 file = %Plug.Upload{
1772 content_type: "image/jpg",
1773 path: Path.absname("test/fixtures/image.jpg"),
1774 filename: "an_image.jpg"
1779 |> assign(:user, user)
1780 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1782 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1785 test "mascot retrieving", %{conn: conn} do
1786 user = insert(:user)
1787 # When user hasn't set a mascot, we should just get pleroma tan back
1790 |> assign(:user, user)
1791 |> get("/api/v1/pleroma/mascot")
1793 assert %{"url" => url} = json_response(conn, 200)
1794 assert url =~ "pleroma-fox-tan-smol"
1796 # When a user sets their mascot, we should get that back
1797 file = %Plug.Upload{
1798 content_type: "image/jpg",
1799 path: Path.absname("test/fixtures/image.jpg"),
1800 filename: "an_image.jpg"
1805 |> assign(:user, user)
1806 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1808 assert json_response(conn, 200)
1810 user = User.get_cached_by_id(user.id)
1814 |> assign(:user, user)
1815 |> get("/api/v1/pleroma/mascot")
1817 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1818 assert url =~ "an_image"
1821 test "hashtag timeline", %{conn: conn} do
1822 following = insert(:user)
1825 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1827 {:ok, [_activity]} =
1828 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1832 |> get("/api/v1/timelines/tag/2hu")
1834 assert [%{"id" => id}] = json_response(nconn, 200)
1836 assert id == to_string(activity.id)
1838 # works for different capitalization too
1841 |> get("/api/v1/timelines/tag/2HU")
1843 assert [%{"id" => id}] = json_response(nconn, 200)
1845 assert id == to_string(activity.id)
1849 test "multi-hashtag timeline", %{conn: conn} do
1850 user = insert(:user)
1852 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1853 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1854 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1858 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1860 [status_none, status_test1, status_test] = json_response(any_test, 200)
1862 assert to_string(activity_test.id) == status_test["id"]
1863 assert to_string(activity_test1.id) == status_test1["id"]
1864 assert to_string(activity_none.id) == status_none["id"]
1868 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1870 assert [status_test1] == json_response(restricted_test, 200)
1872 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1874 assert [status_none] == json_response(all_test, 200)
1877 test "getting followers", %{conn: conn} do
1878 user = insert(:user)
1879 other_user = insert(:user)
1880 {:ok, user} = User.follow(user, other_user)
1884 |> get("/api/v1/accounts/#{other_user.id}/followers")
1886 assert [%{"id" => id}] = json_response(conn, 200)
1887 assert id == to_string(user.id)
1890 test "getting followers, hide_followers", %{conn: conn} do
1891 user = insert(:user)
1892 other_user = insert(:user, %{info: %{hide_followers: true}})
1893 {:ok, _user} = User.follow(user, other_user)
1897 |> get("/api/v1/accounts/#{other_user.id}/followers")
1899 assert [] == json_response(conn, 200)
1902 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1903 user = insert(:user)
1904 other_user = insert(:user, %{info: %{hide_followers: true}})
1905 {:ok, _user} = User.follow(user, other_user)
1909 |> assign(:user, other_user)
1910 |> get("/api/v1/accounts/#{other_user.id}/followers")
1912 refute [] == json_response(conn, 200)
1915 test "getting followers, pagination", %{conn: conn} do
1916 user = insert(:user)
1917 follower1 = insert(:user)
1918 follower2 = insert(:user)
1919 follower3 = insert(:user)
1920 {:ok, _} = User.follow(follower1, user)
1921 {:ok, _} = User.follow(follower2, user)
1922 {:ok, _} = User.follow(follower3, user)
1926 |> assign(:user, user)
1930 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1932 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1933 assert id3 == follower3.id
1934 assert id2 == follower2.id
1938 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1940 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1941 assert id2 == follower2.id
1942 assert id1 == follower1.id
1946 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1948 assert [%{"id" => id2}] = json_response(res_conn, 200)
1949 assert id2 == follower2.id
1951 assert [link_header] = get_resp_header(res_conn, "link")
1952 assert link_header =~ ~r/min_id=#{follower2.id}/
1953 assert link_header =~ ~r/max_id=#{follower2.id}/
1956 test "getting following", %{conn: conn} do
1957 user = insert(:user)
1958 other_user = insert(:user)
1959 {:ok, user} = User.follow(user, other_user)
1963 |> get("/api/v1/accounts/#{user.id}/following")
1965 assert [%{"id" => id}] = json_response(conn, 200)
1966 assert id == to_string(other_user.id)
1969 test "getting following, hide_follows", %{conn: conn} do
1970 user = insert(:user, %{info: %{hide_follows: true}})
1971 other_user = insert(:user)
1972 {:ok, user} = User.follow(user, other_user)
1976 |> get("/api/v1/accounts/#{user.id}/following")
1978 assert [] == json_response(conn, 200)
1981 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1982 user = insert(:user, %{info: %{hide_follows: true}})
1983 other_user = insert(:user)
1984 {:ok, user} = User.follow(user, other_user)
1988 |> assign(:user, user)
1989 |> get("/api/v1/accounts/#{user.id}/following")
1991 refute [] == json_response(conn, 200)
1994 test "getting following, pagination", %{conn: conn} do
1995 user = insert(:user)
1996 following1 = insert(:user)
1997 following2 = insert(:user)
1998 following3 = insert(:user)
1999 {:ok, _} = User.follow(user, following1)
2000 {:ok, _} = User.follow(user, following2)
2001 {:ok, _} = User.follow(user, following3)
2005 |> assign(:user, user)
2009 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2011 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2012 assert id3 == following3.id
2013 assert id2 == following2.id
2017 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2019 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2020 assert id2 == following2.id
2021 assert id1 == following1.id
2025 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2027 assert [%{"id" => id2}] = json_response(res_conn, 200)
2028 assert id2 == following2.id
2030 assert [link_header] = get_resp_header(res_conn, "link")
2031 assert link_header =~ ~r/min_id=#{following2.id}/
2032 assert link_header =~ ~r/max_id=#{following2.id}/
2035 test "following / unfollowing a user", %{conn: conn} do
2036 user = insert(:user)
2037 other_user = insert(:user)
2041 |> assign(:user, user)
2042 |> post("/api/v1/accounts/#{other_user.id}/follow")
2044 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2046 user = User.get_cached_by_id(user.id)
2050 |> assign(:user, user)
2051 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2053 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2055 user = User.get_cached_by_id(user.id)
2059 |> assign(:user, user)
2060 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2062 assert %{"id" => id} = json_response(conn, 200)
2063 assert id == to_string(other_user.id)
2066 test "following without reblogs" do
2067 follower = insert(:user)
2068 followed = insert(:user)
2069 other_user = insert(:user)
2073 |> assign(:user, follower)
2074 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2076 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2078 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2079 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2083 |> assign(:user, User.get_cached_by_id(follower.id))
2084 |> get("/api/v1/timelines/home")
2086 assert [] == json_response(conn, 200)
2090 |> assign(:user, follower)
2091 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2093 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2097 |> assign(:user, User.get_cached_by_id(follower.id))
2098 |> get("/api/v1/timelines/home")
2100 expected_activity_id = reblog.id
2101 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2104 test "following / unfollowing errors" do
2105 user = insert(:user)
2109 |> assign(:user, user)
2112 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2113 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2116 user = User.get_cached_by_id(user.id)
2117 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2118 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2120 # self follow via uri
2121 user = User.get_cached_by_id(user.id)
2122 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2123 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2125 # follow non existing user
2126 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2127 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2129 # follow non existing user via uri
2130 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2131 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2133 # unfollow non existing user
2134 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2135 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2138 describe "mute/unmute" do
2139 test "with notifications", %{conn: conn} do
2140 user = insert(:user)
2141 other_user = insert(:user)
2145 |> assign(:user, user)
2146 |> post("/api/v1/accounts/#{other_user.id}/mute")
2148 response = json_response(conn, 200)
2150 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2151 user = User.get_cached_by_id(user.id)
2155 |> assign(:user, user)
2156 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2158 response = json_response(conn, 200)
2159 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2162 test "without notifications", %{conn: conn} do
2163 user = insert(:user)
2164 other_user = insert(:user)
2168 |> assign(:user, user)
2169 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2171 response = json_response(conn, 200)
2173 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2174 user = User.get_cached_by_id(user.id)
2178 |> assign(:user, user)
2179 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2181 response = json_response(conn, 200)
2182 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2186 test "subscribing / unsubscribing to a user", %{conn: conn} do
2187 user = insert(:user)
2188 subscription_target = insert(:user)
2192 |> assign(:user, user)
2193 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2195 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2199 |> assign(:user, user)
2200 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2202 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2205 test "getting a list of mutes", %{conn: conn} do
2206 user = insert(:user)
2207 other_user = insert(:user)
2209 {:ok, user} = User.mute(user, other_user)
2213 |> assign(:user, user)
2214 |> get("/api/v1/mutes")
2216 other_user_id = to_string(other_user.id)
2217 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2220 test "blocking / unblocking a user", %{conn: conn} do
2221 user = insert(:user)
2222 other_user = insert(:user)
2226 |> assign(:user, user)
2227 |> post("/api/v1/accounts/#{other_user.id}/block")
2229 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2231 user = User.get_cached_by_id(user.id)
2235 |> assign(:user, user)
2236 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2238 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2241 test "getting a list of blocks", %{conn: conn} do
2242 user = insert(:user)
2243 other_user = insert(:user)
2245 {:ok, user} = User.block(user, other_user)
2249 |> assign(:user, user)
2250 |> get("/api/v1/blocks")
2252 other_user_id = to_string(other_user.id)
2253 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2256 test "blocking / unblocking a domain", %{conn: conn} do
2257 user = insert(:user)
2258 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2262 |> assign(:user, user)
2263 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2265 assert %{} = json_response(conn, 200)
2266 user = User.get_cached_by_ap_id(user.ap_id)
2267 assert User.blocks?(user, other_user)
2271 |> assign(:user, user)
2272 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2274 assert %{} = json_response(conn, 200)
2275 user = User.get_cached_by_ap_id(user.ap_id)
2276 refute User.blocks?(user, other_user)
2279 test "getting a list of domain blocks", %{conn: conn} do
2280 user = insert(:user)
2282 {:ok, user} = User.block_domain(user, "bad.site")
2283 {:ok, user} = User.block_domain(user, "even.worse.site")
2287 |> assign(:user, user)
2288 |> get("/api/v1/domain_blocks")
2290 domain_blocks = json_response(conn, 200)
2292 assert "bad.site" in domain_blocks
2293 assert "even.worse.site" in domain_blocks
2296 test "unimplemented follow_requests, blocks, domain blocks" do
2297 user = insert(:user)
2299 ["blocks", "domain_blocks", "follow_requests"]
2300 |> Enum.each(fn endpoint ->
2303 |> assign(:user, user)
2304 |> get("/api/v1/#{endpoint}")
2306 assert [] = json_response(conn, 200)
2310 test "returns the favorites of a user", %{conn: conn} do
2311 user = insert(:user)
2312 other_user = insert(:user)
2314 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2315 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2317 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2321 |> assign(:user, user)
2322 |> get("/api/v1/favourites")
2324 assert [status] = json_response(first_conn, 200)
2325 assert status["id"] == to_string(activity.id)
2327 assert [{"link", _link_header}] =
2328 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2330 # Honours query params
2331 {:ok, second_activity} =
2332 CommonAPI.post(other_user, %{
2334 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2337 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2339 last_like = status["id"]
2343 |> assign(:user, user)
2344 |> get("/api/v1/favourites?since_id=#{last_like}")
2346 assert [second_status] = json_response(second_conn, 200)
2347 assert second_status["id"] == to_string(second_activity.id)
2351 |> assign(:user, user)
2352 |> get("/api/v1/favourites?limit=0")
2354 assert [] = json_response(third_conn, 200)
2357 describe "getting favorites timeline of specified user" do
2359 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2360 [current_user: current_user, user: user]
2363 test "returns list of statuses favorited by specified user", %{
2365 current_user: current_user,
2368 [activity | _] = insert_pair(:note_activity)
2369 CommonAPI.favorite(activity.id, user)
2373 |> assign(:user, current_user)
2374 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2375 |> json_response(:ok)
2379 assert length(response) == 1
2380 assert like["id"] == activity.id
2383 test "returns favorites for specified user_id when user is not logged in", %{
2387 activity = insert(:note_activity)
2388 CommonAPI.favorite(activity.id, user)
2392 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2393 |> json_response(:ok)
2395 assert length(response) == 1
2398 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2400 current_user: current_user,
2404 CommonAPI.post(current_user, %{
2405 "status" => "Hi @#{user.nickname}!",
2406 "visibility" => "direct"
2409 CommonAPI.favorite(direct.id, user)
2413 |> assign(:user, current_user)
2414 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2415 |> json_response(:ok)
2417 assert length(response) == 1
2419 anonymous_response =
2421 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2422 |> json_response(:ok)
2424 assert Enum.empty?(anonymous_response)
2427 test "does not return others' favorited DM when user is not one of recipients", %{
2429 current_user: current_user,
2432 user_two = insert(:user)
2435 CommonAPI.post(user_two, %{
2436 "status" => "Hi @#{user.nickname}!",
2437 "visibility" => "direct"
2440 CommonAPI.favorite(direct.id, user)
2444 |> assign(:user, current_user)
2445 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2446 |> json_response(:ok)
2448 assert Enum.empty?(response)
2451 test "paginates favorites using since_id and max_id", %{
2453 current_user: current_user,
2456 activities = insert_list(10, :note_activity)
2458 Enum.each(activities, fn activity ->
2459 CommonAPI.favorite(activity.id, user)
2462 third_activity = Enum.at(activities, 2)
2463 seventh_activity = Enum.at(activities, 6)
2467 |> assign(:user, current_user)
2468 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2469 since_id: third_activity.id,
2470 max_id: seventh_activity.id
2472 |> json_response(:ok)
2474 assert length(response) == 3
2475 refute third_activity in response
2476 refute seventh_activity in response
2479 test "limits favorites using limit parameter", %{
2481 current_user: current_user,
2485 |> insert_list(:note_activity)
2486 |> Enum.each(fn activity ->
2487 CommonAPI.favorite(activity.id, user)
2492 |> assign(:user, current_user)
2493 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2494 |> json_response(:ok)
2496 assert length(response) == 3
2499 test "returns empty response when user does not have any favorited statuses", %{
2501 current_user: current_user,
2506 |> assign(:user, current_user)
2507 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2508 |> json_response(:ok)
2510 assert Enum.empty?(response)
2513 test "returns 404 error when specified user is not exist", %{conn: conn} do
2514 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2516 assert json_response(conn, 404) == %{"error" => "Record not found"}
2519 test "returns 403 error when user has hidden own favorites", %{
2521 current_user: current_user
2523 user = insert(:user, %{info: %{hide_favorites: true}})
2524 activity = insert(:note_activity)
2525 CommonAPI.favorite(activity.id, user)
2529 |> assign(:user, current_user)
2530 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2532 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2535 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2536 user = insert(:user)
2537 activity = insert(:note_activity)
2538 CommonAPI.favorite(activity.id, user)
2542 |> assign(:user, current_user)
2543 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2545 assert user.info.hide_favorites
2546 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2550 test "get instance information", %{conn: conn} do
2551 conn = get(conn, "/api/v1/instance")
2552 assert result = json_response(conn, 200)
2554 email = Config.get([:instance, :email])
2555 # Note: not checking for "max_toot_chars" since it's optional
2561 "email" => from_config_email,
2563 "streaming_api" => _
2568 "registrations" => _,
2572 assert email == from_config_email
2575 test "get instance stats", %{conn: conn} do
2576 user = insert(:user, %{local: true})
2578 user2 = insert(:user, %{local: true})
2579 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2581 insert(:user, %{local: false, nickname: "u@peer1.com"})
2582 insert(:user, %{local: false, nickname: "u@peer2.com"})
2584 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2586 # Stats should count users with missing or nil `info.deactivated` value
2587 user = User.get_cached_by_id(user.id)
2588 info_change = Changeset.change(user.info, %{deactivated: nil})
2592 |> Changeset.change()
2593 |> Changeset.put_embed(:info, info_change)
2594 |> User.update_and_set_cache()
2596 Pleroma.Stats.force_update()
2598 conn = get(conn, "/api/v1/instance")
2600 assert result = json_response(conn, 200)
2602 stats = result["stats"]
2605 assert stats["user_count"] == 1
2606 assert stats["status_count"] == 1
2607 assert stats["domain_count"] == 2
2610 test "get peers", %{conn: conn} do
2611 insert(:user, %{local: false, nickname: "u@peer1.com"})
2612 insert(:user, %{local: false, nickname: "u@peer2.com"})
2614 Pleroma.Stats.force_update()
2616 conn = get(conn, "/api/v1/instance/peers")
2618 assert result = json_response(conn, 200)
2620 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2623 test "put settings", %{conn: conn} do
2624 user = insert(:user)
2628 |> assign(:user, user)
2629 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2631 assert _result = json_response(conn, 200)
2633 user = User.get_cached_by_ap_id(user.ap_id)
2634 assert user.info.settings == %{"programming" => "socks"}
2637 describe "pinned statuses" do
2639 user = insert(:user)
2640 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2642 [user: user, activity: activity]
2645 clear_config([:instance, :max_pinned_statuses]) do
2646 Config.put([:instance, :max_pinned_statuses], 1)
2649 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2650 {:ok, _} = CommonAPI.pin(activity.id, user)
2654 |> assign(:user, user)
2655 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2656 |> json_response(200)
2658 id_str = to_string(activity.id)
2660 assert [%{"id" => ^id_str, "pinned" => true}] = result
2663 test "pin status", %{conn: conn, user: user, activity: activity} do
2664 id_str = to_string(activity.id)
2666 assert %{"id" => ^id_str, "pinned" => true} =
2668 |> assign(:user, user)
2669 |> post("/api/v1/statuses/#{activity.id}/pin")
2670 |> json_response(200)
2672 assert [%{"id" => ^id_str, "pinned" => true}] =
2674 |> assign(:user, user)
2675 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2676 |> json_response(200)
2679 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2680 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2684 |> assign(:user, user)
2685 |> post("/api/v1/statuses/#{dm.id}/pin")
2687 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2690 test "unpin status", %{conn: conn, user: user, activity: activity} do
2691 {:ok, _} = CommonAPI.pin(activity.id, user)
2693 id_str = to_string(activity.id)
2694 user = refresh_record(user)
2696 assert %{"id" => ^id_str, "pinned" => false} =
2698 |> assign(:user, user)
2699 |> post("/api/v1/statuses/#{activity.id}/unpin")
2700 |> json_response(200)
2704 |> assign(:user, user)
2705 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2706 |> json_response(200)
2709 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2712 |> assign(:user, user)
2713 |> post("/api/v1/statuses/1/unpin")
2715 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2718 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2719 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2721 id_str_one = to_string(activity_one.id)
2723 assert %{"id" => ^id_str_one, "pinned" => true} =
2725 |> assign(:user, user)
2726 |> post("/api/v1/statuses/#{id_str_one}/pin")
2727 |> json_response(200)
2729 user = refresh_record(user)
2731 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2733 |> assign(:user, user)
2734 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2735 |> json_response(400)
2741 Config.put([:rich_media, :enabled], true)
2743 user = insert(:user)
2747 test "returns rich-media card", %{conn: conn, user: user} do
2748 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2751 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2752 "provider_name" => "example.com",
2753 "provider_url" => "https://example.com",
2754 "title" => "The Rock",
2756 "url" => "https://example.com/ogp",
2758 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2761 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2762 "title" => "The Rock",
2763 "type" => "video.movie",
2764 "url" => "https://example.com/ogp",
2766 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2773 |> get("/api/v1/statuses/#{activity.id}/card")
2774 |> json_response(200)
2776 assert response == card_data
2778 # works with private posts
2780 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2784 |> assign(:user, user)
2785 |> get("/api/v1/statuses/#{activity.id}/card")
2786 |> json_response(200)
2788 assert response_two == card_data
2791 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2793 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2797 |> get("/api/v1/statuses/#{activity.id}/card")
2798 |> json_response(:ok)
2800 assert response == %{
2802 "title" => "Pleroma",
2803 "description" => "",
2805 "provider_name" => "example.com",
2806 "provider_url" => "https://example.com",
2807 "url" => "https://example.com/ogp-missing-data",
2810 "title" => "Pleroma",
2811 "type" => "website",
2812 "url" => "https://example.com/ogp-missing-data"
2820 user = insert(:user)
2821 for_user = insert(:user)
2824 CommonAPI.post(user, %{
2825 "status" => "heweoo?"
2829 CommonAPI.post(user, %{
2830 "status" => "heweoo!"
2835 |> assign(:user, for_user)
2836 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2838 assert json_response(response1, 200)["bookmarked"] == true
2842 |> assign(:user, for_user)
2843 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2845 assert json_response(response2, 200)["bookmarked"] == true
2849 |> assign(:user, for_user)
2850 |> get("/api/v1/bookmarks")
2852 assert [json_response(response2, 200), json_response(response1, 200)] ==
2853 json_response(bookmarks, 200)
2857 |> assign(:user, for_user)
2858 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2860 assert json_response(response1, 200)["bookmarked"] == false
2864 |> assign(:user, for_user)
2865 |> get("/api/v1/bookmarks")
2867 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2870 describe "conversation muting" do
2872 post_user = insert(:user)
2873 user = insert(:user)
2875 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2877 [user: user, activity: activity]
2880 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2881 id_str = to_string(activity.id)
2883 assert %{"id" => ^id_str, "muted" => true} =
2885 |> assign(:user, user)
2886 |> post("/api/v1/statuses/#{activity.id}/mute")
2887 |> json_response(200)
2890 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2891 {:ok, _} = CommonAPI.add_mute(user, activity)
2895 |> assign(:user, user)
2896 |> post("/api/v1/statuses/#{activity.id}/mute")
2898 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2901 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2902 {:ok, _} = CommonAPI.add_mute(user, activity)
2904 id_str = to_string(activity.id)
2905 user = refresh_record(user)
2907 assert %{"id" => ^id_str, "muted" => false} =
2909 |> assign(:user, user)
2910 |> post("/api/v1/statuses/#{activity.id}/unmute")
2911 |> json_response(200)
2915 describe "reports" do
2917 reporter = insert(:user)
2918 target_user = insert(:user)
2920 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2922 [reporter: reporter, target_user: target_user, activity: activity]
2925 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2926 assert %{"action_taken" => false, "id" => _} =
2928 |> assign(:user, reporter)
2929 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2930 |> json_response(200)
2933 test "submit a report with statuses and comment", %{
2936 target_user: target_user,
2939 assert %{"action_taken" => false, "id" => _} =
2941 |> assign(:user, reporter)
2942 |> post("/api/v1/reports", %{
2943 "account_id" => target_user.id,
2944 "status_ids" => [activity.id],
2945 "comment" => "bad status!",
2946 "forward" => "false"
2948 |> json_response(200)
2951 test "account_id is required", %{
2956 assert %{"error" => "Valid `account_id` required"} =
2958 |> assign(:user, reporter)
2959 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2960 |> json_response(400)
2963 test "comment must be up to the size specified in the config", %{
2966 target_user: target_user
2968 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2969 comment = String.pad_trailing("a", max_size + 1, "a")
2971 error = %{"error" => "Comment must be up to #{max_size} characters"}
2975 |> assign(:user, reporter)
2976 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2977 |> json_response(400)
2980 test "returns error when account is not exist", %{
2987 |> assign(:user, reporter)
2988 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
2990 assert json_response(conn, 400) == %{"error" => "Account not found"}
2994 describe "link headers" do
2995 test "preserves parameters in link headers", %{conn: conn} do
2996 user = insert(:user)
2997 other_user = insert(:user)
3000 CommonAPI.post(other_user, %{
3001 "status" => "hi @#{user.nickname}",
3002 "visibility" => "public"
3006 CommonAPI.post(other_user, %{
3007 "status" => "hi @#{user.nickname}",
3008 "visibility" => "public"
3011 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3012 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3016 |> assign(:user, user)
3017 |> get("/api/v1/notifications", %{media_only: true})
3019 assert [link_header] = get_resp_header(conn, "link")
3020 assert link_header =~ ~r/media_only=true/
3021 assert link_header =~ ~r/min_id=#{notification2.id}/
3022 assert link_header =~ ~r/max_id=#{notification1.id}/
3026 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3027 # Need to set an old-style integer ID to reproduce the problem
3028 # (these are no longer assigned to new accounts but were preserved
3029 # for existing accounts during the migration to flakeIDs)
3030 user_one = insert(:user, %{id: 1212})
3031 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3035 |> get("/api/v1/accounts/#{user_one.id}")
3039 |> get("/api/v1/accounts/#{user_two.nickname}")
3043 |> get("/api/v1/accounts/#{user_two.id}")
3045 acc_one = json_response(resp_one, 200)
3046 acc_two = json_response(resp_two, 200)
3047 acc_three = json_response(resp_three, 200)
3048 refute acc_one == acc_two
3049 assert acc_two == acc_three
3052 describe "custom emoji" do
3053 test "with tags", %{conn: conn} do
3056 |> get("/api/v1/custom_emojis")
3057 |> json_response(200)
3059 assert Map.has_key?(emoji, "shortcode")
3060 assert Map.has_key?(emoji, "static_url")
3061 assert Map.has_key?(emoji, "tags")
3062 assert is_list(emoji["tags"])
3063 assert Map.has_key?(emoji, "category")
3064 assert Map.has_key?(emoji, "url")
3065 assert Map.has_key?(emoji, "visible_in_picker")
3069 describe "index/2 redirections" do
3070 setup %{conn: conn} do
3074 signing_salt: "cooldude"
3079 |> Plug.Session.call(Plug.Session.init(session_opts))
3082 test_path = "/web/statuses/test"
3083 %{conn: conn, path: test_path}
3086 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3087 conn = get(conn, path)
3089 assert conn.status == 302
3090 assert redirected_to(conn) == "/web/login"
3093 test "redirects not logged-in users to the login page on private instances", %{
3097 Config.put([:instance, :public], false)
3099 conn = get(conn, path)
3101 assert conn.status == 302
3102 assert redirected_to(conn) == "/web/login"
3105 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3106 token = insert(:oauth_token)
3110 |> assign(:user, token.user)
3111 |> put_session(:oauth_token, token.token)
3114 assert conn.status == 200
3117 test "saves referer path to session", %{conn: conn, path: path} do
3118 conn = get(conn, path)
3119 return_to = Plug.Conn.get_session(conn, :return_to)
3121 assert return_to == path
3124 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3125 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3126 auth = insert(:oauth_authorization, app: app)
3130 |> put_session(:return_to, path)
3131 |> get("/web/login", %{code: auth.token})
3133 assert conn.status == 302
3134 assert redirected_to(conn) == path
3137 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3138 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3139 auth = insert(:oauth_authorization, app: app)
3141 conn = get(conn, "/web/login", %{code: auth.token})
3143 assert conn.status == 302
3144 assert redirected_to(conn) == "/web/getting-started"
3148 describe "scheduled activities" do
3149 test "creates a scheduled activity", %{conn: conn} do
3150 user = insert(:user)
3151 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3155 |> assign(:user, user)
3156 |> post("/api/v1/statuses", %{
3157 "status" => "scheduled",
3158 "scheduled_at" => scheduled_at
3161 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3162 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3163 assert [] == Repo.all(Activity)
3166 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3167 user = insert(:user)
3168 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3170 file = %Plug.Upload{
3171 content_type: "image/jpg",
3172 path: Path.absname("test/fixtures/image.jpg"),
3173 filename: "an_image.jpg"
3176 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3180 |> assign(:user, user)
3181 |> post("/api/v1/statuses", %{
3182 "media_ids" => [to_string(upload.id)],
3183 "status" => "scheduled",
3184 "scheduled_at" => scheduled_at
3187 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3188 assert %{"type" => "image"} = media_attachment
3191 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3193 user = insert(:user)
3196 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3200 |> assign(:user, user)
3201 |> post("/api/v1/statuses", %{
3202 "status" => "not scheduled",
3203 "scheduled_at" => scheduled_at
3206 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3207 assert [] == Repo.all(ScheduledActivity)
3210 test "returns error when daily user limit is exceeded", %{conn: conn} do
3211 user = insert(:user)
3214 NaiveDateTime.utc_now()
3215 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3216 |> NaiveDateTime.to_iso8601()
3218 attrs = %{params: %{}, scheduled_at: today}
3219 {:ok, _} = ScheduledActivity.create(user, attrs)
3220 {:ok, _} = ScheduledActivity.create(user, attrs)
3224 |> assign(:user, user)
3225 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3227 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3230 test "returns error when total user limit is exceeded", %{conn: conn} do
3231 user = insert(:user)
3234 NaiveDateTime.utc_now()
3235 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3236 |> NaiveDateTime.to_iso8601()
3239 NaiveDateTime.utc_now()
3240 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3241 |> NaiveDateTime.to_iso8601()
3243 attrs = %{params: %{}, scheduled_at: today}
3244 {:ok, _} = ScheduledActivity.create(user, attrs)
3245 {:ok, _} = ScheduledActivity.create(user, attrs)
3246 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3250 |> assign(:user, user)
3251 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3253 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3256 test "shows scheduled activities", %{conn: conn} do
3257 user = insert(:user)
3258 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3259 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3260 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3261 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3265 |> assign(:user, user)
3270 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3272 result = json_response(conn_res, 200)
3273 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3278 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3280 result = json_response(conn_res, 200)
3281 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3286 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3288 result = json_response(conn_res, 200)
3289 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3292 test "shows a scheduled activity", %{conn: conn} do
3293 user = insert(:user)
3294 scheduled_activity = insert(:scheduled_activity, user: user)
3298 |> assign(:user, user)
3299 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3301 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3302 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3306 |> assign(:user, user)
3307 |> get("/api/v1/scheduled_statuses/404")
3309 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3312 test "updates a scheduled activity", %{conn: conn} do
3313 user = insert(:user)
3314 scheduled_activity = insert(:scheduled_activity, user: user)
3317 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3321 |> assign(:user, user)
3322 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3323 scheduled_at: new_scheduled_at
3326 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3327 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3331 |> assign(:user, user)
3332 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3334 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3337 test "deletes a scheduled activity", %{conn: conn} do
3338 user = insert(:user)
3339 scheduled_activity = insert(:scheduled_activity, user: user)
3343 |> assign(:user, user)
3344 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3346 assert %{} = json_response(res_conn, 200)
3347 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3351 |> assign(:user, user)
3352 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3354 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3358 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3359 user1 = insert(:user)
3360 user2 = insert(:user)
3361 user3 = insert(:user)
3363 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3365 # Reply to status from another user
3368 |> assign(:user, user2)
3369 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3371 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3373 activity = Activity.get_by_id_with_object(id)
3375 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3376 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3378 # Reblog from the third user
3381 |> assign(:user, user3)
3382 |> post("/api/v1/statuses/#{activity.id}/reblog")
3384 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3385 json_response(conn2, 200)
3387 assert to_string(activity.id) == id
3389 # Getting third user status
3392 |> assign(:user, user3)
3393 |> get("api/v1/timelines/home")
3395 [reblogged_activity] = json_response(conn3, 200)
3397 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3399 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3400 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3403 describe "create account by app" do
3404 test "Account registration via Application", %{conn: conn} do
3407 |> post("/api/v1/apps", %{
3408 client_name: "client_name",
3409 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3410 scopes: "read, write, follow"
3414 "client_id" => client_id,
3415 "client_secret" => client_secret,
3417 "name" => "client_name",
3418 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3421 } = json_response(conn, 200)
3425 |> post("/oauth/token", %{
3426 grant_type: "client_credentials",
3427 client_id: client_id,
3428 client_secret: client_secret
3431 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3432 json_response(conn, 200)
3435 token_from_db = Repo.get_by(Token, token: token)
3436 assert token_from_db
3438 assert scope == "read write follow"
3442 |> put_req_header("authorization", "Bearer " <> token)
3443 |> post("/api/v1/accounts", %{
3445 email: "lain@example.org",
3446 password: "PlzDontHackLain",
3451 "access_token" => token,
3452 "created_at" => _created_at,
3454 "token_type" => "Bearer"
3455 } = json_response(conn, 200)
3457 token_from_db = Repo.get_by(Token, token: token)
3458 assert token_from_db
3459 token_from_db = Repo.preload(token_from_db, :user)
3460 assert token_from_db.user
3462 assert token_from_db.user.info.confirmation_pending
3465 test "rate limit", %{conn: conn} do
3466 app_token = insert(:oauth_token, user: nil)
3469 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3470 |> Map.put(:remote_ip, {15, 15, 15, 15})
3475 |> post("/api/v1/accounts", %{
3476 username: "#{i}lain",
3477 email: "#{i}lain@example.org",
3478 password: "PlzDontHackLain",
3483 "access_token" => token,
3484 "created_at" => _created_at,
3486 "token_type" => "Bearer"
3487 } = json_response(conn, 200)
3489 token_from_db = Repo.get_by(Token, token: token)
3490 assert token_from_db
3491 token_from_db = Repo.preload(token_from_db, :user)
3492 assert token_from_db.user
3494 assert token_from_db.user.info.confirmation_pending
3499 |> post("/api/v1/accounts", %{
3501 email: "6lain@example.org",
3502 password: "PlzDontHackLain",
3506 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3510 describe "GET /api/v1/polls/:id" do
3511 test "returns poll entity for object id", %{conn: conn} do
3512 user = insert(:user)
3515 CommonAPI.post(user, %{
3516 "status" => "Pleroma does",
3517 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3520 object = Object.normalize(activity)
3524 |> assign(:user, user)
3525 |> get("/api/v1/polls/#{object.id}")
3527 response = json_response(conn, 200)
3528 id = to_string(object.id)
3529 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3532 test "does not expose polls for private statuses", %{conn: conn} do
3533 user = insert(:user)
3534 other_user = insert(:user)
3537 CommonAPI.post(user, %{
3538 "status" => "Pleroma does",
3539 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3540 "visibility" => "private"
3543 object = Object.normalize(activity)
3547 |> assign(:user, other_user)
3548 |> get("/api/v1/polls/#{object.id}")
3550 assert json_response(conn, 404)
3554 describe "POST /api/v1/polls/:id/votes" do
3555 test "votes are added to the poll", %{conn: conn} do
3556 user = insert(:user)
3557 other_user = insert(:user)
3560 CommonAPI.post(user, %{
3561 "status" => "A very delicious sandwich",
3563 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3569 object = Object.normalize(activity)
3573 |> assign(:user, other_user)
3574 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3576 assert json_response(conn, 200)
3577 object = Object.get_by_id(object.id)
3579 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3584 test "author can't vote", %{conn: conn} do
3585 user = insert(:user)
3588 CommonAPI.post(user, %{
3589 "status" => "Am I cute?",
3590 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3593 object = Object.normalize(activity)
3596 |> assign(:user, user)
3597 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3598 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3600 object = Object.get_by_id(object.id)
3602 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3605 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3606 user = insert(:user)
3607 other_user = insert(:user)
3610 CommonAPI.post(user, %{
3611 "status" => "The glass is",
3612 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3615 object = Object.normalize(activity)
3618 |> assign(:user, other_user)
3619 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3620 |> json_response(422) == %{"error" => "Too many choices"}
3622 object = Object.get_by_id(object.id)
3624 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3629 test "does not allow choice index to be greater than options count", %{conn: conn} do
3630 user = insert(:user)
3631 other_user = insert(:user)
3634 CommonAPI.post(user, %{
3635 "status" => "Am I cute?",
3636 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3639 object = Object.normalize(activity)
3643 |> assign(:user, other_user)
3644 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3646 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3649 test "returns 404 error when object is not exist", %{conn: conn} do
3650 user = insert(:user)
3654 |> assign(:user, user)
3655 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3657 assert json_response(conn, 404) == %{"error" => "Record not found"}
3660 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3661 user = insert(:user)
3662 other_user = insert(:user)
3665 CommonAPI.post(user, %{
3666 "status" => "Am I cute?",
3667 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3668 "visibility" => "private"
3671 object = Object.normalize(activity)
3675 |> assign(:user, other_user)
3676 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3678 assert json_response(conn, 404) == %{"error" => "Record not found"}
3682 describe "GET /api/v1/statuses/:id/favourited_by" do
3684 user = insert(:user)
3685 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3689 |> assign(:user, user)
3691 [conn: conn, activity: activity]
3694 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3695 other_user = insert(:user)
3696 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3700 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3701 |> json_response(:ok)
3703 [%{"id" => id}] = response
3705 assert id == other_user.id
3708 test "returns empty array when status has not been favorited yet", %{
3714 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3715 |> json_response(:ok)
3717 assert Enum.empty?(response)
3720 test "does not return users who have favorited the status but are blocked", %{
3721 conn: %{assigns: %{user: user}} = conn,
3724 other_user = insert(:user)
3725 {:ok, user} = User.block(user, other_user)
3727 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3731 |> assign(:user, user)
3732 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3733 |> json_response(:ok)
3735 assert Enum.empty?(response)
3738 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3739 other_user = insert(:user)
3740 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3744 |> assign(:user, nil)
3745 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3746 |> json_response(:ok)
3748 [%{"id" => id}] = response
3749 assert id == other_user.id
3753 describe "GET /api/v1/statuses/:id/reblogged_by" do
3755 user = insert(:user)
3756 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3760 |> assign(:user, user)
3762 [conn: conn, activity: activity]
3765 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3766 other_user = insert(:user)
3767 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3771 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3772 |> json_response(:ok)
3774 [%{"id" => id}] = response
3776 assert id == other_user.id
3779 test "returns empty array when status has not been reblogged yet", %{
3785 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3786 |> json_response(:ok)
3788 assert Enum.empty?(response)
3791 test "does not return users who have reblogged the status but are blocked", %{
3792 conn: %{assigns: %{user: user}} = conn,
3795 other_user = insert(:user)
3796 {:ok, user} = User.block(user, other_user)
3798 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3802 |> assign(:user, user)
3803 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3804 |> json_response(:ok)
3806 assert Enum.empty?(response)
3809 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3810 other_user = insert(:user)
3811 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3815 |> assign(:user, nil)
3816 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3817 |> json_response(:ok)
3819 [%{"id" => id}] = response
3820 assert id == other_user.id
3824 describe "POST /auth/password, with valid parameters" do
3825 setup %{conn: conn} do
3826 user = insert(:user)
3827 conn = post(conn, "/auth/password?email=#{user.email}")
3828 %{conn: conn, user: user}
3831 test "it returns 204", %{conn: conn} do
3832 assert json_response(conn, :no_content)
3835 test "it creates a PasswordResetToken record for user", %{user: user} do
3836 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3840 test "it sends an email to user", %{user: user} do
3841 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3843 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3844 notify_email = Config.get([:instance, :notify_email])
3845 instance_name = Config.get([:instance, :name])
3848 from: {instance_name, notify_email},
3849 to: {user.name, user.email},
3850 html_body: email.html_body
3855 describe "POST /auth/password, with invalid parameters" do
3857 user = insert(:user)
3861 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3862 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3863 assert conn.status == 404
3864 assert conn.resp_body == ""
3867 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3868 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3869 conn = post(conn, "/auth/password?email=#{user.email}")
3870 assert conn.status == 400
3871 assert conn.resp_body == ""
3875 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3877 user = insert(:user)
3878 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3882 |> Changeset.change()
3883 |> Changeset.put_embed(:info, info_change)
3886 assert user.info.confirmation_pending
3891 clear_config([:instance, :account_activation_required]) do
3892 Config.put([:instance, :account_activation_required], true)
3895 test "resend account confirmation email", %{conn: conn, user: user} do
3897 |> assign(:user, user)
3898 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3899 |> json_response(:no_content)
3901 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3902 notify_email = Config.get([:instance, :notify_email])
3903 instance_name = Config.get([:instance, :name])
3906 from: {instance_name, notify_email},
3907 to: {user.name, user.email},
3908 html_body: email.html_body
3913 describe "GET /api/v1/suggestions" do
3915 user = insert(:user)
3916 other_user = insert(:user)
3917 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3918 url500 = "http://test500?#{host}&#{user.nickname}"
3919 url200 = "http://test200?#{host}&#{user.nickname}"
3922 %{method: :get, url: ^url500} ->
3923 %Tesla.Env{status: 500, body: "bad request"}
3925 %{method: :get, url: ^url200} ->
3929 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3931 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3935 [user: user, other_user: other_user]
3938 clear_config(:suggestions)
3940 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3941 Config.put([:suggestions, :enabled], false)
3945 |> assign(:user, user)
3946 |> get("/api/v1/suggestions")
3947 |> json_response(200)
3952 test "returns error", %{conn: conn, user: user} do
3953 Config.put([:suggestions, :enabled], true)
3954 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
3958 |> assign(:user, user)
3959 |> get("/api/v1/suggestions")
3960 |> json_response(500)
3962 assert res == "Something went wrong"
3965 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
3966 Config.put([:suggestions, :enabled], true)
3967 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
3971 |> assign(:user, user)
3972 |> get("/api/v1/suggestions")
3973 |> json_response(200)
3978 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
3979 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
3983 "acct" => other_user.ap_id,
3984 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
3985 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
3986 "id" => other_user.id