1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.ActivityExpiration
12 alias Pleroma.Notification
15 alias Pleroma.ScheduledActivity
16 alias Pleroma.Tests.ObanHelpers
18 alias Pleroma.Web.ActivityPub.ActivityPub
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.MastodonAPI.FilterView
21 alias Pleroma.Web.OAuth.App
22 alias Pleroma.Web.OAuth.Token
23 alias Pleroma.Web.OStatus
24 alias Pleroma.Web.Push
25 import Pleroma.Factory
26 import ExUnit.CaptureLog
28 import Swoosh.TestAssertions
30 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
33 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
37 clear_config([:instance, :public])
38 clear_config([:rich_media, :enabled])
40 test "the home timeline", %{conn: conn} do
42 following = insert(:user)
44 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
51 assert Enum.empty?(json_response(conn, 200))
53 {:ok, user} = User.follow(user, following)
57 |> assign(:user, user)
58 |> get("/api/v1/timelines/home")
60 assert [%{"content" => "test"}] = json_response(conn, 200)
63 test "the public timeline", %{conn: conn} do
64 following = insert(:user)
67 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
70 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
74 |> get("/api/v1/timelines/public", %{"local" => "False"})
76 assert length(json_response(conn, 200)) == 2
80 |> get("/api/v1/timelines/public", %{"local" => "True"})
82 assert [%{"content" => "test"}] = json_response(conn, 200)
86 |> get("/api/v1/timelines/public", %{"local" => "1"})
88 assert [%{"content" => "test"}] = json_response(conn, 200)
92 test "the public timeline when public is set to false", %{conn: conn} do
93 Config.put([:instance, :public], false)
96 |> get("/api/v1/timelines/public", %{"local" => "False"})
97 |> json_response(403) == %{"error" => "This resource requires authentication."}
100 describe "posting statuses" do
106 |> assign(:user, user)
111 test "posting a status", %{conn: conn} do
112 idempotency_key = "Pikachu rocks!"
116 |> put_req_header("idempotency-key", idempotency_key)
117 |> post("/api/v1/statuses", %{
119 "spoiler_text" => "2hu",
120 "sensitive" => "false"
123 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
125 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
127 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
128 json_response(conn_one, 200)
130 assert Activity.get_by_id(id)
134 |> put_req_header("idempotency-key", idempotency_key)
135 |> post("/api/v1/statuses", %{
137 "spoiler_text" => "2hu",
138 "sensitive" => "false"
141 assert %{"id" => second_id} = json_response(conn_two, 200)
142 assert id == second_id
146 |> post("/api/v1/statuses", %{
148 "spoiler_text" => "2hu",
149 "sensitive" => "false"
152 assert %{"id" => third_id} = json_response(conn_three, 200)
153 refute id == third_id
155 # An activity that will expire:
157 expires_in = 120 * 60
161 |> post("api/v1/statuses", %{
162 "status" => "oolong",
163 "expires_in" => expires_in
166 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
167 assert activity = Activity.get_by_id(fourth_id)
168 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
170 estimated_expires_at =
171 NaiveDateTime.utc_now()
172 |> NaiveDateTime.add(expires_in)
173 |> NaiveDateTime.truncate(:second)
175 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
176 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
178 assert fourth_response["pleroma"]["expires_at"] ==
179 NaiveDateTime.to_iso8601(expiration.scheduled_at)
182 test "replying to a status", %{conn: conn} do
184 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
188 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
190 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
192 activity = Activity.get_by_id(id)
194 assert activity.data["context"] == replied_to.data["context"]
195 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
198 test "replying to a direct message with visibility other than direct", %{conn: conn} do
200 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
202 Enum.each(["public", "private", "unlisted"], fn visibility ->
205 |> post("/api/v1/statuses", %{
206 "status" => "@#{user.nickname} hey",
207 "in_reply_to_id" => replied_to.id,
208 "visibility" => visibility
211 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
215 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
218 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
220 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
221 assert Activity.get_by_id(id)
224 test "posting a sensitive status", %{conn: conn} do
227 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
229 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
230 assert Activity.get_by_id(id)
233 test "posting a fake status", %{conn: conn} do
236 |> post("/api/v1/statuses", %{
238 "\"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"
241 real_status = json_response(real_conn, 200)
244 assert Object.get_by_ap_id(real_status["uri"])
248 |> Map.put("id", nil)
249 |> Map.put("url", nil)
250 |> Map.put("uri", nil)
251 |> Map.put("created_at", nil)
252 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
256 |> post("/api/v1/statuses", %{
258 "\"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",
262 fake_status = json_response(fake_conn, 200)
265 refute Object.get_by_ap_id(fake_status["uri"])
269 |> Map.put("id", nil)
270 |> Map.put("url", nil)
271 |> Map.put("uri", nil)
272 |> Map.put("created_at", nil)
273 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
275 assert real_status == fake_status
278 test "posting a status with OGP link preview", %{conn: conn} do
279 Config.put([:rich_media, :enabled], true)
283 |> post("/api/v1/statuses", %{
284 "status" => "https://example.com/ogp"
287 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
288 assert Activity.get_by_id(id)
291 test "posting a direct status", %{conn: conn} do
292 user2 = insert(:user)
293 content = "direct cofe @#{user2.nickname}"
297 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
299 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
300 assert activity = Activity.get_by_id(id)
301 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
302 assert activity.data["to"] == [user2.ap_id]
303 assert activity.data["cc"] == []
307 describe "posting polls" do
308 test "posting a poll", %{conn: conn} do
310 time = NaiveDateTime.utc_now()
314 |> assign(:user, user)
315 |> post("/api/v1/statuses", %{
316 "status" => "Who is the #bestgrill?",
317 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
320 response = json_response(conn, 200)
322 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
323 title in ["Rei", "Asuka", "Misato"]
326 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
327 refute response["poll"]["expred"]
330 test "option limit is enforced", %{conn: conn} do
332 limit = Config.get([:instance, :poll_limits, :max_options])
336 |> assign(:user, user)
337 |> post("/api/v1/statuses", %{
339 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
342 %{"error" => error} = json_response(conn, 422)
343 assert error == "Poll can't contain more than #{limit} options"
346 test "option character limit is enforced", %{conn: conn} do
348 limit = Config.get([:instance, :poll_limits, :max_option_chars])
352 |> assign(:user, user)
353 |> post("/api/v1/statuses", %{
356 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
361 %{"error" => error} = json_response(conn, 422)
362 assert error == "Poll options cannot be longer than #{limit} characters each"
365 test "minimal date limit is enforced", %{conn: conn} do
367 limit = Config.get([:instance, :poll_limits, :min_expiration])
371 |> assign(:user, user)
372 |> post("/api/v1/statuses", %{
373 "status" => "imagine arbitrary limits",
375 "options" => ["this post was made by pleroma gang"],
376 "expires_in" => limit - 1
380 %{"error" => error} = json_response(conn, 422)
381 assert error == "Expiration date is too soon"
384 test "maximum date limit is enforced", %{conn: conn} do
386 limit = Config.get([:instance, :poll_limits, :max_expiration])
390 |> assign(:user, user)
391 |> post("/api/v1/statuses", %{
392 "status" => "imagine arbitrary limits",
394 "options" => ["this post was made by pleroma gang"],
395 "expires_in" => limit + 1
399 %{"error" => error} = json_response(conn, 422)
400 assert error == "Expiration date is too far in the future"
404 test "direct timeline", %{conn: conn} do
405 user_one = insert(:user)
406 user_two = insert(:user)
408 {:ok, user_two} = User.follow(user_two, user_one)
411 CommonAPI.post(user_one, %{
412 "status" => "Hi @#{user_two.nickname}!",
413 "visibility" => "direct"
416 {:ok, _follower_only} =
417 CommonAPI.post(user_one, %{
418 "status" => "Hi @#{user_two.nickname}!",
419 "visibility" => "private"
422 # Only direct should be visible here
425 |> assign(:user, user_two)
426 |> get("api/v1/timelines/direct")
428 [status] = json_response(res_conn, 200)
430 assert %{"visibility" => "direct"} = status
431 assert status["url"] != direct.data["id"]
433 # User should be able to see their own direct message
436 |> assign(:user, user_one)
437 |> get("api/v1/timelines/direct")
439 [status] = json_response(res_conn, 200)
441 assert %{"visibility" => "direct"} = status
443 # Both should be visible here
446 |> assign(:user, user_two)
447 |> get("api/v1/timelines/home")
449 [_s1, _s2] = json_response(res_conn, 200)
452 Enum.each(1..20, fn _ ->
454 CommonAPI.post(user_one, %{
455 "status" => "Hi @#{user_two.nickname}!",
456 "visibility" => "direct"
462 |> assign(:user, user_two)
463 |> get("api/v1/timelines/direct")
465 statuses = json_response(res_conn, 200)
466 assert length(statuses) == 20
470 |> assign(:user, user_two)
471 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
473 [status] = json_response(res_conn, 200)
475 assert status["url"] != direct.data["id"]
478 test "Conversations", %{conn: conn} do
479 user_one = insert(:user)
480 user_two = insert(:user)
481 user_three = insert(:user)
483 {:ok, user_two} = User.follow(user_two, user_one)
486 CommonAPI.post(user_one, %{
487 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
488 "visibility" => "direct"
491 {:ok, _follower_only} =
492 CommonAPI.post(user_one, %{
493 "status" => "Hi @#{user_two.nickname}!",
494 "visibility" => "private"
499 |> assign(:user, user_one)
500 |> get("/api/v1/conversations")
502 assert response = json_response(res_conn, 200)
507 "accounts" => res_accounts,
508 "last_status" => res_last_status,
513 account_ids = Enum.map(res_accounts, & &1["id"])
514 assert length(res_accounts) == 2
515 assert user_two.id in account_ids
516 assert user_three.id in account_ids
517 assert is_binary(res_id)
518 assert unread == true
519 assert res_last_status["id"] == direct.id
521 # Apparently undocumented API endpoint
524 |> assign(:user, user_one)
525 |> post("/api/v1/conversations/#{res_id}/read")
527 assert response = json_response(res_conn, 200)
528 assert length(response["accounts"]) == 2
529 assert response["last_status"]["id"] == direct.id
530 assert response["unread"] == false
532 # (vanilla) Mastodon frontend behaviour
535 |> assign(:user, user_one)
536 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
538 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
541 test "doesn't include DMs from blocked users", %{conn: conn} do
542 blocker = insert(:user)
543 blocked = insert(:user)
545 {:ok, blocker} = User.block(blocker, blocked)
547 {:ok, _blocked_direct} =
548 CommonAPI.post(blocked, %{
549 "status" => "Hi @#{blocker.nickname}!",
550 "visibility" => "direct"
554 CommonAPI.post(user, %{
555 "status" => "Hi @#{blocker.nickname}!",
556 "visibility" => "direct"
561 |> assign(:user, user)
562 |> get("api/v1/timelines/direct")
564 [status] = json_response(res_conn, 200)
565 assert status["id"] == direct.id
568 test "verify_credentials", %{conn: conn} do
573 |> assign(:user, user)
574 |> get("/api/v1/accounts/verify_credentials")
576 response = json_response(conn, 200)
578 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
579 assert response["pleroma"]["chat_token"]
580 assert id == to_string(user.id)
583 test "verify_credentials default scope unlisted", %{conn: conn} do
584 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
588 |> assign(:user, user)
589 |> get("/api/v1/accounts/verify_credentials")
591 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
592 assert id == to_string(user.id)
595 test "apps/verify_credentials", %{conn: conn} do
596 token = insert(:oauth_token)
600 |> assign(:user, token.user)
601 |> assign(:token, token)
602 |> get("/api/v1/apps/verify_credentials")
604 app = Repo.preload(token, :app).app
607 "name" => app.client_name,
608 "website" => app.website,
609 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
612 assert expected == json_response(conn, 200)
615 test "user avatar can be set", %{conn: conn} do
617 avatar_image = File.read!("test/fixtures/avatar_data_uri")
621 |> assign(:user, user)
622 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
624 user = refresh_record(user)
638 assert %{"url" => _} = json_response(conn, 200)
641 test "user avatar can be reset", %{conn: conn} do
646 |> assign(:user, user)
647 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
649 user = User.get_cached_by_id(user.id)
651 assert user.avatar == nil
653 assert %{"url" => nil} = json_response(conn, 200)
656 test "can set profile banner", %{conn: conn} do
661 |> assign(:user, user)
662 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
664 user = refresh_record(user)
665 assert user.info.banner["type"] == "Image"
667 assert %{"url" => _} = json_response(conn, 200)
670 test "can reset profile banner", %{conn: conn} do
675 |> assign(:user, user)
676 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
678 user = refresh_record(user)
679 assert user.info.banner == %{}
681 assert %{"url" => nil} = json_response(conn, 200)
684 test "background image can be set", %{conn: conn} do
689 |> assign(:user, user)
690 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
692 user = refresh_record(user)
693 assert user.info.background["type"] == "Image"
694 assert %{"url" => _} = json_response(conn, 200)
697 test "background image can be reset", %{conn: conn} do
702 |> assign(:user, user)
703 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
705 user = refresh_record(user)
706 assert user.info.background == %{}
707 assert %{"url" => nil} = json_response(conn, 200)
710 test "creates an oauth app", %{conn: conn} do
712 app_attrs = build(:oauth_app)
716 |> assign(:user, user)
717 |> post("/api/v1/apps", %{
718 client_name: app_attrs.client_name,
719 redirect_uris: app_attrs.redirect_uris
722 [app] = Repo.all(App)
725 "name" => app.client_name,
726 "website" => app.website,
727 "client_id" => app.client_id,
728 "client_secret" => app.client_secret,
729 "id" => app.id |> to_string(),
730 "redirect_uri" => app.redirect_uris,
731 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
734 assert expected == json_response(conn, 200)
737 test "get a status", %{conn: conn} do
738 activity = insert(:note_activity)
742 |> get("/api/v1/statuses/#{activity.id}")
744 assert %{"id" => id} = json_response(conn, 200)
745 assert id == to_string(activity.id)
748 test "get statuses by IDs", %{conn: conn} do
749 %{id: id1} = insert(:note_activity)
750 %{id: id2} = insert(:note_activity)
752 query_string = "ids[]=#{id1}&ids[]=#{id2}"
753 conn = get(conn, "/api/v1/statuses/?#{query_string}")
755 assert [%{"id" => ^id1}, %{"id" => ^id2}] = json_response(conn, :ok)
758 describe "deleting a status" do
759 test "when you created it", %{conn: conn} do
760 activity = insert(:note_activity)
761 author = User.get_cached_by_ap_id(activity.data["actor"])
765 |> assign(:user, author)
766 |> delete("/api/v1/statuses/#{activity.id}")
768 assert %{} = json_response(conn, 200)
770 refute Activity.get_by_id(activity.id)
773 test "when you didn't create it", %{conn: conn} do
774 activity = insert(:note_activity)
779 |> assign(:user, user)
780 |> delete("/api/v1/statuses/#{activity.id}")
782 assert %{"error" => _} = json_response(conn, 403)
784 assert Activity.get_by_id(activity.id) == activity
787 test "when you're an admin or moderator", %{conn: conn} do
788 activity1 = insert(:note_activity)
789 activity2 = insert(:note_activity)
790 admin = insert(:user, info: %{is_admin: true})
791 moderator = insert(:user, info: %{is_moderator: true})
795 |> assign(:user, admin)
796 |> delete("/api/v1/statuses/#{activity1.id}")
798 assert %{} = json_response(res_conn, 200)
802 |> assign(:user, moderator)
803 |> delete("/api/v1/statuses/#{activity2.id}")
805 assert %{} = json_response(res_conn, 200)
807 refute Activity.get_by_id(activity1.id)
808 refute Activity.get_by_id(activity2.id)
812 describe "filters" do
813 test "creating a filter", %{conn: conn} do
816 filter = %Pleroma.Filter{
823 |> assign(:user, user)
824 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
826 assert response = json_response(conn, 200)
827 assert response["phrase"] == filter.phrase
828 assert response["context"] == filter.context
829 assert response["irreversible"] == false
830 assert response["id"] != nil
831 assert response["id"] != ""
834 test "fetching a list of filters", %{conn: conn} do
837 query_one = %Pleroma.Filter{
844 query_two = %Pleroma.Filter{
851 {:ok, filter_one} = Pleroma.Filter.create(query_one)
852 {:ok, filter_two} = Pleroma.Filter.create(query_two)
856 |> assign(:user, user)
857 |> get("/api/v1/filters")
858 |> json_response(200)
864 filters: [filter_two, filter_one]
868 test "get a filter", %{conn: conn} do
871 query = %Pleroma.Filter{
878 {:ok, filter} = Pleroma.Filter.create(query)
882 |> assign(:user, user)
883 |> get("/api/v1/filters/#{filter.filter_id}")
885 assert _response = json_response(conn, 200)
888 test "update a filter", %{conn: conn} do
891 query = %Pleroma.Filter{
898 {:ok, _filter} = Pleroma.Filter.create(query)
900 new = %Pleroma.Filter{
907 |> assign(:user, user)
908 |> put("/api/v1/filters/#{query.filter_id}", %{
913 assert response = json_response(conn, 200)
914 assert response["phrase"] == new.phrase
915 assert response["context"] == new.context
918 test "delete a filter", %{conn: conn} do
921 query = %Pleroma.Filter{
928 {:ok, filter} = Pleroma.Filter.create(query)
932 |> assign(:user, user)
933 |> delete("/api/v1/filters/#{filter.filter_id}")
935 assert response = json_response(conn, 200)
936 assert response == %{}
940 describe "list timelines" do
941 test "list timeline", %{conn: conn} do
943 other_user = insert(:user)
944 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
945 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
946 {:ok, list} = Pleroma.List.create("name", user)
947 {:ok, list} = Pleroma.List.follow(list, other_user)
951 |> assign(:user, user)
952 |> get("/api/v1/timelines/list/#{list.id}")
954 assert [%{"id" => id}] = json_response(conn, 200)
956 assert id == to_string(activity_two.id)
959 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
961 other_user = insert(:user)
962 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
964 {:ok, _activity_two} =
965 CommonAPI.post(other_user, %{
966 "status" => "Marisa is cute.",
967 "visibility" => "private"
970 {:ok, list} = Pleroma.List.create("name", user)
971 {:ok, list} = Pleroma.List.follow(list, other_user)
975 |> assign(:user, user)
976 |> get("/api/v1/timelines/list/#{list.id}")
978 assert [%{"id" => id}] = json_response(conn, 200)
980 assert id == to_string(activity_one.id)
984 describe "notifications" do
985 test "list of notifications", %{conn: conn} do
987 other_user = insert(:user)
989 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
991 {:ok, [_notification]} = Notification.create_notifications(activity)
995 |> assign(:user, user)
996 |> get("/api/v1/notifications")
999 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1001 }\">@<span>#{user.nickname}</span></a></span>"
1003 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1004 assert response == expected_response
1007 test "getting a single notification", %{conn: conn} do
1008 user = insert(:user)
1009 other_user = insert(:user)
1011 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1013 {:ok, [notification]} = Notification.create_notifications(activity)
1017 |> assign(:user, user)
1018 |> get("/api/v1/notifications/#{notification.id}")
1021 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1023 }\">@<span>#{user.nickname}</span></a></span>"
1025 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1026 assert response == expected_response
1029 test "dismissing a single notification", %{conn: conn} do
1030 user = insert(:user)
1031 other_user = insert(:user)
1033 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1035 {:ok, [notification]} = Notification.create_notifications(activity)
1039 |> assign(:user, user)
1040 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1042 assert %{} = json_response(conn, 200)
1045 test "clearing all notifications", %{conn: conn} do
1046 user = insert(:user)
1047 other_user = insert(:user)
1049 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1051 {:ok, [_notification]} = Notification.create_notifications(activity)
1055 |> assign(:user, user)
1056 |> post("/api/v1/notifications/clear")
1058 assert %{} = json_response(conn, 200)
1062 |> assign(:user, user)
1063 |> get("/api/v1/notifications")
1065 assert all = json_response(conn, 200)
1069 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1070 user = insert(:user)
1071 other_user = insert(:user)
1073 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1074 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1075 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1076 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1078 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1079 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1080 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1081 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1085 |> assign(:user, user)
1090 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1092 result = json_response(conn_res, 200)
1093 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1098 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1100 result = json_response(conn_res, 200)
1101 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1106 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1108 result = json_response(conn_res, 200)
1109 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1112 test "filters notifications using exclude_types", %{conn: conn} do
1113 user = insert(:user)
1114 other_user = insert(:user)
1116 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1117 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1118 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1119 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1120 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1122 mention_notification_id =
1123 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1125 favorite_notification_id =
1126 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1128 reblog_notification_id =
1129 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1131 follow_notification_id =
1132 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1136 |> assign(:user, user)
1139 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1141 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1144 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1146 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1149 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1151 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1154 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1156 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1159 test "destroy multiple", %{conn: conn} do
1160 user = insert(:user)
1161 other_user = insert(:user)
1163 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1164 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1165 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1166 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1168 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1169 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1170 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1171 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1175 |> assign(:user, user)
1179 |> get("/api/v1/notifications")
1181 result = json_response(conn_res, 200)
1182 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1186 |> assign(:user, other_user)
1190 |> get("/api/v1/notifications")
1192 result = json_response(conn_res, 200)
1193 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1197 |> delete("/api/v1/notifications/destroy_multiple", %{
1198 "ids" => [notification1_id, notification2_id]
1201 assert json_response(conn_destroy, 200) == %{}
1205 |> get("/api/v1/notifications")
1207 result = json_response(conn_res, 200)
1208 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1211 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1212 user = insert(:user)
1213 user2 = insert(:user)
1215 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1216 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1218 conn = assign(conn, :user, user)
1220 conn = get(conn, "/api/v1/notifications")
1222 assert length(json_response(conn, 200)) == 1
1224 {:ok, user} = User.mute(user, user2)
1226 conn = assign(build_conn(), :user, user)
1227 conn = get(conn, "/api/v1/notifications")
1229 assert json_response(conn, 200) == []
1232 test "see notifications after muting user without notifications", %{conn: conn} do
1233 user = insert(:user)
1234 user2 = insert(:user)
1236 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1237 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1239 conn = assign(conn, :user, user)
1241 conn = get(conn, "/api/v1/notifications")
1243 assert length(json_response(conn, 200)) == 1
1245 {:ok, user} = User.mute(user, user2, false)
1247 conn = assign(build_conn(), :user, user)
1248 conn = get(conn, "/api/v1/notifications")
1250 assert length(json_response(conn, 200)) == 1
1253 test "see notifications after muting user with notifications and with_muted parameter", %{
1256 user = insert(:user)
1257 user2 = insert(:user)
1259 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1260 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1262 conn = assign(conn, :user, user)
1264 conn = get(conn, "/api/v1/notifications")
1266 assert length(json_response(conn, 200)) == 1
1268 {:ok, user} = User.mute(user, user2)
1270 conn = assign(build_conn(), :user, user)
1271 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1273 assert length(json_response(conn, 200)) == 1
1277 describe "reblogging" do
1278 test "reblogs and returns the reblogged status", %{conn: conn} do
1279 activity = insert(:note_activity)
1280 user = insert(:user)
1284 |> assign(:user, user)
1285 |> post("/api/v1/statuses/#{activity.id}/reblog")
1288 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1290 } = json_response(conn, 200)
1292 assert to_string(activity.id) == id
1295 test "reblogged status for another user", %{conn: conn} do
1296 activity = insert(:note_activity)
1297 user1 = insert(:user)
1298 user2 = insert(:user)
1299 user3 = insert(:user)
1300 CommonAPI.favorite(activity.id, user2)
1301 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1302 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1303 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1307 |> assign(:user, user3)
1308 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1311 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1312 "reblogged" => false,
1313 "favourited" => false,
1314 "bookmarked" => false
1315 } = json_response(conn_res, 200)
1319 |> assign(:user, user2)
1320 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1323 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1324 "reblogged" => true,
1325 "favourited" => true,
1326 "bookmarked" => true
1327 } = json_response(conn_res, 200)
1329 assert to_string(activity.id) == id
1332 test "returns 400 error when activity is not exist", %{conn: conn} do
1333 user = insert(:user)
1337 |> assign(:user, user)
1338 |> post("/api/v1/statuses/foo/reblog")
1340 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1344 describe "unreblogging" do
1345 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1346 activity = insert(:note_activity)
1347 user = insert(:user)
1349 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1353 |> assign(:user, user)
1354 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1356 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1358 assert to_string(activity.id) == id
1361 test "returns 400 error when activity is not exist", %{conn: conn} do
1362 user = insert(:user)
1366 |> assign(:user, user)
1367 |> post("/api/v1/statuses/foo/unreblog")
1369 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1373 describe "favoriting" do
1374 test "favs a status and returns it", %{conn: conn} do
1375 activity = insert(:note_activity)
1376 user = insert(:user)
1380 |> assign(:user, user)
1381 |> post("/api/v1/statuses/#{activity.id}/favourite")
1383 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1384 json_response(conn, 200)
1386 assert to_string(activity.id) == id
1389 test "returns 400 error for a wrong id", %{conn: conn} do
1390 user = insert(:user)
1394 |> assign(:user, user)
1395 |> post("/api/v1/statuses/1/favourite")
1397 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1401 describe "unfavoriting" do
1402 test "unfavorites a status and returns it", %{conn: conn} do
1403 activity = insert(:note_activity)
1404 user = insert(:user)
1406 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1410 |> assign(:user, user)
1411 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1413 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1414 json_response(conn, 200)
1416 assert to_string(activity.id) == id
1419 test "returns 400 error for a wrong id", %{conn: conn} do
1420 user = insert(:user)
1424 |> assign(:user, user)
1425 |> post("/api/v1/statuses/1/unfavourite")
1427 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1431 describe "user timelines" do
1432 test "gets a users statuses", %{conn: conn} do
1433 user_one = insert(:user)
1434 user_two = insert(:user)
1435 user_three = insert(:user)
1437 {:ok, user_three} = User.follow(user_three, user_one)
1439 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1441 {:ok, direct_activity} =
1442 CommonAPI.post(user_one, %{
1443 "status" => "Hi, @#{user_two.nickname}.",
1444 "visibility" => "direct"
1447 {:ok, private_activity} =
1448 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1452 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1454 assert [%{"id" => id}] = json_response(resp, 200)
1455 assert id == to_string(activity.id)
1459 |> assign(:user, user_two)
1460 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1462 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1463 assert id_one == to_string(direct_activity.id)
1464 assert id_two == to_string(activity.id)
1468 |> assign(:user, user_three)
1469 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1471 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1472 assert id_one == to_string(private_activity.id)
1473 assert id_two == to_string(activity.id)
1476 test "unimplemented pinned statuses feature", %{conn: conn} do
1477 note = insert(:note_activity)
1478 user = User.get_cached_by_ap_id(note.data["actor"])
1482 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1484 assert json_response(conn, 200) == []
1487 test "gets an users media", %{conn: conn} do
1488 note = insert(:note_activity)
1489 user = User.get_cached_by_ap_id(note.data["actor"])
1491 file = %Plug.Upload{
1492 content_type: "image/jpg",
1493 path: Path.absname("test/fixtures/image.jpg"),
1494 filename: "an_image.jpg"
1497 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1499 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1503 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1505 assert [%{"id" => id}] = json_response(conn, 200)
1506 assert id == to_string(image_post.id)
1510 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1512 assert [%{"id" => id}] = json_response(conn, 200)
1513 assert id == to_string(image_post.id)
1516 test "gets a user's statuses without reblogs", %{conn: conn} do
1517 user = insert(:user)
1518 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1519 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1523 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1525 assert [%{"id" => id}] = json_response(conn, 200)
1526 assert id == to_string(post.id)
1530 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1532 assert [%{"id" => id}] = json_response(conn, 200)
1533 assert id == to_string(post.id)
1536 test "filters user's statuses by a hashtag", %{conn: conn} do
1537 user = insert(:user)
1538 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1539 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1543 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1545 assert [%{"id" => id}] = json_response(conn, 200)
1546 assert id == to_string(post.id)
1550 describe "user relationships" do
1551 test "returns the relationships for the current user", %{conn: conn} do
1552 user = insert(:user)
1553 other_user = insert(:user)
1554 {:ok, user} = User.follow(user, other_user)
1558 |> assign(:user, user)
1559 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1561 assert [relationship] = json_response(conn, 200)
1563 assert to_string(other_user.id) == relationship["id"]
1567 describe "media upload" do
1569 user = insert(:user)
1573 |> assign(:user, user)
1575 image = %Plug.Upload{
1576 content_type: "image/jpg",
1577 path: Path.absname("test/fixtures/image.jpg"),
1578 filename: "an_image.jpg"
1581 [conn: conn, image: image]
1584 clear_config([:media_proxy])
1585 clear_config([Pleroma.Upload])
1587 test "returns uploaded image", %{conn: conn, image: image} do
1588 desc = "Description of the image"
1592 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1593 |> json_response(:ok)
1595 assert media["type"] == "image"
1596 assert media["description"] == desc
1599 object = Repo.get(Object, media["id"])
1600 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1604 describe "locked accounts" do
1605 test "/api/v1/follow_requests works" do
1606 user = insert(:user, %{info: %User.Info{locked: true}})
1607 other_user = insert(:user)
1609 {:ok, _activity} = ActivityPub.follow(other_user, user)
1611 user = User.get_cached_by_id(user.id)
1612 other_user = User.get_cached_by_id(other_user.id)
1614 assert User.following?(other_user, user) == false
1618 |> assign(:user, user)
1619 |> get("/api/v1/follow_requests")
1621 assert [relationship] = json_response(conn, 200)
1622 assert to_string(other_user.id) == relationship["id"]
1625 test "/api/v1/follow_requests/:id/authorize works" do
1626 user = insert(:user, %{info: %User.Info{locked: true}})
1627 other_user = insert(:user)
1629 {:ok, _activity} = ActivityPub.follow(other_user, user)
1631 user = User.get_cached_by_id(user.id)
1632 other_user = User.get_cached_by_id(other_user.id)
1634 assert User.following?(other_user, user) == false
1638 |> assign(:user, user)
1639 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1641 assert relationship = json_response(conn, 200)
1642 assert to_string(other_user.id) == relationship["id"]
1644 user = User.get_cached_by_id(user.id)
1645 other_user = User.get_cached_by_id(other_user.id)
1647 assert User.following?(other_user, user) == true
1650 test "verify_credentials", %{conn: conn} do
1651 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1655 |> assign(:user, user)
1656 |> get("/api/v1/accounts/verify_credentials")
1658 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1659 assert id == to_string(user.id)
1662 test "/api/v1/follow_requests/:id/reject works" do
1663 user = insert(:user, %{info: %User.Info{locked: true}})
1664 other_user = insert(:user)
1666 {:ok, _activity} = ActivityPub.follow(other_user, user)
1668 user = User.get_cached_by_id(user.id)
1672 |> assign(:user, user)
1673 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1675 assert relationship = json_response(conn, 200)
1676 assert to_string(other_user.id) == relationship["id"]
1678 user = User.get_cached_by_id(user.id)
1679 other_user = User.get_cached_by_id(other_user.id)
1681 assert User.following?(other_user, user) == false
1685 describe "account fetching" do
1686 test "works by id" do
1687 user = insert(:user)
1691 |> get("/api/v1/accounts/#{user.id}")
1693 assert %{"id" => id} = json_response(conn, 200)
1694 assert id == to_string(user.id)
1698 |> get("/api/v1/accounts/-1")
1700 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1703 test "works by nickname" do
1704 user = insert(:user)
1708 |> get("/api/v1/accounts/#{user.nickname}")
1710 assert %{"id" => id} = json_response(conn, 200)
1711 assert id == user.id
1714 test "works by nickname for remote users" do
1715 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1716 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1717 user = insert(:user, nickname: "user@example.com", local: false)
1721 |> get("/api/v1/accounts/#{user.nickname}")
1723 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1724 assert %{"id" => id} = json_response(conn, 200)
1725 assert id == user.id
1728 test "respects limit_to_local_content == :all for remote user nicknames" do
1729 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1730 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1732 user = insert(:user, nickname: "user@example.com", local: false)
1736 |> get("/api/v1/accounts/#{user.nickname}")
1738 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1739 assert json_response(conn, 404)
1742 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1743 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1744 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1746 user = insert(:user, nickname: "user@example.com", local: false)
1747 reading_user = insert(:user)
1751 |> get("/api/v1/accounts/#{user.nickname}")
1753 assert json_response(conn, 404)
1757 |> assign(:user, reading_user)
1758 |> get("/api/v1/accounts/#{user.nickname}")
1760 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1761 assert %{"id" => id} = json_response(conn, 200)
1762 assert id == user.id
1766 test "mascot upload", %{conn: conn} do
1767 user = insert(:user)
1769 non_image_file = %Plug.Upload{
1770 content_type: "audio/mpeg",
1771 path: Path.absname("test/fixtures/sound.mp3"),
1772 filename: "sound.mp3"
1777 |> assign(:user, user)
1778 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1780 assert json_response(conn, 415)
1782 file = %Plug.Upload{
1783 content_type: "image/jpg",
1784 path: Path.absname("test/fixtures/image.jpg"),
1785 filename: "an_image.jpg"
1790 |> assign(:user, user)
1791 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1793 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1796 test "mascot retrieving", %{conn: conn} do
1797 user = insert(:user)
1798 # When user hasn't set a mascot, we should just get pleroma tan back
1801 |> assign(:user, user)
1802 |> get("/api/v1/pleroma/mascot")
1804 assert %{"url" => url} = json_response(conn, 200)
1805 assert url =~ "pleroma-fox-tan-smol"
1807 # When a user sets their mascot, we should get that back
1808 file = %Plug.Upload{
1809 content_type: "image/jpg",
1810 path: Path.absname("test/fixtures/image.jpg"),
1811 filename: "an_image.jpg"
1816 |> assign(:user, user)
1817 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1819 assert json_response(conn, 200)
1821 user = User.get_cached_by_id(user.id)
1825 |> assign(:user, user)
1826 |> get("/api/v1/pleroma/mascot")
1828 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1829 assert url =~ "an_image"
1832 test "hashtag timeline", %{conn: conn} do
1833 following = insert(:user)
1836 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1838 {:ok, [_activity]} =
1839 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1843 |> get("/api/v1/timelines/tag/2hu")
1845 assert [%{"id" => id}] = json_response(nconn, 200)
1847 assert id == to_string(activity.id)
1849 # works for different capitalization too
1852 |> get("/api/v1/timelines/tag/2HU")
1854 assert [%{"id" => id}] = json_response(nconn, 200)
1856 assert id == to_string(activity.id)
1860 test "multi-hashtag timeline", %{conn: conn} do
1861 user = insert(:user)
1863 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1864 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1865 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1869 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1871 [status_none, status_test1, status_test] = json_response(any_test, 200)
1873 assert to_string(activity_test.id) == status_test["id"]
1874 assert to_string(activity_test1.id) == status_test1["id"]
1875 assert to_string(activity_none.id) == status_none["id"]
1879 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1881 assert [status_test1] == json_response(restricted_test, 200)
1883 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1885 assert [status_none] == json_response(all_test, 200)
1888 test "getting followers", %{conn: conn} do
1889 user = insert(:user)
1890 other_user = insert(:user)
1891 {:ok, user} = User.follow(user, other_user)
1895 |> get("/api/v1/accounts/#{other_user.id}/followers")
1897 assert [%{"id" => id}] = json_response(conn, 200)
1898 assert id == to_string(user.id)
1901 test "getting followers, hide_followers", %{conn: conn} do
1902 user = insert(:user)
1903 other_user = insert(:user, %{info: %{hide_followers: true}})
1904 {:ok, _user} = User.follow(user, other_user)
1908 |> get("/api/v1/accounts/#{other_user.id}/followers")
1910 assert [] == json_response(conn, 200)
1913 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1914 user = insert(:user)
1915 other_user = insert(:user, %{info: %{hide_followers: true}})
1916 {:ok, _user} = User.follow(user, other_user)
1920 |> assign(:user, other_user)
1921 |> get("/api/v1/accounts/#{other_user.id}/followers")
1923 refute [] == json_response(conn, 200)
1926 test "getting followers, pagination", %{conn: conn} do
1927 user = insert(:user)
1928 follower1 = insert(:user)
1929 follower2 = insert(:user)
1930 follower3 = insert(:user)
1931 {:ok, _} = User.follow(follower1, user)
1932 {:ok, _} = User.follow(follower2, user)
1933 {:ok, _} = User.follow(follower3, user)
1937 |> assign(:user, user)
1941 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1943 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1944 assert id3 == follower3.id
1945 assert id2 == follower2.id
1949 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1951 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1952 assert id2 == follower2.id
1953 assert id1 == follower1.id
1957 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1959 assert [%{"id" => id2}] = json_response(res_conn, 200)
1960 assert id2 == follower2.id
1962 assert [link_header] = get_resp_header(res_conn, "link")
1963 assert link_header =~ ~r/min_id=#{follower2.id}/
1964 assert link_header =~ ~r/max_id=#{follower2.id}/
1967 test "getting following", %{conn: conn} do
1968 user = insert(:user)
1969 other_user = insert(:user)
1970 {:ok, user} = User.follow(user, other_user)
1974 |> get("/api/v1/accounts/#{user.id}/following")
1976 assert [%{"id" => id}] = json_response(conn, 200)
1977 assert id == to_string(other_user.id)
1980 test "getting following, hide_follows", %{conn: conn} do
1981 user = insert(:user, %{info: %{hide_follows: true}})
1982 other_user = insert(:user)
1983 {:ok, user} = User.follow(user, other_user)
1987 |> get("/api/v1/accounts/#{user.id}/following")
1989 assert [] == json_response(conn, 200)
1992 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1993 user = insert(:user, %{info: %{hide_follows: true}})
1994 other_user = insert(:user)
1995 {:ok, user} = User.follow(user, other_user)
1999 |> assign(:user, user)
2000 |> get("/api/v1/accounts/#{user.id}/following")
2002 refute [] == json_response(conn, 200)
2005 test "getting following, pagination", %{conn: conn} do
2006 user = insert(:user)
2007 following1 = insert(:user)
2008 following2 = insert(:user)
2009 following3 = insert(:user)
2010 {:ok, _} = User.follow(user, following1)
2011 {:ok, _} = User.follow(user, following2)
2012 {:ok, _} = User.follow(user, following3)
2016 |> assign(:user, user)
2020 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2022 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2023 assert id3 == following3.id
2024 assert id2 == following2.id
2028 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2030 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2031 assert id2 == following2.id
2032 assert id1 == following1.id
2036 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2038 assert [%{"id" => id2}] = json_response(res_conn, 200)
2039 assert id2 == following2.id
2041 assert [link_header] = get_resp_header(res_conn, "link")
2042 assert link_header =~ ~r/min_id=#{following2.id}/
2043 assert link_header =~ ~r/max_id=#{following2.id}/
2046 test "following / unfollowing a user", %{conn: conn} do
2047 user = insert(:user)
2048 other_user = insert(:user)
2052 |> assign(:user, user)
2053 |> post("/api/v1/accounts/#{other_user.id}/follow")
2055 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2057 user = User.get_cached_by_id(user.id)
2061 |> assign(:user, user)
2062 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2064 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2066 user = User.get_cached_by_id(user.id)
2070 |> assign(:user, user)
2071 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2073 assert %{"id" => id} = json_response(conn, 200)
2074 assert id == to_string(other_user.id)
2077 test "following without reblogs" do
2078 follower = insert(:user)
2079 followed = insert(:user)
2080 other_user = insert(:user)
2084 |> assign(:user, follower)
2085 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2087 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2089 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2090 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2094 |> assign(:user, User.get_cached_by_id(follower.id))
2095 |> get("/api/v1/timelines/home")
2097 assert [] == json_response(conn, 200)
2101 |> assign(:user, follower)
2102 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2104 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2108 |> assign(:user, User.get_cached_by_id(follower.id))
2109 |> get("/api/v1/timelines/home")
2111 expected_activity_id = reblog.id
2112 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2115 test "following / unfollowing errors" do
2116 user = insert(:user)
2120 |> assign(:user, user)
2123 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2124 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2127 user = User.get_cached_by_id(user.id)
2128 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2129 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2131 # self follow via uri
2132 user = User.get_cached_by_id(user.id)
2133 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2134 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2136 # follow non existing user
2137 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2138 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2140 # follow non existing user via uri
2141 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2142 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2144 # unfollow non existing user
2145 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2146 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2149 describe "mute/unmute" do
2150 test "with notifications", %{conn: conn} do
2151 user = insert(:user)
2152 other_user = insert(:user)
2156 |> assign(:user, user)
2157 |> post("/api/v1/accounts/#{other_user.id}/mute")
2159 response = json_response(conn, 200)
2161 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2162 user = User.get_cached_by_id(user.id)
2166 |> assign(:user, user)
2167 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2169 response = json_response(conn, 200)
2170 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2173 test "without notifications", %{conn: conn} do
2174 user = insert(:user)
2175 other_user = insert(:user)
2179 |> assign(:user, user)
2180 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2182 response = json_response(conn, 200)
2184 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2185 user = User.get_cached_by_id(user.id)
2189 |> assign(:user, user)
2190 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2192 response = json_response(conn, 200)
2193 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2197 test "subscribing / unsubscribing to a user", %{conn: conn} do
2198 user = insert(:user)
2199 subscription_target = insert(:user)
2203 |> assign(:user, user)
2204 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2206 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2210 |> assign(:user, user)
2211 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2213 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2216 test "getting a list of mutes", %{conn: conn} do
2217 user = insert(:user)
2218 other_user = insert(:user)
2220 {:ok, user} = User.mute(user, other_user)
2224 |> assign(:user, user)
2225 |> get("/api/v1/mutes")
2227 other_user_id = to_string(other_user.id)
2228 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2231 test "blocking / unblocking a user", %{conn: conn} do
2232 user = insert(:user)
2233 other_user = insert(:user)
2237 |> assign(:user, user)
2238 |> post("/api/v1/accounts/#{other_user.id}/block")
2240 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2242 user = User.get_cached_by_id(user.id)
2246 |> assign(:user, user)
2247 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2249 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2252 test "getting a list of blocks", %{conn: conn} do
2253 user = insert(:user)
2254 other_user = insert(:user)
2256 {:ok, user} = User.block(user, other_user)
2260 |> assign(:user, user)
2261 |> get("/api/v1/blocks")
2263 other_user_id = to_string(other_user.id)
2264 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2267 test "blocking / unblocking a domain", %{conn: conn} do
2268 user = insert(:user)
2269 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2273 |> assign(:user, user)
2274 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2276 assert %{} = json_response(conn, 200)
2277 user = User.get_cached_by_ap_id(user.ap_id)
2278 assert User.blocks?(user, other_user)
2282 |> assign(:user, user)
2283 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2285 assert %{} = json_response(conn, 200)
2286 user = User.get_cached_by_ap_id(user.ap_id)
2287 refute User.blocks?(user, other_user)
2290 test "getting a list of domain blocks", %{conn: conn} do
2291 user = insert(:user)
2293 {:ok, user} = User.block_domain(user, "bad.site")
2294 {:ok, user} = User.block_domain(user, "even.worse.site")
2298 |> assign(:user, user)
2299 |> get("/api/v1/domain_blocks")
2301 domain_blocks = json_response(conn, 200)
2303 assert "bad.site" in domain_blocks
2304 assert "even.worse.site" in domain_blocks
2307 test "unimplemented follow_requests, blocks, domain blocks" do
2308 user = insert(:user)
2310 ["blocks", "domain_blocks", "follow_requests"]
2311 |> Enum.each(fn endpoint ->
2314 |> assign(:user, user)
2315 |> get("/api/v1/#{endpoint}")
2317 assert [] = json_response(conn, 200)
2321 test "returns the favorites of a user", %{conn: conn} do
2322 user = insert(:user)
2323 other_user = insert(:user)
2325 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2326 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2328 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2332 |> assign(:user, user)
2333 |> get("/api/v1/favourites")
2335 assert [status] = json_response(first_conn, 200)
2336 assert status["id"] == to_string(activity.id)
2338 assert [{"link", _link_header}] =
2339 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2341 # Honours query params
2342 {:ok, second_activity} =
2343 CommonAPI.post(other_user, %{
2345 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2348 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2350 last_like = status["id"]
2354 |> assign(:user, user)
2355 |> get("/api/v1/favourites?since_id=#{last_like}")
2357 assert [second_status] = json_response(second_conn, 200)
2358 assert second_status["id"] == to_string(second_activity.id)
2362 |> assign(:user, user)
2363 |> get("/api/v1/favourites?limit=0")
2365 assert [] = json_response(third_conn, 200)
2368 describe "getting favorites timeline of specified user" do
2370 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2371 [current_user: current_user, user: user]
2374 test "returns list of statuses favorited by specified user", %{
2376 current_user: current_user,
2379 [activity | _] = insert_pair(:note_activity)
2380 CommonAPI.favorite(activity.id, user)
2384 |> assign(:user, current_user)
2385 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2386 |> json_response(:ok)
2390 assert length(response) == 1
2391 assert like["id"] == activity.id
2394 test "returns favorites for specified user_id when user is not logged in", %{
2398 activity = insert(:note_activity)
2399 CommonAPI.favorite(activity.id, user)
2403 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2404 |> json_response(:ok)
2406 assert length(response) == 1
2409 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2411 current_user: current_user,
2415 CommonAPI.post(current_user, %{
2416 "status" => "Hi @#{user.nickname}!",
2417 "visibility" => "direct"
2420 CommonAPI.favorite(direct.id, user)
2424 |> assign(:user, current_user)
2425 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2426 |> json_response(:ok)
2428 assert length(response) == 1
2430 anonymous_response =
2432 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2433 |> json_response(:ok)
2435 assert Enum.empty?(anonymous_response)
2438 test "does not return others' favorited DM when user is not one of recipients", %{
2440 current_user: current_user,
2443 user_two = insert(:user)
2446 CommonAPI.post(user_two, %{
2447 "status" => "Hi @#{user.nickname}!",
2448 "visibility" => "direct"
2451 CommonAPI.favorite(direct.id, user)
2455 |> assign(:user, current_user)
2456 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2457 |> json_response(:ok)
2459 assert Enum.empty?(response)
2462 test "paginates favorites using since_id and max_id", %{
2464 current_user: current_user,
2467 activities = insert_list(10, :note_activity)
2469 Enum.each(activities, fn activity ->
2470 CommonAPI.favorite(activity.id, user)
2473 third_activity = Enum.at(activities, 2)
2474 seventh_activity = Enum.at(activities, 6)
2478 |> assign(:user, current_user)
2479 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2480 since_id: third_activity.id,
2481 max_id: seventh_activity.id
2483 |> json_response(:ok)
2485 assert length(response) == 3
2486 refute third_activity in response
2487 refute seventh_activity in response
2490 test "limits favorites using limit parameter", %{
2492 current_user: current_user,
2496 |> insert_list(:note_activity)
2497 |> Enum.each(fn activity ->
2498 CommonAPI.favorite(activity.id, user)
2503 |> assign(:user, current_user)
2504 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2505 |> json_response(:ok)
2507 assert length(response) == 3
2510 test "returns empty response when user does not have any favorited statuses", %{
2512 current_user: current_user,
2517 |> assign(:user, current_user)
2518 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2519 |> json_response(:ok)
2521 assert Enum.empty?(response)
2524 test "returns 404 error when specified user is not exist", %{conn: conn} do
2525 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2527 assert json_response(conn, 404) == %{"error" => "Record not found"}
2530 test "returns 403 error when user has hidden own favorites", %{
2532 current_user: current_user
2534 user = insert(:user, %{info: %{hide_favorites: true}})
2535 activity = insert(:note_activity)
2536 CommonAPI.favorite(activity.id, user)
2540 |> assign(:user, current_user)
2541 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2543 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2546 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2547 user = insert(:user)
2548 activity = insert(:note_activity)
2549 CommonAPI.favorite(activity.id, user)
2553 |> assign(:user, current_user)
2554 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2556 assert user.info.hide_favorites
2557 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2561 test "get instance information", %{conn: conn} do
2562 conn = get(conn, "/api/v1/instance")
2563 assert result = json_response(conn, 200)
2565 email = Config.get([:instance, :email])
2566 # Note: not checking for "max_toot_chars" since it's optional
2572 "email" => from_config_email,
2574 "streaming_api" => _
2579 "registrations" => _,
2583 assert email == from_config_email
2586 test "get instance stats", %{conn: conn} do
2587 user = insert(:user, %{local: true})
2589 user2 = insert(:user, %{local: true})
2590 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2592 insert(:user, %{local: false, nickname: "u@peer1.com"})
2593 insert(:user, %{local: false, nickname: "u@peer2.com"})
2595 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2597 # Stats should count users with missing or nil `info.deactivated` value
2598 user = User.get_cached_by_id(user.id)
2599 info_change = Changeset.change(user.info, %{deactivated: nil})
2603 |> Changeset.change()
2604 |> Changeset.put_embed(:info, info_change)
2605 |> User.update_and_set_cache()
2607 Pleroma.Stats.force_update()
2609 conn = get(conn, "/api/v1/instance")
2611 assert result = json_response(conn, 200)
2613 stats = result["stats"]
2616 assert stats["user_count"] == 1
2617 assert stats["status_count"] == 1
2618 assert stats["domain_count"] == 2
2621 test "get peers", %{conn: conn} do
2622 insert(:user, %{local: false, nickname: "u@peer1.com"})
2623 insert(:user, %{local: false, nickname: "u@peer2.com"})
2625 Pleroma.Stats.force_update()
2627 conn = get(conn, "/api/v1/instance/peers")
2629 assert result = json_response(conn, 200)
2631 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2634 test "put settings", %{conn: conn} do
2635 user = insert(:user)
2639 |> assign(:user, user)
2640 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2642 assert _result = json_response(conn, 200)
2644 user = User.get_cached_by_ap_id(user.ap_id)
2645 assert user.info.settings == %{"programming" => "socks"}
2648 describe "pinned statuses" do
2650 user = insert(:user)
2651 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2653 [user: user, activity: activity]
2656 clear_config([:instance, :max_pinned_statuses]) do
2657 Config.put([:instance, :max_pinned_statuses], 1)
2660 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2661 {:ok, _} = CommonAPI.pin(activity.id, user)
2665 |> assign(:user, user)
2666 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2667 |> json_response(200)
2669 id_str = to_string(activity.id)
2671 assert [%{"id" => ^id_str, "pinned" => true}] = result
2674 test "pin status", %{conn: conn, user: user, activity: activity} do
2675 id_str = to_string(activity.id)
2677 assert %{"id" => ^id_str, "pinned" => true} =
2679 |> assign(:user, user)
2680 |> post("/api/v1/statuses/#{activity.id}/pin")
2681 |> json_response(200)
2683 assert [%{"id" => ^id_str, "pinned" => true}] =
2685 |> assign(:user, user)
2686 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2687 |> json_response(200)
2690 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2691 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2695 |> assign(:user, user)
2696 |> post("/api/v1/statuses/#{dm.id}/pin")
2698 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2701 test "unpin status", %{conn: conn, user: user, activity: activity} do
2702 {:ok, _} = CommonAPI.pin(activity.id, user)
2704 id_str = to_string(activity.id)
2705 user = refresh_record(user)
2707 assert %{"id" => ^id_str, "pinned" => false} =
2709 |> assign(:user, user)
2710 |> post("/api/v1/statuses/#{activity.id}/unpin")
2711 |> json_response(200)
2715 |> assign(:user, user)
2716 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2717 |> json_response(200)
2720 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2723 |> assign(:user, user)
2724 |> post("/api/v1/statuses/1/unpin")
2726 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2729 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2730 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2732 id_str_one = to_string(activity_one.id)
2734 assert %{"id" => ^id_str_one, "pinned" => true} =
2736 |> assign(:user, user)
2737 |> post("/api/v1/statuses/#{id_str_one}/pin")
2738 |> json_response(200)
2740 user = refresh_record(user)
2742 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2744 |> assign(:user, user)
2745 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2746 |> json_response(400)
2752 Config.put([:rich_media, :enabled], true)
2754 user = insert(:user)
2758 test "returns rich-media card", %{conn: conn, user: user} do
2759 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2762 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2763 "provider_name" => "example.com",
2764 "provider_url" => "https://example.com",
2765 "title" => "The Rock",
2767 "url" => "https://example.com/ogp",
2769 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2772 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2773 "title" => "The Rock",
2774 "type" => "video.movie",
2775 "url" => "https://example.com/ogp",
2777 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2784 |> get("/api/v1/statuses/#{activity.id}/card")
2785 |> json_response(200)
2787 assert response == card_data
2789 # works with private posts
2791 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2795 |> assign(:user, user)
2796 |> get("/api/v1/statuses/#{activity.id}/card")
2797 |> json_response(200)
2799 assert response_two == card_data
2802 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2804 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2808 |> get("/api/v1/statuses/#{activity.id}/card")
2809 |> json_response(:ok)
2811 assert response == %{
2813 "title" => "Pleroma",
2814 "description" => "",
2816 "provider_name" => "example.com",
2817 "provider_url" => "https://example.com",
2818 "url" => "https://example.com/ogp-missing-data",
2821 "title" => "Pleroma",
2822 "type" => "website",
2823 "url" => "https://example.com/ogp-missing-data"
2831 user = insert(:user)
2832 for_user = insert(:user)
2835 CommonAPI.post(user, %{
2836 "status" => "heweoo?"
2840 CommonAPI.post(user, %{
2841 "status" => "heweoo!"
2846 |> assign(:user, for_user)
2847 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2849 assert json_response(response1, 200)["bookmarked"] == true
2853 |> assign(:user, for_user)
2854 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2856 assert json_response(response2, 200)["bookmarked"] == true
2860 |> assign(:user, for_user)
2861 |> get("/api/v1/bookmarks")
2863 assert [json_response(response2, 200), json_response(response1, 200)] ==
2864 json_response(bookmarks, 200)
2868 |> assign(:user, for_user)
2869 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2871 assert json_response(response1, 200)["bookmarked"] == false
2875 |> assign(:user, for_user)
2876 |> get("/api/v1/bookmarks")
2878 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2881 describe "conversation muting" do
2883 post_user = insert(:user)
2884 user = insert(:user)
2886 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2888 [user: user, activity: activity]
2891 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2892 id_str = to_string(activity.id)
2894 assert %{"id" => ^id_str, "muted" => true} =
2896 |> assign(:user, user)
2897 |> post("/api/v1/statuses/#{activity.id}/mute")
2898 |> json_response(200)
2901 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2902 {:ok, _} = CommonAPI.add_mute(user, activity)
2906 |> assign(:user, user)
2907 |> post("/api/v1/statuses/#{activity.id}/mute")
2909 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2912 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2913 {:ok, _} = CommonAPI.add_mute(user, activity)
2915 id_str = to_string(activity.id)
2916 user = refresh_record(user)
2918 assert %{"id" => ^id_str, "muted" => false} =
2920 |> assign(:user, user)
2921 |> post("/api/v1/statuses/#{activity.id}/unmute")
2922 |> json_response(200)
2926 describe "reports" do
2928 reporter = insert(:user)
2929 target_user = insert(:user)
2931 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2933 [reporter: reporter, target_user: target_user, activity: activity]
2936 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2937 assert %{"action_taken" => false, "id" => _} =
2939 |> assign(:user, reporter)
2940 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2941 |> json_response(200)
2944 test "submit a report with statuses and comment", %{
2947 target_user: target_user,
2950 assert %{"action_taken" => false, "id" => _} =
2952 |> assign(:user, reporter)
2953 |> post("/api/v1/reports", %{
2954 "account_id" => target_user.id,
2955 "status_ids" => [activity.id],
2956 "comment" => "bad status!",
2957 "forward" => "false"
2959 |> json_response(200)
2962 test "account_id is required", %{
2967 assert %{"error" => "Valid `account_id` required"} =
2969 |> assign(:user, reporter)
2970 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2971 |> json_response(400)
2974 test "comment must be up to the size specified in the config", %{
2977 target_user: target_user
2979 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2980 comment = String.pad_trailing("a", max_size + 1, "a")
2982 error = %{"error" => "Comment must be up to #{max_size} characters"}
2986 |> assign(:user, reporter)
2987 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2988 |> json_response(400)
2991 test "returns error when account is not exist", %{
2998 |> assign(:user, reporter)
2999 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3001 assert json_response(conn, 400) == %{"error" => "Account not found"}
3005 describe "link headers" do
3006 test "preserves parameters in link headers", %{conn: conn} do
3007 user = insert(:user)
3008 other_user = insert(:user)
3011 CommonAPI.post(other_user, %{
3012 "status" => "hi @#{user.nickname}",
3013 "visibility" => "public"
3017 CommonAPI.post(other_user, %{
3018 "status" => "hi @#{user.nickname}",
3019 "visibility" => "public"
3022 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3023 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3027 |> assign(:user, user)
3028 |> get("/api/v1/notifications", %{media_only: true})
3030 assert [link_header] = get_resp_header(conn, "link")
3031 assert link_header =~ ~r/media_only=true/
3032 assert link_header =~ ~r/min_id=#{notification2.id}/
3033 assert link_header =~ ~r/max_id=#{notification1.id}/
3037 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3038 # Need to set an old-style integer ID to reproduce the problem
3039 # (these are no longer assigned to new accounts but were preserved
3040 # for existing accounts during the migration to flakeIDs)
3041 user_one = insert(:user, %{id: 1212})
3042 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3046 |> get("/api/v1/accounts/#{user_one.id}")
3050 |> get("/api/v1/accounts/#{user_two.nickname}")
3054 |> get("/api/v1/accounts/#{user_two.id}")
3056 acc_one = json_response(resp_one, 200)
3057 acc_two = json_response(resp_two, 200)
3058 acc_three = json_response(resp_three, 200)
3059 refute acc_one == acc_two
3060 assert acc_two == acc_three
3063 describe "custom emoji" do
3064 test "with tags", %{conn: conn} do
3067 |> get("/api/v1/custom_emojis")
3068 |> json_response(200)
3070 assert Map.has_key?(emoji, "shortcode")
3071 assert Map.has_key?(emoji, "static_url")
3072 assert Map.has_key?(emoji, "tags")
3073 assert is_list(emoji["tags"])
3074 assert Map.has_key?(emoji, "category")
3075 assert Map.has_key?(emoji, "url")
3076 assert Map.has_key?(emoji, "visible_in_picker")
3080 describe "index/2 redirections" do
3081 setup %{conn: conn} do
3085 signing_salt: "cooldude"
3090 |> Plug.Session.call(Plug.Session.init(session_opts))
3093 test_path = "/web/statuses/test"
3094 %{conn: conn, path: test_path}
3097 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3098 conn = get(conn, path)
3100 assert conn.status == 302
3101 assert redirected_to(conn) == "/web/login"
3104 test "redirects not logged-in users to the login page on private instances", %{
3108 Config.put([:instance, :public], false)
3110 conn = get(conn, path)
3112 assert conn.status == 302
3113 assert redirected_to(conn) == "/web/login"
3116 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3117 token = insert(:oauth_token)
3121 |> assign(:user, token.user)
3122 |> put_session(:oauth_token, token.token)
3125 assert conn.status == 200
3128 test "saves referer path to session", %{conn: conn, path: path} do
3129 conn = get(conn, path)
3130 return_to = Plug.Conn.get_session(conn, :return_to)
3132 assert return_to == path
3135 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3136 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3137 auth = insert(:oauth_authorization, app: app)
3141 |> put_session(:return_to, path)
3142 |> get("/web/login", %{code: auth.token})
3144 assert conn.status == 302
3145 assert redirected_to(conn) == path
3148 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3149 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3150 auth = insert(:oauth_authorization, app: app)
3152 conn = get(conn, "/web/login", %{code: auth.token})
3154 assert conn.status == 302
3155 assert redirected_to(conn) == "/web/getting-started"
3159 describe "scheduled activities" do
3160 test "creates a scheduled activity", %{conn: conn} do
3161 user = insert(:user)
3162 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3166 |> assign(:user, user)
3167 |> post("/api/v1/statuses", %{
3168 "status" => "scheduled",
3169 "scheduled_at" => scheduled_at
3172 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3173 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3174 assert [] == Repo.all(Activity)
3177 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3178 user = insert(:user)
3179 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3181 file = %Plug.Upload{
3182 content_type: "image/jpg",
3183 path: Path.absname("test/fixtures/image.jpg"),
3184 filename: "an_image.jpg"
3187 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3191 |> assign(:user, user)
3192 |> post("/api/v1/statuses", %{
3193 "media_ids" => [to_string(upload.id)],
3194 "status" => "scheduled",
3195 "scheduled_at" => scheduled_at
3198 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3199 assert %{"type" => "image"} = media_attachment
3202 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3204 user = insert(:user)
3207 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3211 |> assign(:user, user)
3212 |> post("/api/v1/statuses", %{
3213 "status" => "not scheduled",
3214 "scheduled_at" => scheduled_at
3217 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3218 assert [] == Repo.all(ScheduledActivity)
3221 test "returns error when daily user limit is exceeded", %{conn: conn} do
3222 user = insert(:user)
3225 NaiveDateTime.utc_now()
3226 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3227 |> NaiveDateTime.to_iso8601()
3229 attrs = %{params: %{}, scheduled_at: today}
3230 {:ok, _} = ScheduledActivity.create(user, attrs)
3231 {:ok, _} = ScheduledActivity.create(user, attrs)
3235 |> assign(:user, user)
3236 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3238 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3241 test "returns error when total user limit is exceeded", %{conn: conn} do
3242 user = insert(:user)
3245 NaiveDateTime.utc_now()
3246 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3247 |> NaiveDateTime.to_iso8601()
3250 NaiveDateTime.utc_now()
3251 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3252 |> NaiveDateTime.to_iso8601()
3254 attrs = %{params: %{}, scheduled_at: today}
3255 {:ok, _} = ScheduledActivity.create(user, attrs)
3256 {:ok, _} = ScheduledActivity.create(user, attrs)
3257 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3261 |> assign(:user, user)
3262 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3264 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3267 test "shows scheduled activities", %{conn: conn} do
3268 user = insert(:user)
3269 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3270 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3271 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3272 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3276 |> assign(:user, user)
3281 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3283 result = json_response(conn_res, 200)
3284 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3289 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3291 result = json_response(conn_res, 200)
3292 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3297 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3299 result = json_response(conn_res, 200)
3300 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3303 test "shows a scheduled activity", %{conn: conn} do
3304 user = insert(:user)
3305 scheduled_activity = insert(:scheduled_activity, user: user)
3309 |> assign(:user, user)
3310 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3312 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3313 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3317 |> assign(:user, user)
3318 |> get("/api/v1/scheduled_statuses/404")
3320 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3323 test "updates a scheduled activity", %{conn: conn} do
3324 user = insert(:user)
3325 scheduled_activity = insert(:scheduled_activity, user: user)
3328 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3332 |> assign(:user, user)
3333 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3334 scheduled_at: new_scheduled_at
3337 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3338 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3342 |> assign(:user, user)
3343 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3345 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3348 test "deletes a scheduled activity", %{conn: conn} do
3349 user = insert(:user)
3350 scheduled_activity = insert(:scheduled_activity, user: user)
3354 |> assign(:user, user)
3355 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3357 assert %{} = json_response(res_conn, 200)
3358 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3362 |> assign(:user, user)
3363 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3365 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3369 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3370 user1 = insert(:user)
3371 user2 = insert(:user)
3372 user3 = insert(:user)
3374 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3376 # Reply to status from another user
3379 |> assign(:user, user2)
3380 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3382 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3384 activity = Activity.get_by_id_with_object(id)
3386 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3387 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3389 # Reblog from the third user
3392 |> assign(:user, user3)
3393 |> post("/api/v1/statuses/#{activity.id}/reblog")
3395 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3396 json_response(conn2, 200)
3398 assert to_string(activity.id) == id
3400 # Getting third user status
3403 |> assign(:user, user3)
3404 |> get("api/v1/timelines/home")
3406 [reblogged_activity] = json_response(conn3, 200)
3408 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3410 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3411 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3414 describe "create account by app" do
3415 test "Account registration via Application", %{conn: conn} do
3418 |> post("/api/v1/apps", %{
3419 client_name: "client_name",
3420 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3421 scopes: "read, write, follow"
3425 "client_id" => client_id,
3426 "client_secret" => client_secret,
3428 "name" => "client_name",
3429 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3432 } = json_response(conn, 200)
3436 |> post("/oauth/token", %{
3437 grant_type: "client_credentials",
3438 client_id: client_id,
3439 client_secret: client_secret
3442 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3443 json_response(conn, 200)
3446 token_from_db = Repo.get_by(Token, token: token)
3447 assert token_from_db
3449 assert scope == "read write follow"
3453 |> put_req_header("authorization", "Bearer " <> token)
3454 |> post("/api/v1/accounts", %{
3456 email: "lain@example.org",
3457 password: "PlzDontHackLain",
3462 "access_token" => token,
3463 "created_at" => _created_at,
3465 "token_type" => "Bearer"
3466 } = json_response(conn, 200)
3468 token_from_db = Repo.get_by(Token, token: token)
3469 assert token_from_db
3470 token_from_db = Repo.preload(token_from_db, :user)
3471 assert token_from_db.user
3473 assert token_from_db.user.info.confirmation_pending
3476 test "rate limit", %{conn: conn} do
3477 app_token = insert(:oauth_token, user: nil)
3480 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3481 |> Map.put(:remote_ip, {15, 15, 15, 15})
3486 |> post("/api/v1/accounts", %{
3487 username: "#{i}lain",
3488 email: "#{i}lain@example.org",
3489 password: "PlzDontHackLain",
3494 "access_token" => token,
3495 "created_at" => _created_at,
3497 "token_type" => "Bearer"
3498 } = json_response(conn, 200)
3500 token_from_db = Repo.get_by(Token, token: token)
3501 assert token_from_db
3502 token_from_db = Repo.preload(token_from_db, :user)
3503 assert token_from_db.user
3505 assert token_from_db.user.info.confirmation_pending
3510 |> post("/api/v1/accounts", %{
3512 email: "6lain@example.org",
3513 password: "PlzDontHackLain",
3517 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3521 describe "GET /api/v1/polls/:id" do
3522 test "returns poll entity for object id", %{conn: conn} do
3523 user = insert(:user)
3526 CommonAPI.post(user, %{
3527 "status" => "Pleroma does",
3528 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3531 object = Object.normalize(activity)
3535 |> assign(:user, user)
3536 |> get("/api/v1/polls/#{object.id}")
3538 response = json_response(conn, 200)
3539 id = to_string(object.id)
3540 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3543 test "does not expose polls for private statuses", %{conn: conn} do
3544 user = insert(:user)
3545 other_user = insert(:user)
3548 CommonAPI.post(user, %{
3549 "status" => "Pleroma does",
3550 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3551 "visibility" => "private"
3554 object = Object.normalize(activity)
3558 |> assign(:user, other_user)
3559 |> get("/api/v1/polls/#{object.id}")
3561 assert json_response(conn, 404)
3565 describe "POST /api/v1/polls/:id/votes" do
3566 test "votes are added to the poll", %{conn: conn} do
3567 user = insert(:user)
3568 other_user = insert(:user)
3571 CommonAPI.post(user, %{
3572 "status" => "A very delicious sandwich",
3574 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3580 object = Object.normalize(activity)
3584 |> assign(:user, other_user)
3585 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3587 assert json_response(conn, 200)
3588 object = Object.get_by_id(object.id)
3590 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3595 test "author can't vote", %{conn: conn} do
3596 user = insert(:user)
3599 CommonAPI.post(user, %{
3600 "status" => "Am I cute?",
3601 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3604 object = Object.normalize(activity)
3607 |> assign(:user, user)
3608 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3609 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3611 object = Object.get_by_id(object.id)
3613 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3616 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3617 user = insert(:user)
3618 other_user = insert(:user)
3621 CommonAPI.post(user, %{
3622 "status" => "The glass is",
3623 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3626 object = Object.normalize(activity)
3629 |> assign(:user, other_user)
3630 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3631 |> json_response(422) == %{"error" => "Too many choices"}
3633 object = Object.get_by_id(object.id)
3635 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3640 test "does not allow choice index to be greater than options count", %{conn: conn} do
3641 user = insert(:user)
3642 other_user = insert(:user)
3645 CommonAPI.post(user, %{
3646 "status" => "Am I cute?",
3647 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3650 object = Object.normalize(activity)
3654 |> assign(:user, other_user)
3655 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3657 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3660 test "returns 404 error when object is not exist", %{conn: conn} do
3661 user = insert(:user)
3665 |> assign(:user, user)
3666 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3668 assert json_response(conn, 404) == %{"error" => "Record not found"}
3671 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3672 user = insert(:user)
3673 other_user = insert(:user)
3676 CommonAPI.post(user, %{
3677 "status" => "Am I cute?",
3678 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3679 "visibility" => "private"
3682 object = Object.normalize(activity)
3686 |> assign(:user, other_user)
3687 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3689 assert json_response(conn, 404) == %{"error" => "Record not found"}
3693 describe "GET /api/v1/statuses/:id/favourited_by" do
3695 user = insert(:user)
3696 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3700 |> assign(:user, user)
3702 [conn: conn, activity: activity, user: user]
3705 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3706 other_user = insert(:user)
3707 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3711 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3712 |> json_response(:ok)
3714 [%{"id" => id}] = response
3716 assert id == other_user.id
3719 test "returns empty array when status has not been favorited yet", %{
3725 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3726 |> json_response(:ok)
3728 assert Enum.empty?(response)
3731 test "does not return users who have favorited the status but are blocked", %{
3732 conn: %{assigns: %{user: user}} = conn,
3735 other_user = insert(:user)
3736 {:ok, user} = User.block(user, other_user)
3738 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3742 |> assign(:user, user)
3743 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3744 |> json_response(:ok)
3746 assert Enum.empty?(response)
3749 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3750 other_user = insert(:user)
3751 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3755 |> assign(:user, nil)
3756 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3757 |> json_response(:ok)
3759 [%{"id" => id}] = response
3760 assert id == other_user.id
3763 test "requires authentification for private posts", %{conn: conn, user: user} do
3764 other_user = insert(:user)
3767 CommonAPI.post(user, %{
3768 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3769 "visibility" => "direct"
3772 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3775 |> assign(:user, nil)
3776 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3777 |> json_response(404)
3781 |> assign(:user, other_user)
3782 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3783 |> json_response(200)
3785 [%{"id" => id}] = response
3786 assert id == other_user.id
3790 describe "GET /api/v1/statuses/:id/reblogged_by" do
3792 user = insert(:user)
3793 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3797 |> assign(:user, user)
3799 [conn: conn, activity: activity, user: user]
3802 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3803 other_user = insert(:user)
3804 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3808 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3809 |> json_response(:ok)
3811 [%{"id" => id}] = response
3813 assert id == other_user.id
3816 test "returns empty array when status has not been reblogged yet", %{
3822 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3823 |> json_response(:ok)
3825 assert Enum.empty?(response)
3828 test "does not return users who have reblogged the status but are blocked", %{
3829 conn: %{assigns: %{user: user}} = conn,
3832 other_user = insert(:user)
3833 {:ok, user} = User.block(user, other_user)
3835 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3839 |> assign(:user, user)
3840 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3841 |> json_response(:ok)
3843 assert Enum.empty?(response)
3846 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3847 other_user = insert(:user)
3848 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3852 |> assign(:user, nil)
3853 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3854 |> json_response(:ok)
3856 [%{"id" => id}] = response
3857 assert id == other_user.id
3860 test "requires authentification for private posts", %{conn: conn, user: user} do
3861 other_user = insert(:user)
3864 CommonAPI.post(user, %{
3865 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3866 "visibility" => "direct"
3870 |> assign(:user, nil)
3871 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3872 |> json_response(404)
3876 |> assign(:user, other_user)
3877 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3878 |> json_response(200)
3880 assert [] == response
3884 describe "POST /auth/password, with valid parameters" do
3885 setup %{conn: conn} do
3886 user = insert(:user)
3887 conn = post(conn, "/auth/password?email=#{user.email}")
3888 %{conn: conn, user: user}
3891 test "it returns 204", %{conn: conn} do
3892 assert json_response(conn, :no_content)
3895 test "it creates a PasswordResetToken record for user", %{user: user} do
3896 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3900 test "it sends an email to user", %{user: user} do
3901 ObanHelpers.perform_all()
3902 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3904 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3905 notify_email = Config.get([:instance, :notify_email])
3906 instance_name = Config.get([:instance, :name])
3909 from: {instance_name, notify_email},
3910 to: {user.name, user.email},
3911 html_body: email.html_body
3916 describe "POST /auth/password, with invalid parameters" do
3918 user = insert(:user)
3922 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3923 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3924 assert conn.status == 404
3925 assert conn.resp_body == ""
3928 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3929 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3930 conn = post(conn, "/auth/password?email=#{user.email}")
3931 assert conn.status == 400
3932 assert conn.resp_body == ""
3936 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3938 user = insert(:user)
3939 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3943 |> Changeset.change()
3944 |> Changeset.put_embed(:info, info_change)
3947 assert user.info.confirmation_pending
3952 clear_config([:instance, :account_activation_required]) do
3953 Config.put([:instance, :account_activation_required], true)
3956 test "resend account confirmation email", %{conn: conn, user: user} do
3958 |> assign(:user, user)
3959 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3960 |> json_response(:no_content)
3962 ObanHelpers.perform_all()
3964 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3965 notify_email = Config.get([:instance, :notify_email])
3966 instance_name = Config.get([:instance, :name])
3969 from: {instance_name, notify_email},
3970 to: {user.name, user.email},
3971 html_body: email.html_body
3976 describe "GET /api/v1/suggestions" do
3978 user = insert(:user)
3979 other_user = insert(:user)
3980 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3981 url500 = "http://test500?#{host}&#{user.nickname}"
3982 url200 = "http://test200?#{host}&#{user.nickname}"
3985 %{method: :get, url: ^url500} ->
3986 %Tesla.Env{status: 500, body: "bad request"}
3988 %{method: :get, url: ^url200} ->
3992 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3994 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3998 [user: user, other_user: other_user]
4001 clear_config(:suggestions)
4003 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4004 Config.put([:suggestions, :enabled], false)
4008 |> assign(:user, user)
4009 |> get("/api/v1/suggestions")
4010 |> json_response(200)
4015 test "returns error", %{conn: conn, user: user} do
4016 Config.put([:suggestions, :enabled], true)
4017 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4019 assert capture_log(fn ->
4022 |> assign(:user, user)
4023 |> get("/api/v1/suggestions")
4024 |> json_response(500)
4026 assert res == "Something went wrong"
4027 end) =~ "Could not retrieve suggestions"
4030 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4031 Config.put([:suggestions, :enabled], true)
4032 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4036 |> assign(:user, user)
4037 |> get("/api/v1/suggestions")
4038 |> json_response(200)
4043 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4044 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4048 "acct" => other_user.ap_id,
4049 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4050 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4051 "id" => other_user.id