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} = response = json_response(conn, 200)
300 assert response["visibility"] == "direct"
301 assert response["pleroma"]["direct_conversation_id"]
302 assert activity = Activity.get_by_id(id)
303 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
304 assert activity.data["to"] == [user2.ap_id]
305 assert activity.data["cc"] == []
309 describe "posting polls" do
310 test "posting a poll", %{conn: conn} do
312 time = NaiveDateTime.utc_now()
316 |> assign(:user, user)
317 |> post("/api/v1/statuses", %{
318 "status" => "Who is the #bestgrill?",
319 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
322 response = json_response(conn, 200)
324 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
325 title in ["Rei", "Asuka", "Misato"]
328 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
329 refute response["poll"]["expred"]
332 test "option limit is enforced", %{conn: conn} do
334 limit = Config.get([:instance, :poll_limits, :max_options])
338 |> assign(:user, user)
339 |> post("/api/v1/statuses", %{
341 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
344 %{"error" => error} = json_response(conn, 422)
345 assert error == "Poll can't contain more than #{limit} options"
348 test "option character limit is enforced", %{conn: conn} do
350 limit = Config.get([:instance, :poll_limits, :max_option_chars])
354 |> assign(:user, user)
355 |> post("/api/v1/statuses", %{
358 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
363 %{"error" => error} = json_response(conn, 422)
364 assert error == "Poll options cannot be longer than #{limit} characters each"
367 test "minimal date limit is enforced", %{conn: conn} do
369 limit = Config.get([:instance, :poll_limits, :min_expiration])
373 |> assign(:user, user)
374 |> post("/api/v1/statuses", %{
375 "status" => "imagine arbitrary limits",
377 "options" => ["this post was made by pleroma gang"],
378 "expires_in" => limit - 1
382 %{"error" => error} = json_response(conn, 422)
383 assert error == "Expiration date is too soon"
386 test "maximum date limit is enforced", %{conn: conn} do
388 limit = Config.get([:instance, :poll_limits, :max_expiration])
392 |> assign(:user, user)
393 |> post("/api/v1/statuses", %{
394 "status" => "imagine arbitrary limits",
396 "options" => ["this post was made by pleroma gang"],
397 "expires_in" => limit + 1
401 %{"error" => error} = json_response(conn, 422)
402 assert error == "Expiration date is too far in the future"
406 test "direct timeline", %{conn: conn} do
407 user_one = insert(:user)
408 user_two = insert(:user)
410 {:ok, user_two} = User.follow(user_two, user_one)
413 CommonAPI.post(user_one, %{
414 "status" => "Hi @#{user_two.nickname}!",
415 "visibility" => "direct"
418 {:ok, _follower_only} =
419 CommonAPI.post(user_one, %{
420 "status" => "Hi @#{user_two.nickname}!",
421 "visibility" => "private"
424 # Only direct should be visible here
427 |> assign(:user, user_two)
428 |> get("api/v1/timelines/direct")
430 [status] = json_response(res_conn, 200)
432 assert %{"visibility" => "direct"} = status
433 assert status["url"] != direct.data["id"]
435 # User should be able to see their own direct message
438 |> assign(:user, user_one)
439 |> get("api/v1/timelines/direct")
441 [status] = json_response(res_conn, 200)
443 assert %{"visibility" => "direct"} = status
445 # Both should be visible here
448 |> assign(:user, user_two)
449 |> get("api/v1/timelines/home")
451 [_s1, _s2] = json_response(res_conn, 200)
454 Enum.each(1..20, fn _ ->
456 CommonAPI.post(user_one, %{
457 "status" => "Hi @#{user_two.nickname}!",
458 "visibility" => "direct"
464 |> assign(:user, user_two)
465 |> get("api/v1/timelines/direct")
467 statuses = json_response(res_conn, 200)
468 assert length(statuses) == 20
472 |> assign(:user, user_two)
473 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
475 [status] = json_response(res_conn, 200)
477 assert status["url"] != direct.data["id"]
480 test "Conversations", %{conn: conn} do
481 user_one = insert(:user)
482 user_two = insert(:user)
483 user_three = insert(:user)
485 {:ok, user_two} = User.follow(user_two, user_one)
488 CommonAPI.post(user_one, %{
489 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
490 "visibility" => "direct"
493 {:ok, _follower_only} =
494 CommonAPI.post(user_one, %{
495 "status" => "Hi @#{user_two.nickname}!",
496 "visibility" => "private"
501 |> assign(:user, user_one)
502 |> get("/api/v1/conversations")
504 assert response = json_response(res_conn, 200)
509 "accounts" => res_accounts,
510 "last_status" => res_last_status,
515 account_ids = Enum.map(res_accounts, & &1["id"])
516 assert length(res_accounts) == 2
517 assert user_two.id in account_ids
518 assert user_three.id in account_ids
519 assert is_binary(res_id)
520 assert unread == true
521 assert res_last_status["id"] == direct.id
523 # Apparently undocumented API endpoint
526 |> assign(:user, user_one)
527 |> post("/api/v1/conversations/#{res_id}/read")
529 assert response = json_response(res_conn, 200)
530 assert length(response["accounts"]) == 2
531 assert response["last_status"]["id"] == direct.id
532 assert response["unread"] == false
534 # (vanilla) Mastodon frontend behaviour
537 |> assign(:user, user_one)
538 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
540 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
543 test "doesn't include DMs from blocked users", %{conn: conn} do
544 blocker = insert(:user)
545 blocked = insert(:user)
547 {:ok, blocker} = User.block(blocker, blocked)
549 {:ok, _blocked_direct} =
550 CommonAPI.post(blocked, %{
551 "status" => "Hi @#{blocker.nickname}!",
552 "visibility" => "direct"
556 CommonAPI.post(user, %{
557 "status" => "Hi @#{blocker.nickname}!",
558 "visibility" => "direct"
563 |> assign(:user, user)
564 |> get("api/v1/timelines/direct")
566 [status] = json_response(res_conn, 200)
567 assert status["id"] == direct.id
570 test "verify_credentials", %{conn: conn} do
575 |> assign(:user, user)
576 |> get("/api/v1/accounts/verify_credentials")
578 response = json_response(conn, 200)
580 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
581 assert response["pleroma"]["chat_token"]
582 assert id == to_string(user.id)
585 test "verify_credentials default scope unlisted", %{conn: conn} do
586 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
590 |> assign(:user, user)
591 |> get("/api/v1/accounts/verify_credentials")
593 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
594 assert id == to_string(user.id)
597 test "apps/verify_credentials", %{conn: conn} do
598 token = insert(:oauth_token)
602 |> assign(:user, token.user)
603 |> assign(:token, token)
604 |> get("/api/v1/apps/verify_credentials")
606 app = Repo.preload(token, :app).app
609 "name" => app.client_name,
610 "website" => app.website,
611 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
614 assert expected == json_response(conn, 200)
617 test "user avatar can be set", %{conn: conn} do
619 avatar_image = File.read!("test/fixtures/avatar_data_uri")
623 |> assign(:user, user)
624 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
626 user = refresh_record(user)
640 assert %{"url" => _} = json_response(conn, 200)
643 test "user avatar can be reset", %{conn: conn} do
648 |> assign(:user, user)
649 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
651 user = User.get_cached_by_id(user.id)
653 assert user.avatar == nil
655 assert %{"url" => nil} = json_response(conn, 200)
658 test "can set profile banner", %{conn: conn} do
663 |> assign(:user, user)
664 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
666 user = refresh_record(user)
667 assert user.info.banner["type"] == "Image"
669 assert %{"url" => _} = json_response(conn, 200)
672 test "can reset profile banner", %{conn: conn} do
677 |> assign(:user, user)
678 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
680 user = refresh_record(user)
681 assert user.info.banner == %{}
683 assert %{"url" => nil} = json_response(conn, 200)
686 test "background image can be set", %{conn: conn} do
691 |> assign(:user, user)
692 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
694 user = refresh_record(user)
695 assert user.info.background["type"] == "Image"
696 assert %{"url" => _} = json_response(conn, 200)
699 test "background image can be reset", %{conn: conn} do
704 |> assign(:user, user)
705 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
707 user = refresh_record(user)
708 assert user.info.background == %{}
709 assert %{"url" => nil} = json_response(conn, 200)
712 test "creates an oauth app", %{conn: conn} do
714 app_attrs = build(:oauth_app)
718 |> assign(:user, user)
719 |> post("/api/v1/apps", %{
720 client_name: app_attrs.client_name,
721 redirect_uris: app_attrs.redirect_uris
724 [app] = Repo.all(App)
727 "name" => app.client_name,
728 "website" => app.website,
729 "client_id" => app.client_id,
730 "client_secret" => app.client_secret,
731 "id" => app.id |> to_string(),
732 "redirect_uri" => app.redirect_uris,
733 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
736 assert expected == json_response(conn, 200)
739 test "get a status", %{conn: conn} do
740 activity = insert(:note_activity)
744 |> get("/api/v1/statuses/#{activity.id}")
746 assert %{"id" => id} = json_response(conn, 200)
747 assert id == to_string(activity.id)
750 test "get statuses by IDs", %{conn: conn} do
751 %{id: id1} = insert(:note_activity)
752 %{id: id2} = insert(:note_activity)
754 query_string = "ids[]=#{id1}&ids[]=#{id2}"
755 conn = get(conn, "/api/v1/statuses/?#{query_string}")
757 assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"])
760 describe "deleting a status" do
761 test "when you created it", %{conn: conn} do
762 activity = insert(:note_activity)
763 author = User.get_cached_by_ap_id(activity.data["actor"])
767 |> assign(:user, author)
768 |> delete("/api/v1/statuses/#{activity.id}")
770 assert %{} = json_response(conn, 200)
772 refute Activity.get_by_id(activity.id)
775 test "when you didn't create it", %{conn: conn} do
776 activity = insert(:note_activity)
781 |> assign(:user, user)
782 |> delete("/api/v1/statuses/#{activity.id}")
784 assert %{"error" => _} = json_response(conn, 403)
786 assert Activity.get_by_id(activity.id) == activity
789 test "when you're an admin or moderator", %{conn: conn} do
790 activity1 = insert(:note_activity)
791 activity2 = insert(:note_activity)
792 admin = insert(:user, info: %{is_admin: true})
793 moderator = insert(:user, info: %{is_moderator: true})
797 |> assign(:user, admin)
798 |> delete("/api/v1/statuses/#{activity1.id}")
800 assert %{} = json_response(res_conn, 200)
804 |> assign(:user, moderator)
805 |> delete("/api/v1/statuses/#{activity2.id}")
807 assert %{} = json_response(res_conn, 200)
809 refute Activity.get_by_id(activity1.id)
810 refute Activity.get_by_id(activity2.id)
814 describe "filters" do
815 test "creating a filter", %{conn: conn} do
818 filter = %Pleroma.Filter{
825 |> assign(:user, user)
826 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
828 assert response = json_response(conn, 200)
829 assert response["phrase"] == filter.phrase
830 assert response["context"] == filter.context
831 assert response["irreversible"] == false
832 assert response["id"] != nil
833 assert response["id"] != ""
836 test "fetching a list of filters", %{conn: conn} do
839 query_one = %Pleroma.Filter{
846 query_two = %Pleroma.Filter{
853 {:ok, filter_one} = Pleroma.Filter.create(query_one)
854 {:ok, filter_two} = Pleroma.Filter.create(query_two)
858 |> assign(:user, user)
859 |> get("/api/v1/filters")
860 |> json_response(200)
866 filters: [filter_two, filter_one]
870 test "get a filter", %{conn: conn} do
873 query = %Pleroma.Filter{
880 {:ok, filter} = Pleroma.Filter.create(query)
884 |> assign(:user, user)
885 |> get("/api/v1/filters/#{filter.filter_id}")
887 assert _response = json_response(conn, 200)
890 test "update a filter", %{conn: conn} do
893 query = %Pleroma.Filter{
900 {:ok, _filter} = Pleroma.Filter.create(query)
902 new = %Pleroma.Filter{
909 |> assign(:user, user)
910 |> put("/api/v1/filters/#{query.filter_id}", %{
915 assert response = json_response(conn, 200)
916 assert response["phrase"] == new.phrase
917 assert response["context"] == new.context
920 test "delete a filter", %{conn: conn} do
923 query = %Pleroma.Filter{
930 {:ok, filter} = Pleroma.Filter.create(query)
934 |> assign(:user, user)
935 |> delete("/api/v1/filters/#{filter.filter_id}")
937 assert response = json_response(conn, 200)
938 assert response == %{}
942 describe "list timelines" do
943 test "list timeline", %{conn: conn} do
945 other_user = insert(:user)
946 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
947 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
948 {:ok, list} = Pleroma.List.create("name", user)
949 {:ok, list} = Pleroma.List.follow(list, other_user)
953 |> assign(:user, user)
954 |> get("/api/v1/timelines/list/#{list.id}")
956 assert [%{"id" => id}] = json_response(conn, 200)
958 assert id == to_string(activity_two.id)
961 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
963 other_user = insert(:user)
964 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
966 {:ok, _activity_two} =
967 CommonAPI.post(other_user, %{
968 "status" => "Marisa is cute.",
969 "visibility" => "private"
972 {:ok, list} = Pleroma.List.create("name", user)
973 {:ok, list} = Pleroma.List.follow(list, other_user)
977 |> assign(:user, user)
978 |> get("/api/v1/timelines/list/#{list.id}")
980 assert [%{"id" => id}] = json_response(conn, 200)
982 assert id == to_string(activity_one.id)
986 describe "notifications" do
987 test "list of notifications", %{conn: conn} do
989 other_user = insert(:user)
991 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
993 {:ok, [_notification]} = Notification.create_notifications(activity)
997 |> assign(:user, user)
998 |> get("/api/v1/notifications")
1001 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1003 }\">@<span>#{user.nickname}</span></a></span>"
1005 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1006 assert response == expected_response
1009 test "getting a single notification", %{conn: conn} do
1010 user = insert(:user)
1011 other_user = insert(:user)
1013 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1015 {:ok, [notification]} = Notification.create_notifications(activity)
1019 |> assign(:user, user)
1020 |> get("/api/v1/notifications/#{notification.id}")
1023 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1025 }\">@<span>#{user.nickname}</span></a></span>"
1027 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1028 assert response == expected_response
1031 test "dismissing a single notification", %{conn: conn} do
1032 user = insert(:user)
1033 other_user = insert(:user)
1035 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1037 {:ok, [notification]} = Notification.create_notifications(activity)
1041 |> assign(:user, user)
1042 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1044 assert %{} = json_response(conn, 200)
1047 test "clearing all notifications", %{conn: conn} do
1048 user = insert(:user)
1049 other_user = insert(:user)
1051 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1053 {:ok, [_notification]} = Notification.create_notifications(activity)
1057 |> assign(:user, user)
1058 |> post("/api/v1/notifications/clear")
1060 assert %{} = json_response(conn, 200)
1064 |> assign(:user, user)
1065 |> get("/api/v1/notifications")
1067 assert all = json_response(conn, 200)
1071 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1072 user = insert(:user)
1073 other_user = insert(:user)
1075 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1076 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1077 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1078 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1080 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1081 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1082 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1083 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1087 |> assign(:user, user)
1092 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1094 result = json_response(conn_res, 200)
1095 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1100 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1102 result = json_response(conn_res, 200)
1103 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1108 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1110 result = json_response(conn_res, 200)
1111 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1114 test "filters notifications using exclude_types", %{conn: conn} do
1115 user = insert(:user)
1116 other_user = insert(:user)
1118 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1119 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1120 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1121 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1122 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1124 mention_notification_id =
1125 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1127 favorite_notification_id =
1128 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1130 reblog_notification_id =
1131 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1133 follow_notification_id =
1134 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1138 |> assign(:user, user)
1141 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1143 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1146 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1148 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1151 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1153 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1156 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1158 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1161 test "destroy multiple", %{conn: conn} do
1162 user = insert(:user)
1163 other_user = insert(:user)
1165 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1166 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1167 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1168 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1170 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1171 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1172 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1173 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1177 |> assign(:user, user)
1181 |> get("/api/v1/notifications")
1183 result = json_response(conn_res, 200)
1184 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1188 |> assign(:user, other_user)
1192 |> get("/api/v1/notifications")
1194 result = json_response(conn_res, 200)
1195 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1199 |> delete("/api/v1/notifications/destroy_multiple", %{
1200 "ids" => [notification1_id, notification2_id]
1203 assert json_response(conn_destroy, 200) == %{}
1207 |> get("/api/v1/notifications")
1209 result = json_response(conn_res, 200)
1210 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1213 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1214 user = insert(:user)
1215 user2 = insert(:user)
1217 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1218 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1220 conn = assign(conn, :user, user)
1222 conn = get(conn, "/api/v1/notifications")
1224 assert length(json_response(conn, 200)) == 1
1226 {:ok, user} = User.mute(user, user2)
1228 conn = assign(build_conn(), :user, user)
1229 conn = get(conn, "/api/v1/notifications")
1231 assert json_response(conn, 200) == []
1234 test "see notifications after muting user without notifications", %{conn: conn} do
1235 user = insert(:user)
1236 user2 = insert(:user)
1238 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1239 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1241 conn = assign(conn, :user, user)
1243 conn = get(conn, "/api/v1/notifications")
1245 assert length(json_response(conn, 200)) == 1
1247 {:ok, user} = User.mute(user, user2, false)
1249 conn = assign(build_conn(), :user, user)
1250 conn = get(conn, "/api/v1/notifications")
1252 assert length(json_response(conn, 200)) == 1
1255 test "see notifications after muting user with notifications and with_muted parameter", %{
1258 user = insert(:user)
1259 user2 = insert(:user)
1261 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1262 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1264 conn = assign(conn, :user, user)
1266 conn = get(conn, "/api/v1/notifications")
1268 assert length(json_response(conn, 200)) == 1
1270 {:ok, user} = User.mute(user, user2)
1272 conn = assign(build_conn(), :user, user)
1273 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1275 assert length(json_response(conn, 200)) == 1
1279 describe "reblogging" do
1280 test "reblogs and returns the reblogged status", %{conn: conn} do
1281 activity = insert(:note_activity)
1282 user = insert(:user)
1286 |> assign(:user, user)
1287 |> post("/api/v1/statuses/#{activity.id}/reblog")
1290 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1292 } = json_response(conn, 200)
1294 assert to_string(activity.id) == id
1297 test "reblogged status for another user", %{conn: conn} do
1298 activity = insert(:note_activity)
1299 user1 = insert(:user)
1300 user2 = insert(:user)
1301 user3 = insert(:user)
1302 CommonAPI.favorite(activity.id, user2)
1303 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1304 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1305 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1309 |> assign(:user, user3)
1310 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1313 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1314 "reblogged" => false,
1315 "favourited" => false,
1316 "bookmarked" => false
1317 } = json_response(conn_res, 200)
1321 |> assign(:user, user2)
1322 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1325 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1326 "reblogged" => true,
1327 "favourited" => true,
1328 "bookmarked" => true
1329 } = json_response(conn_res, 200)
1331 assert to_string(activity.id) == id
1334 test "returns 400 error when activity is not exist", %{conn: conn} do
1335 user = insert(:user)
1339 |> assign(:user, user)
1340 |> post("/api/v1/statuses/foo/reblog")
1342 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1346 describe "unreblogging" do
1347 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1348 activity = insert(:note_activity)
1349 user = insert(:user)
1351 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1355 |> assign(:user, user)
1356 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1358 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1360 assert to_string(activity.id) == id
1363 test "returns 400 error when activity is not exist", %{conn: conn} do
1364 user = insert(:user)
1368 |> assign(:user, user)
1369 |> post("/api/v1/statuses/foo/unreblog")
1371 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1375 describe "favoriting" do
1376 test "favs a status and returns it", %{conn: conn} do
1377 activity = insert(:note_activity)
1378 user = insert(:user)
1382 |> assign(:user, user)
1383 |> post("/api/v1/statuses/#{activity.id}/favourite")
1385 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1386 json_response(conn, 200)
1388 assert to_string(activity.id) == id
1391 test "returns 400 error for a wrong id", %{conn: conn} do
1392 user = insert(:user)
1396 |> assign(:user, user)
1397 |> post("/api/v1/statuses/1/favourite")
1399 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1403 describe "unfavoriting" do
1404 test "unfavorites a status and returns it", %{conn: conn} do
1405 activity = insert(:note_activity)
1406 user = insert(:user)
1408 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1412 |> assign(:user, user)
1413 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1415 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1416 json_response(conn, 200)
1418 assert to_string(activity.id) == id
1421 test "returns 400 error for a wrong id", %{conn: conn} do
1422 user = insert(:user)
1426 |> assign(:user, user)
1427 |> post("/api/v1/statuses/1/unfavourite")
1429 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1433 describe "user timelines" do
1434 test "gets a users statuses", %{conn: conn} do
1435 user_one = insert(:user)
1436 user_two = insert(:user)
1437 user_three = insert(:user)
1439 {:ok, user_three} = User.follow(user_three, user_one)
1441 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1443 {:ok, direct_activity} =
1444 CommonAPI.post(user_one, %{
1445 "status" => "Hi, @#{user_two.nickname}.",
1446 "visibility" => "direct"
1449 {:ok, private_activity} =
1450 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1454 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1456 assert [%{"id" => id}] = json_response(resp, 200)
1457 assert id == to_string(activity.id)
1461 |> assign(:user, user_two)
1462 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1464 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1465 assert id_one == to_string(direct_activity.id)
1466 assert id_two == to_string(activity.id)
1470 |> assign(:user, user_three)
1471 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1473 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1474 assert id_one == to_string(private_activity.id)
1475 assert id_two == to_string(activity.id)
1478 test "unimplemented pinned statuses feature", %{conn: conn} do
1479 note = insert(:note_activity)
1480 user = User.get_cached_by_ap_id(note.data["actor"])
1484 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1486 assert json_response(conn, 200) == []
1489 test "gets an users media", %{conn: conn} do
1490 note = insert(:note_activity)
1491 user = User.get_cached_by_ap_id(note.data["actor"])
1493 file = %Plug.Upload{
1494 content_type: "image/jpg",
1495 path: Path.absname("test/fixtures/image.jpg"),
1496 filename: "an_image.jpg"
1499 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1501 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1505 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1507 assert [%{"id" => id}] = json_response(conn, 200)
1508 assert id == to_string(image_post.id)
1512 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1514 assert [%{"id" => id}] = json_response(conn, 200)
1515 assert id == to_string(image_post.id)
1518 test "gets a user's statuses without reblogs", %{conn: conn} do
1519 user = insert(:user)
1520 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1521 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1525 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1527 assert [%{"id" => id}] = json_response(conn, 200)
1528 assert id == to_string(post.id)
1532 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1534 assert [%{"id" => id}] = json_response(conn, 200)
1535 assert id == to_string(post.id)
1538 test "filters user's statuses by a hashtag", %{conn: conn} do
1539 user = insert(:user)
1540 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1541 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1545 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1547 assert [%{"id" => id}] = json_response(conn, 200)
1548 assert id == to_string(post.id)
1552 describe "user relationships" do
1553 test "returns the relationships for the current user", %{conn: conn} do
1554 user = insert(:user)
1555 other_user = insert(:user)
1556 {:ok, user} = User.follow(user, other_user)
1560 |> assign(:user, user)
1561 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1563 assert [relationship] = json_response(conn, 200)
1565 assert to_string(other_user.id) == relationship["id"]
1569 describe "media upload" do
1571 user = insert(:user)
1575 |> assign(:user, user)
1577 image = %Plug.Upload{
1578 content_type: "image/jpg",
1579 path: Path.absname("test/fixtures/image.jpg"),
1580 filename: "an_image.jpg"
1583 [conn: conn, image: image]
1586 clear_config([:media_proxy])
1587 clear_config([Pleroma.Upload])
1589 test "returns uploaded image", %{conn: conn, image: image} do
1590 desc = "Description of the image"
1594 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1595 |> json_response(:ok)
1597 assert media["type"] == "image"
1598 assert media["description"] == desc
1601 object = Repo.get(Object, media["id"])
1602 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1606 describe "locked accounts" do
1607 test "/api/v1/follow_requests works" do
1608 user = insert(:user, %{info: %User.Info{locked: true}})
1609 other_user = insert(:user)
1611 {:ok, _activity} = ActivityPub.follow(other_user, user)
1613 user = User.get_cached_by_id(user.id)
1614 other_user = User.get_cached_by_id(other_user.id)
1616 assert User.following?(other_user, user) == false
1620 |> assign(:user, user)
1621 |> get("/api/v1/follow_requests")
1623 assert [relationship] = json_response(conn, 200)
1624 assert to_string(other_user.id) == relationship["id"]
1627 test "/api/v1/follow_requests/:id/authorize works" do
1628 user = insert(:user, %{info: %User.Info{locked: true}})
1629 other_user = insert(:user)
1631 {:ok, _activity} = ActivityPub.follow(other_user, user)
1633 user = User.get_cached_by_id(user.id)
1634 other_user = User.get_cached_by_id(other_user.id)
1636 assert User.following?(other_user, user) == false
1640 |> assign(:user, user)
1641 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1643 assert relationship = json_response(conn, 200)
1644 assert to_string(other_user.id) == relationship["id"]
1646 user = User.get_cached_by_id(user.id)
1647 other_user = User.get_cached_by_id(other_user.id)
1649 assert User.following?(other_user, user) == true
1652 test "verify_credentials", %{conn: conn} do
1653 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1657 |> assign(:user, user)
1658 |> get("/api/v1/accounts/verify_credentials")
1660 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1661 assert id == to_string(user.id)
1664 test "/api/v1/follow_requests/:id/reject works" do
1665 user = insert(:user, %{info: %User.Info{locked: true}})
1666 other_user = insert(:user)
1668 {:ok, _activity} = ActivityPub.follow(other_user, user)
1670 user = User.get_cached_by_id(user.id)
1674 |> assign(:user, user)
1675 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1677 assert relationship = json_response(conn, 200)
1678 assert to_string(other_user.id) == relationship["id"]
1680 user = User.get_cached_by_id(user.id)
1681 other_user = User.get_cached_by_id(other_user.id)
1683 assert User.following?(other_user, user) == false
1687 describe "account fetching" do
1688 test "works by id" do
1689 user = insert(:user)
1693 |> get("/api/v1/accounts/#{user.id}")
1695 assert %{"id" => id} = json_response(conn, 200)
1696 assert id == to_string(user.id)
1700 |> get("/api/v1/accounts/-1")
1702 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1705 test "works by nickname" do
1706 user = insert(:user)
1710 |> get("/api/v1/accounts/#{user.nickname}")
1712 assert %{"id" => id} = json_response(conn, 200)
1713 assert id == user.id
1716 test "works by nickname for remote users" do
1717 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1718 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1719 user = insert(:user, nickname: "user@example.com", local: false)
1723 |> get("/api/v1/accounts/#{user.nickname}")
1725 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1726 assert %{"id" => id} = json_response(conn, 200)
1727 assert id == user.id
1730 test "respects limit_to_local_content == :all for remote user nicknames" do
1731 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1732 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1734 user = insert(:user, nickname: "user@example.com", local: false)
1738 |> get("/api/v1/accounts/#{user.nickname}")
1740 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1741 assert json_response(conn, 404)
1744 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1745 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1746 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1748 user = insert(:user, nickname: "user@example.com", local: false)
1749 reading_user = insert(:user)
1753 |> get("/api/v1/accounts/#{user.nickname}")
1755 assert json_response(conn, 404)
1759 |> assign(:user, reading_user)
1760 |> get("/api/v1/accounts/#{user.nickname}")
1762 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1763 assert %{"id" => id} = json_response(conn, 200)
1764 assert id == user.id
1768 test "mascot upload", %{conn: conn} do
1769 user = insert(:user)
1771 non_image_file = %Plug.Upload{
1772 content_type: "audio/mpeg",
1773 path: Path.absname("test/fixtures/sound.mp3"),
1774 filename: "sound.mp3"
1779 |> assign(:user, user)
1780 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1782 assert json_response(conn, 415)
1784 file = %Plug.Upload{
1785 content_type: "image/jpg",
1786 path: Path.absname("test/fixtures/image.jpg"),
1787 filename: "an_image.jpg"
1792 |> assign(:user, user)
1793 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1795 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1798 test "mascot retrieving", %{conn: conn} do
1799 user = insert(:user)
1800 # When user hasn't set a mascot, we should just get pleroma tan back
1803 |> assign(:user, user)
1804 |> get("/api/v1/pleroma/mascot")
1806 assert %{"url" => url} = json_response(conn, 200)
1807 assert url =~ "pleroma-fox-tan-smol"
1809 # When a user sets their mascot, we should get that back
1810 file = %Plug.Upload{
1811 content_type: "image/jpg",
1812 path: Path.absname("test/fixtures/image.jpg"),
1813 filename: "an_image.jpg"
1818 |> assign(:user, user)
1819 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1821 assert json_response(conn, 200)
1823 user = User.get_cached_by_id(user.id)
1827 |> assign(:user, user)
1828 |> get("/api/v1/pleroma/mascot")
1830 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1831 assert url =~ "an_image"
1834 test "hashtag timeline", %{conn: conn} do
1835 following = insert(:user)
1838 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1840 {:ok, [_activity]} =
1841 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1845 |> get("/api/v1/timelines/tag/2hu")
1847 assert [%{"id" => id}] = json_response(nconn, 200)
1849 assert id == to_string(activity.id)
1851 # works for different capitalization too
1854 |> get("/api/v1/timelines/tag/2HU")
1856 assert [%{"id" => id}] = json_response(nconn, 200)
1858 assert id == to_string(activity.id)
1862 test "multi-hashtag timeline", %{conn: conn} do
1863 user = insert(:user)
1865 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1866 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1867 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1871 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1873 [status_none, status_test1, status_test] = json_response(any_test, 200)
1875 assert to_string(activity_test.id) == status_test["id"]
1876 assert to_string(activity_test1.id) == status_test1["id"]
1877 assert to_string(activity_none.id) == status_none["id"]
1881 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1883 assert [status_test1] == json_response(restricted_test, 200)
1885 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1887 assert [status_none] == json_response(all_test, 200)
1890 test "getting followers", %{conn: conn} do
1891 user = insert(:user)
1892 other_user = insert(:user)
1893 {:ok, user} = User.follow(user, other_user)
1897 |> get("/api/v1/accounts/#{other_user.id}/followers")
1899 assert [%{"id" => id}] = json_response(conn, 200)
1900 assert id == to_string(user.id)
1903 test "getting followers, hide_followers", %{conn: conn} do
1904 user = insert(:user)
1905 other_user = insert(:user, %{info: %{hide_followers: true}})
1906 {:ok, _user} = User.follow(user, other_user)
1910 |> get("/api/v1/accounts/#{other_user.id}/followers")
1912 assert [] == json_response(conn, 200)
1915 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1916 user = insert(:user)
1917 other_user = insert(:user, %{info: %{hide_followers: true}})
1918 {:ok, _user} = User.follow(user, other_user)
1922 |> assign(:user, other_user)
1923 |> get("/api/v1/accounts/#{other_user.id}/followers")
1925 refute [] == json_response(conn, 200)
1928 test "getting followers, pagination", %{conn: conn} do
1929 user = insert(:user)
1930 follower1 = insert(:user)
1931 follower2 = insert(:user)
1932 follower3 = insert(:user)
1933 {:ok, _} = User.follow(follower1, user)
1934 {:ok, _} = User.follow(follower2, user)
1935 {:ok, _} = User.follow(follower3, user)
1939 |> assign(:user, user)
1943 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1945 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1946 assert id3 == follower3.id
1947 assert id2 == follower2.id
1951 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1953 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1954 assert id2 == follower2.id
1955 assert id1 == follower1.id
1959 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1961 assert [%{"id" => id2}] = json_response(res_conn, 200)
1962 assert id2 == follower2.id
1964 assert [link_header] = get_resp_header(res_conn, "link")
1965 assert link_header =~ ~r/min_id=#{follower2.id}/
1966 assert link_header =~ ~r/max_id=#{follower2.id}/
1969 test "getting following", %{conn: conn} do
1970 user = insert(:user)
1971 other_user = insert(:user)
1972 {:ok, user} = User.follow(user, other_user)
1976 |> get("/api/v1/accounts/#{user.id}/following")
1978 assert [%{"id" => id}] = json_response(conn, 200)
1979 assert id == to_string(other_user.id)
1982 test "getting following, hide_follows", %{conn: conn} do
1983 user = insert(:user, %{info: %{hide_follows: true}})
1984 other_user = insert(:user)
1985 {:ok, user} = User.follow(user, other_user)
1989 |> get("/api/v1/accounts/#{user.id}/following")
1991 assert [] == json_response(conn, 200)
1994 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1995 user = insert(:user, %{info: %{hide_follows: true}})
1996 other_user = insert(:user)
1997 {:ok, user} = User.follow(user, other_user)
2001 |> assign(:user, user)
2002 |> get("/api/v1/accounts/#{user.id}/following")
2004 refute [] == json_response(conn, 200)
2007 test "getting following, pagination", %{conn: conn} do
2008 user = insert(:user)
2009 following1 = insert(:user)
2010 following2 = insert(:user)
2011 following3 = insert(:user)
2012 {:ok, _} = User.follow(user, following1)
2013 {:ok, _} = User.follow(user, following2)
2014 {:ok, _} = User.follow(user, following3)
2018 |> assign(:user, user)
2022 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2024 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2025 assert id3 == following3.id
2026 assert id2 == following2.id
2030 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2032 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2033 assert id2 == following2.id
2034 assert id1 == following1.id
2038 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2040 assert [%{"id" => id2}] = json_response(res_conn, 200)
2041 assert id2 == following2.id
2043 assert [link_header] = get_resp_header(res_conn, "link")
2044 assert link_header =~ ~r/min_id=#{following2.id}/
2045 assert link_header =~ ~r/max_id=#{following2.id}/
2048 test "following / unfollowing a user", %{conn: conn} do
2049 user = insert(:user)
2050 other_user = insert(:user)
2054 |> assign(:user, user)
2055 |> post("/api/v1/accounts/#{other_user.id}/follow")
2057 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2059 user = User.get_cached_by_id(user.id)
2063 |> assign(:user, user)
2064 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2066 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2068 user = User.get_cached_by_id(user.id)
2072 |> assign(:user, user)
2073 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2075 assert %{"id" => id} = json_response(conn, 200)
2076 assert id == to_string(other_user.id)
2079 test "following without reblogs" do
2080 follower = insert(:user)
2081 followed = insert(:user)
2082 other_user = insert(:user)
2086 |> assign(:user, follower)
2087 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2089 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2091 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2092 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2096 |> assign(:user, User.get_cached_by_id(follower.id))
2097 |> get("/api/v1/timelines/home")
2099 assert [] == json_response(conn, 200)
2103 |> assign(:user, follower)
2104 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2106 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2110 |> assign(:user, User.get_cached_by_id(follower.id))
2111 |> get("/api/v1/timelines/home")
2113 expected_activity_id = reblog.id
2114 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2117 test "following / unfollowing errors" do
2118 user = insert(:user)
2122 |> assign(:user, user)
2125 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2126 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2129 user = User.get_cached_by_id(user.id)
2130 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2131 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2133 # self follow via uri
2134 user = User.get_cached_by_id(user.id)
2135 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2136 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2138 # follow non existing user
2139 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2140 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2142 # follow non existing user via uri
2143 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2144 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2146 # unfollow non existing user
2147 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2148 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2151 describe "mute/unmute" do
2152 test "with notifications", %{conn: conn} do
2153 user = insert(:user)
2154 other_user = insert(:user)
2158 |> assign(:user, user)
2159 |> post("/api/v1/accounts/#{other_user.id}/mute")
2161 response = json_response(conn, 200)
2163 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2164 user = User.get_cached_by_id(user.id)
2168 |> assign(:user, user)
2169 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2171 response = json_response(conn, 200)
2172 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2175 test "without notifications", %{conn: conn} do
2176 user = insert(:user)
2177 other_user = insert(:user)
2181 |> assign(:user, user)
2182 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2184 response = json_response(conn, 200)
2186 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2187 user = User.get_cached_by_id(user.id)
2191 |> assign(:user, user)
2192 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2194 response = json_response(conn, 200)
2195 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2199 test "subscribing / unsubscribing to a user", %{conn: conn} do
2200 user = insert(:user)
2201 subscription_target = insert(:user)
2205 |> assign(:user, user)
2206 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2208 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2212 |> assign(:user, user)
2213 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2215 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2218 test "getting a list of mutes", %{conn: conn} do
2219 user = insert(:user)
2220 other_user = insert(:user)
2222 {:ok, user} = User.mute(user, other_user)
2226 |> assign(:user, user)
2227 |> get("/api/v1/mutes")
2229 other_user_id = to_string(other_user.id)
2230 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2233 test "blocking / unblocking a user", %{conn: conn} do
2234 user = insert(:user)
2235 other_user = insert(:user)
2239 |> assign(:user, user)
2240 |> post("/api/v1/accounts/#{other_user.id}/block")
2242 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2244 user = User.get_cached_by_id(user.id)
2248 |> assign(:user, user)
2249 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2251 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2254 test "getting a list of blocks", %{conn: conn} do
2255 user = insert(:user)
2256 other_user = insert(:user)
2258 {:ok, user} = User.block(user, other_user)
2262 |> assign(:user, user)
2263 |> get("/api/v1/blocks")
2265 other_user_id = to_string(other_user.id)
2266 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2269 test "blocking / unblocking a domain", %{conn: conn} do
2270 user = insert(:user)
2271 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2275 |> assign(:user, user)
2276 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2278 assert %{} = json_response(conn, 200)
2279 user = User.get_cached_by_ap_id(user.ap_id)
2280 assert User.blocks?(user, other_user)
2284 |> assign(:user, user)
2285 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2287 assert %{} = json_response(conn, 200)
2288 user = User.get_cached_by_ap_id(user.ap_id)
2289 refute User.blocks?(user, other_user)
2292 test "getting a list of domain blocks", %{conn: conn} do
2293 user = insert(:user)
2295 {:ok, user} = User.block_domain(user, "bad.site")
2296 {:ok, user} = User.block_domain(user, "even.worse.site")
2300 |> assign(:user, user)
2301 |> get("/api/v1/domain_blocks")
2303 domain_blocks = json_response(conn, 200)
2305 assert "bad.site" in domain_blocks
2306 assert "even.worse.site" in domain_blocks
2309 test "unimplemented follow_requests, blocks, domain blocks" do
2310 user = insert(:user)
2312 ["blocks", "domain_blocks", "follow_requests"]
2313 |> Enum.each(fn endpoint ->
2316 |> assign(:user, user)
2317 |> get("/api/v1/#{endpoint}")
2319 assert [] = json_response(conn, 200)
2323 test "returns the favorites of a user", %{conn: conn} do
2324 user = insert(:user)
2325 other_user = insert(:user)
2327 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2328 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2330 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2334 |> assign(:user, user)
2335 |> get("/api/v1/favourites")
2337 assert [status] = json_response(first_conn, 200)
2338 assert status["id"] == to_string(activity.id)
2340 assert [{"link", _link_header}] =
2341 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2343 # Honours query params
2344 {:ok, second_activity} =
2345 CommonAPI.post(other_user, %{
2347 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2350 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2352 last_like = status["id"]
2356 |> assign(:user, user)
2357 |> get("/api/v1/favourites?since_id=#{last_like}")
2359 assert [second_status] = json_response(second_conn, 200)
2360 assert second_status["id"] == to_string(second_activity.id)
2364 |> assign(:user, user)
2365 |> get("/api/v1/favourites?limit=0")
2367 assert [] = json_response(third_conn, 200)
2370 describe "getting favorites timeline of specified user" do
2372 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2373 [current_user: current_user, user: user]
2376 test "returns list of statuses favorited by specified user", %{
2378 current_user: current_user,
2381 [activity | _] = insert_pair(:note_activity)
2382 CommonAPI.favorite(activity.id, user)
2386 |> assign(:user, current_user)
2387 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2388 |> json_response(:ok)
2392 assert length(response) == 1
2393 assert like["id"] == activity.id
2396 test "returns favorites for specified user_id when user is not logged in", %{
2400 activity = insert(:note_activity)
2401 CommonAPI.favorite(activity.id, user)
2405 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2406 |> json_response(:ok)
2408 assert length(response) == 1
2411 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2413 current_user: current_user,
2417 CommonAPI.post(current_user, %{
2418 "status" => "Hi @#{user.nickname}!",
2419 "visibility" => "direct"
2422 CommonAPI.favorite(direct.id, user)
2426 |> assign(:user, current_user)
2427 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2428 |> json_response(:ok)
2430 assert length(response) == 1
2432 anonymous_response =
2434 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2435 |> json_response(:ok)
2437 assert Enum.empty?(anonymous_response)
2440 test "does not return others' favorited DM when user is not one of recipients", %{
2442 current_user: current_user,
2445 user_two = insert(:user)
2448 CommonAPI.post(user_two, %{
2449 "status" => "Hi @#{user.nickname}!",
2450 "visibility" => "direct"
2453 CommonAPI.favorite(direct.id, user)
2457 |> assign(:user, current_user)
2458 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2459 |> json_response(:ok)
2461 assert Enum.empty?(response)
2464 test "paginates favorites using since_id and max_id", %{
2466 current_user: current_user,
2469 activities = insert_list(10, :note_activity)
2471 Enum.each(activities, fn activity ->
2472 CommonAPI.favorite(activity.id, user)
2475 third_activity = Enum.at(activities, 2)
2476 seventh_activity = Enum.at(activities, 6)
2480 |> assign(:user, current_user)
2481 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2482 since_id: third_activity.id,
2483 max_id: seventh_activity.id
2485 |> json_response(:ok)
2487 assert length(response) == 3
2488 refute third_activity in response
2489 refute seventh_activity in response
2492 test "limits favorites using limit parameter", %{
2494 current_user: current_user,
2498 |> insert_list(:note_activity)
2499 |> Enum.each(fn activity ->
2500 CommonAPI.favorite(activity.id, user)
2505 |> assign(:user, current_user)
2506 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2507 |> json_response(:ok)
2509 assert length(response) == 3
2512 test "returns empty response when user does not have any favorited statuses", %{
2514 current_user: current_user,
2519 |> assign(:user, current_user)
2520 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2521 |> json_response(:ok)
2523 assert Enum.empty?(response)
2526 test "returns 404 error when specified user is not exist", %{conn: conn} do
2527 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2529 assert json_response(conn, 404) == %{"error" => "Record not found"}
2532 test "returns 403 error when user has hidden own favorites", %{
2534 current_user: current_user
2536 user = insert(:user, %{info: %{hide_favorites: true}})
2537 activity = insert(:note_activity)
2538 CommonAPI.favorite(activity.id, user)
2542 |> assign(:user, current_user)
2543 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2545 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2548 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2549 user = insert(:user)
2550 activity = insert(:note_activity)
2551 CommonAPI.favorite(activity.id, user)
2555 |> assign(:user, current_user)
2556 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2558 assert user.info.hide_favorites
2559 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2563 test "get instance information", %{conn: conn} do
2564 conn = get(conn, "/api/v1/instance")
2565 assert result = json_response(conn, 200)
2567 email = Config.get([:instance, :email])
2568 # Note: not checking for "max_toot_chars" since it's optional
2574 "email" => from_config_email,
2576 "streaming_api" => _
2581 "registrations" => _,
2585 assert email == from_config_email
2588 test "get instance stats", %{conn: conn} do
2589 user = insert(:user, %{local: true})
2591 user2 = insert(:user, %{local: true})
2592 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2594 insert(:user, %{local: false, nickname: "u@peer1.com"})
2595 insert(:user, %{local: false, nickname: "u@peer2.com"})
2597 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2599 # Stats should count users with missing or nil `info.deactivated` value
2600 user = User.get_cached_by_id(user.id)
2601 info_change = Changeset.change(user.info, %{deactivated: nil})
2605 |> Changeset.change()
2606 |> Changeset.put_embed(:info, info_change)
2607 |> User.update_and_set_cache()
2609 Pleroma.Stats.force_update()
2611 conn = get(conn, "/api/v1/instance")
2613 assert result = json_response(conn, 200)
2615 stats = result["stats"]
2618 assert stats["user_count"] == 1
2619 assert stats["status_count"] == 1
2620 assert stats["domain_count"] == 2
2623 test "get peers", %{conn: conn} do
2624 insert(:user, %{local: false, nickname: "u@peer1.com"})
2625 insert(:user, %{local: false, nickname: "u@peer2.com"})
2627 Pleroma.Stats.force_update()
2629 conn = get(conn, "/api/v1/instance/peers")
2631 assert result = json_response(conn, 200)
2633 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2636 test "put settings", %{conn: conn} do
2637 user = insert(:user)
2641 |> assign(:user, user)
2642 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2644 assert _result = json_response(conn, 200)
2646 user = User.get_cached_by_ap_id(user.ap_id)
2647 assert user.info.settings == %{"programming" => "socks"}
2650 describe "pinned statuses" do
2652 user = insert(:user)
2653 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2655 [user: user, activity: activity]
2658 clear_config([:instance, :max_pinned_statuses]) do
2659 Config.put([:instance, :max_pinned_statuses], 1)
2662 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2663 {:ok, _} = CommonAPI.pin(activity.id, user)
2667 |> assign(:user, user)
2668 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2669 |> json_response(200)
2671 id_str = to_string(activity.id)
2673 assert [%{"id" => ^id_str, "pinned" => true}] = result
2676 test "pin status", %{conn: conn, user: user, activity: activity} do
2677 id_str = to_string(activity.id)
2679 assert %{"id" => ^id_str, "pinned" => true} =
2681 |> assign(:user, user)
2682 |> post("/api/v1/statuses/#{activity.id}/pin")
2683 |> json_response(200)
2685 assert [%{"id" => ^id_str, "pinned" => true}] =
2687 |> assign(:user, user)
2688 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2689 |> json_response(200)
2692 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2693 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2697 |> assign(:user, user)
2698 |> post("/api/v1/statuses/#{dm.id}/pin")
2700 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2703 test "unpin status", %{conn: conn, user: user, activity: activity} do
2704 {:ok, _} = CommonAPI.pin(activity.id, user)
2706 id_str = to_string(activity.id)
2707 user = refresh_record(user)
2709 assert %{"id" => ^id_str, "pinned" => false} =
2711 |> assign(:user, user)
2712 |> post("/api/v1/statuses/#{activity.id}/unpin")
2713 |> json_response(200)
2717 |> assign(:user, user)
2718 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2719 |> json_response(200)
2722 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2725 |> assign(:user, user)
2726 |> post("/api/v1/statuses/1/unpin")
2728 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2731 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2732 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2734 id_str_one = to_string(activity_one.id)
2736 assert %{"id" => ^id_str_one, "pinned" => true} =
2738 |> assign(:user, user)
2739 |> post("/api/v1/statuses/#{id_str_one}/pin")
2740 |> json_response(200)
2742 user = refresh_record(user)
2744 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2746 |> assign(:user, user)
2747 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2748 |> json_response(400)
2754 Config.put([:rich_media, :enabled], true)
2756 user = insert(:user)
2760 test "returns rich-media card", %{conn: conn, user: user} do
2761 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2764 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2765 "provider_name" => "example.com",
2766 "provider_url" => "https://example.com",
2767 "title" => "The Rock",
2769 "url" => "https://example.com/ogp",
2771 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2774 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2775 "title" => "The Rock",
2776 "type" => "video.movie",
2777 "url" => "https://example.com/ogp",
2779 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2786 |> get("/api/v1/statuses/#{activity.id}/card")
2787 |> json_response(200)
2789 assert response == card_data
2791 # works with private posts
2793 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2797 |> assign(:user, user)
2798 |> get("/api/v1/statuses/#{activity.id}/card")
2799 |> json_response(200)
2801 assert response_two == card_data
2804 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2806 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2810 |> get("/api/v1/statuses/#{activity.id}/card")
2811 |> json_response(:ok)
2813 assert response == %{
2815 "title" => "Pleroma",
2816 "description" => "",
2818 "provider_name" => "example.com",
2819 "provider_url" => "https://example.com",
2820 "url" => "https://example.com/ogp-missing-data",
2823 "title" => "Pleroma",
2824 "type" => "website",
2825 "url" => "https://example.com/ogp-missing-data"
2833 user = insert(:user)
2834 for_user = insert(:user)
2837 CommonAPI.post(user, %{
2838 "status" => "heweoo?"
2842 CommonAPI.post(user, %{
2843 "status" => "heweoo!"
2848 |> assign(:user, for_user)
2849 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2851 assert json_response(response1, 200)["bookmarked"] == true
2855 |> assign(:user, for_user)
2856 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2858 assert json_response(response2, 200)["bookmarked"] == true
2862 |> assign(:user, for_user)
2863 |> get("/api/v1/bookmarks")
2865 assert [json_response(response2, 200), json_response(response1, 200)] ==
2866 json_response(bookmarks, 200)
2870 |> assign(:user, for_user)
2871 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2873 assert json_response(response1, 200)["bookmarked"] == false
2877 |> assign(:user, for_user)
2878 |> get("/api/v1/bookmarks")
2880 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2883 describe "conversation muting" do
2885 post_user = insert(:user)
2886 user = insert(:user)
2888 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2890 [user: user, activity: activity]
2893 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2894 id_str = to_string(activity.id)
2896 assert %{"id" => ^id_str, "muted" => true} =
2898 |> assign(:user, user)
2899 |> post("/api/v1/statuses/#{activity.id}/mute")
2900 |> json_response(200)
2903 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2904 {:ok, _} = CommonAPI.add_mute(user, activity)
2908 |> assign(:user, user)
2909 |> post("/api/v1/statuses/#{activity.id}/mute")
2911 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2914 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2915 {:ok, _} = CommonAPI.add_mute(user, activity)
2917 id_str = to_string(activity.id)
2918 user = refresh_record(user)
2920 assert %{"id" => ^id_str, "muted" => false} =
2922 |> assign(:user, user)
2923 |> post("/api/v1/statuses/#{activity.id}/unmute")
2924 |> json_response(200)
2928 describe "reports" do
2930 reporter = insert(:user)
2931 target_user = insert(:user)
2933 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2935 [reporter: reporter, target_user: target_user, activity: activity]
2938 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2939 assert %{"action_taken" => false, "id" => _} =
2941 |> assign(:user, reporter)
2942 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2943 |> json_response(200)
2946 test "submit a report with statuses and comment", %{
2949 target_user: target_user,
2952 assert %{"action_taken" => false, "id" => _} =
2954 |> assign(:user, reporter)
2955 |> post("/api/v1/reports", %{
2956 "account_id" => target_user.id,
2957 "status_ids" => [activity.id],
2958 "comment" => "bad status!",
2959 "forward" => "false"
2961 |> json_response(200)
2964 test "account_id is required", %{
2969 assert %{"error" => "Valid `account_id` required"} =
2971 |> assign(:user, reporter)
2972 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2973 |> json_response(400)
2976 test "comment must be up to the size specified in the config", %{
2979 target_user: target_user
2981 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2982 comment = String.pad_trailing("a", max_size + 1, "a")
2984 error = %{"error" => "Comment must be up to #{max_size} characters"}
2988 |> assign(:user, reporter)
2989 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2990 |> json_response(400)
2993 test "returns error when account is not exist", %{
3000 |> assign(:user, reporter)
3001 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3003 assert json_response(conn, 400) == %{"error" => "Account not found"}
3007 describe "link headers" do
3008 test "preserves parameters in link headers", %{conn: conn} do
3009 user = insert(:user)
3010 other_user = insert(:user)
3013 CommonAPI.post(other_user, %{
3014 "status" => "hi @#{user.nickname}",
3015 "visibility" => "public"
3019 CommonAPI.post(other_user, %{
3020 "status" => "hi @#{user.nickname}",
3021 "visibility" => "public"
3024 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3025 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3029 |> assign(:user, user)
3030 |> get("/api/v1/notifications", %{media_only: true})
3032 assert [link_header] = get_resp_header(conn, "link")
3033 assert link_header =~ ~r/media_only=true/
3034 assert link_header =~ ~r/min_id=#{notification2.id}/
3035 assert link_header =~ ~r/max_id=#{notification1.id}/
3039 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3040 # Need to set an old-style integer ID to reproduce the problem
3041 # (these are no longer assigned to new accounts but were preserved
3042 # for existing accounts during the migration to flakeIDs)
3043 user_one = insert(:user, %{id: 1212})
3044 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3048 |> get("/api/v1/accounts/#{user_one.id}")
3052 |> get("/api/v1/accounts/#{user_two.nickname}")
3056 |> get("/api/v1/accounts/#{user_two.id}")
3058 acc_one = json_response(resp_one, 200)
3059 acc_two = json_response(resp_two, 200)
3060 acc_three = json_response(resp_three, 200)
3061 refute acc_one == acc_two
3062 assert acc_two == acc_three
3065 describe "custom emoji" do
3066 test "with tags", %{conn: conn} do
3069 |> get("/api/v1/custom_emojis")
3070 |> json_response(200)
3072 assert Map.has_key?(emoji, "shortcode")
3073 assert Map.has_key?(emoji, "static_url")
3074 assert Map.has_key?(emoji, "tags")
3075 assert is_list(emoji["tags"])
3076 assert Map.has_key?(emoji, "category")
3077 assert Map.has_key?(emoji, "url")
3078 assert Map.has_key?(emoji, "visible_in_picker")
3082 describe "index/2 redirections" do
3083 setup %{conn: conn} do
3087 signing_salt: "cooldude"
3092 |> Plug.Session.call(Plug.Session.init(session_opts))
3095 test_path = "/web/statuses/test"
3096 %{conn: conn, path: test_path}
3099 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3100 conn = get(conn, path)
3102 assert conn.status == 302
3103 assert redirected_to(conn) == "/web/login"
3106 test "redirects not logged-in users to the login page on private instances", %{
3110 Config.put([:instance, :public], false)
3112 conn = get(conn, path)
3114 assert conn.status == 302
3115 assert redirected_to(conn) == "/web/login"
3118 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3119 token = insert(:oauth_token)
3123 |> assign(:user, token.user)
3124 |> put_session(:oauth_token, token.token)
3127 assert conn.status == 200
3130 test "saves referer path to session", %{conn: conn, path: path} do
3131 conn = get(conn, path)
3132 return_to = Plug.Conn.get_session(conn, :return_to)
3134 assert return_to == path
3137 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3138 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3139 auth = insert(:oauth_authorization, app: app)
3143 |> put_session(:return_to, path)
3144 |> get("/web/login", %{code: auth.token})
3146 assert conn.status == 302
3147 assert redirected_to(conn) == path
3150 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3151 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3152 auth = insert(:oauth_authorization, app: app)
3154 conn = get(conn, "/web/login", %{code: auth.token})
3156 assert conn.status == 302
3157 assert redirected_to(conn) == "/web/getting-started"
3161 describe "scheduled activities" do
3162 test "creates a scheduled activity", %{conn: conn} do
3163 user = insert(:user)
3164 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3168 |> assign(:user, user)
3169 |> post("/api/v1/statuses", %{
3170 "status" => "scheduled",
3171 "scheduled_at" => scheduled_at
3174 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3175 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3176 assert [] == Repo.all(Activity)
3179 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3180 user = insert(:user)
3181 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3183 file = %Plug.Upload{
3184 content_type: "image/jpg",
3185 path: Path.absname("test/fixtures/image.jpg"),
3186 filename: "an_image.jpg"
3189 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3193 |> assign(:user, user)
3194 |> post("/api/v1/statuses", %{
3195 "media_ids" => [to_string(upload.id)],
3196 "status" => "scheduled",
3197 "scheduled_at" => scheduled_at
3200 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3201 assert %{"type" => "image"} = media_attachment
3204 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3206 user = insert(:user)
3209 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3213 |> assign(:user, user)
3214 |> post("/api/v1/statuses", %{
3215 "status" => "not scheduled",
3216 "scheduled_at" => scheduled_at
3219 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3220 assert [] == Repo.all(ScheduledActivity)
3223 test "returns error when daily user limit is exceeded", %{conn: conn} do
3224 user = insert(:user)
3227 NaiveDateTime.utc_now()
3228 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3229 |> NaiveDateTime.to_iso8601()
3231 attrs = %{params: %{}, scheduled_at: today}
3232 {:ok, _} = ScheduledActivity.create(user, attrs)
3233 {:ok, _} = ScheduledActivity.create(user, attrs)
3237 |> assign(:user, user)
3238 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3240 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3243 test "returns error when total user limit is exceeded", %{conn: conn} do
3244 user = insert(:user)
3247 NaiveDateTime.utc_now()
3248 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3249 |> NaiveDateTime.to_iso8601()
3252 NaiveDateTime.utc_now()
3253 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3254 |> NaiveDateTime.to_iso8601()
3256 attrs = %{params: %{}, scheduled_at: today}
3257 {:ok, _} = ScheduledActivity.create(user, attrs)
3258 {:ok, _} = ScheduledActivity.create(user, attrs)
3259 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3263 |> assign(:user, user)
3264 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3266 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3269 test "shows scheduled activities", %{conn: conn} do
3270 user = insert(:user)
3271 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3272 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3273 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3274 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3278 |> assign(:user, user)
3283 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3285 result = json_response(conn_res, 200)
3286 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3291 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3293 result = json_response(conn_res, 200)
3294 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3299 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3301 result = json_response(conn_res, 200)
3302 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3305 test "shows a scheduled activity", %{conn: conn} do
3306 user = insert(:user)
3307 scheduled_activity = insert(:scheduled_activity, user: user)
3311 |> assign(:user, user)
3312 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3314 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3315 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3319 |> assign(:user, user)
3320 |> get("/api/v1/scheduled_statuses/404")
3322 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3325 test "updates a scheduled activity", %{conn: conn} do
3326 user = insert(:user)
3327 scheduled_activity = insert(:scheduled_activity, user: user)
3330 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3334 |> assign(:user, user)
3335 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3336 scheduled_at: new_scheduled_at
3339 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3340 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3344 |> assign(:user, user)
3345 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3347 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3350 test "deletes a scheduled activity", %{conn: conn} do
3351 user = insert(:user)
3352 scheduled_activity = insert(:scheduled_activity, user: user)
3356 |> assign(:user, user)
3357 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3359 assert %{} = json_response(res_conn, 200)
3360 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3364 |> assign(:user, user)
3365 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3367 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3371 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3372 user1 = insert(:user)
3373 user2 = insert(:user)
3374 user3 = insert(:user)
3376 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3378 # Reply to status from another user
3381 |> assign(:user, user2)
3382 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3384 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3386 activity = Activity.get_by_id_with_object(id)
3388 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3389 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3391 # Reblog from the third user
3394 |> assign(:user, user3)
3395 |> post("/api/v1/statuses/#{activity.id}/reblog")
3397 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3398 json_response(conn2, 200)
3400 assert to_string(activity.id) == id
3402 # Getting third user status
3405 |> assign(:user, user3)
3406 |> get("api/v1/timelines/home")
3408 [reblogged_activity] = json_response(conn3, 200)
3410 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3412 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3413 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3416 describe "create account by app" do
3417 test "Account registration via Application", %{conn: conn} do
3420 |> post("/api/v1/apps", %{
3421 client_name: "client_name",
3422 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3423 scopes: "read, write, follow"
3427 "client_id" => client_id,
3428 "client_secret" => client_secret,
3430 "name" => "client_name",
3431 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3434 } = json_response(conn, 200)
3438 |> post("/oauth/token", %{
3439 grant_type: "client_credentials",
3440 client_id: client_id,
3441 client_secret: client_secret
3444 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3445 json_response(conn, 200)
3448 token_from_db = Repo.get_by(Token, token: token)
3449 assert token_from_db
3451 assert scope == "read write follow"
3455 |> put_req_header("authorization", "Bearer " <> token)
3456 |> post("/api/v1/accounts", %{
3458 email: "lain@example.org",
3459 password: "PlzDontHackLain",
3464 "access_token" => token,
3465 "created_at" => _created_at,
3467 "token_type" => "Bearer"
3468 } = json_response(conn, 200)
3470 token_from_db = Repo.get_by(Token, token: token)
3471 assert token_from_db
3472 token_from_db = Repo.preload(token_from_db, :user)
3473 assert token_from_db.user
3475 assert token_from_db.user.info.confirmation_pending
3478 test "rate limit", %{conn: conn} do
3479 app_token = insert(:oauth_token, user: nil)
3482 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3483 |> Map.put(:remote_ip, {15, 15, 15, 15})
3488 |> post("/api/v1/accounts", %{
3489 username: "#{i}lain",
3490 email: "#{i}lain@example.org",
3491 password: "PlzDontHackLain",
3496 "access_token" => token,
3497 "created_at" => _created_at,
3499 "token_type" => "Bearer"
3500 } = json_response(conn, 200)
3502 token_from_db = Repo.get_by(Token, token: token)
3503 assert token_from_db
3504 token_from_db = Repo.preload(token_from_db, :user)
3505 assert token_from_db.user
3507 assert token_from_db.user.info.confirmation_pending
3512 |> post("/api/v1/accounts", %{
3514 email: "6lain@example.org",
3515 password: "PlzDontHackLain",
3519 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3523 describe "GET /api/v1/polls/:id" do
3524 test "returns poll entity for object id", %{conn: conn} do
3525 user = insert(:user)
3528 CommonAPI.post(user, %{
3529 "status" => "Pleroma does",
3530 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3533 object = Object.normalize(activity)
3537 |> assign(:user, user)
3538 |> get("/api/v1/polls/#{object.id}")
3540 response = json_response(conn, 200)
3541 id = to_string(object.id)
3542 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3545 test "does not expose polls for private statuses", %{conn: conn} do
3546 user = insert(:user)
3547 other_user = insert(:user)
3550 CommonAPI.post(user, %{
3551 "status" => "Pleroma does",
3552 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3553 "visibility" => "private"
3556 object = Object.normalize(activity)
3560 |> assign(:user, other_user)
3561 |> get("/api/v1/polls/#{object.id}")
3563 assert json_response(conn, 404)
3567 describe "POST /api/v1/polls/:id/votes" do
3568 test "votes are added to the poll", %{conn: conn} do
3569 user = insert(:user)
3570 other_user = insert(:user)
3573 CommonAPI.post(user, %{
3574 "status" => "A very delicious sandwich",
3576 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3582 object = Object.normalize(activity)
3586 |> assign(:user, other_user)
3587 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3589 assert json_response(conn, 200)
3590 object = Object.get_by_id(object.id)
3592 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3597 test "author can't vote", %{conn: conn} do
3598 user = insert(:user)
3601 CommonAPI.post(user, %{
3602 "status" => "Am I cute?",
3603 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3606 object = Object.normalize(activity)
3609 |> assign(:user, user)
3610 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3611 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3613 object = Object.get_by_id(object.id)
3615 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3618 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3619 user = insert(:user)
3620 other_user = insert(:user)
3623 CommonAPI.post(user, %{
3624 "status" => "The glass is",
3625 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3628 object = Object.normalize(activity)
3631 |> assign(:user, other_user)
3632 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3633 |> json_response(422) == %{"error" => "Too many choices"}
3635 object = Object.get_by_id(object.id)
3637 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3642 test "does not allow choice index to be greater than options count", %{conn: conn} do
3643 user = insert(:user)
3644 other_user = insert(:user)
3647 CommonAPI.post(user, %{
3648 "status" => "Am I cute?",
3649 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3652 object = Object.normalize(activity)
3656 |> assign(:user, other_user)
3657 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3659 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3662 test "returns 404 error when object is not exist", %{conn: conn} do
3663 user = insert(:user)
3667 |> assign(:user, user)
3668 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3670 assert json_response(conn, 404) == %{"error" => "Record not found"}
3673 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3674 user = insert(:user)
3675 other_user = insert(:user)
3678 CommonAPI.post(user, %{
3679 "status" => "Am I cute?",
3680 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3681 "visibility" => "private"
3684 object = Object.normalize(activity)
3688 |> assign(:user, other_user)
3689 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3691 assert json_response(conn, 404) == %{"error" => "Record not found"}
3695 describe "GET /api/v1/statuses/:id/favourited_by" do
3697 user = insert(:user)
3698 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3702 |> assign(:user, user)
3704 [conn: conn, activity: activity, user: user]
3707 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3708 other_user = insert(:user)
3709 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3713 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3714 |> json_response(:ok)
3716 [%{"id" => id}] = response
3718 assert id == other_user.id
3721 test "returns empty array when status has not been favorited yet", %{
3727 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3728 |> json_response(:ok)
3730 assert Enum.empty?(response)
3733 test "does not return users who have favorited the status but are blocked", %{
3734 conn: %{assigns: %{user: user}} = conn,
3737 other_user = insert(:user)
3738 {:ok, user} = User.block(user, other_user)
3740 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3744 |> assign(:user, user)
3745 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3746 |> json_response(:ok)
3748 assert Enum.empty?(response)
3751 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3752 other_user = insert(:user)
3753 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3757 |> assign(:user, nil)
3758 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3759 |> json_response(:ok)
3761 [%{"id" => id}] = response
3762 assert id == other_user.id
3765 test "requires authentification for private posts", %{conn: conn, user: user} do
3766 other_user = insert(:user)
3769 CommonAPI.post(user, %{
3770 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3771 "visibility" => "direct"
3774 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3777 |> assign(:user, nil)
3778 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3779 |> json_response(404)
3783 |> assign(:user, other_user)
3784 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3785 |> json_response(200)
3787 [%{"id" => id}] = response
3788 assert id == other_user.id
3792 describe "GET /api/v1/statuses/:id/reblogged_by" do
3794 user = insert(:user)
3795 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3799 |> assign(:user, user)
3801 [conn: conn, activity: activity, user: user]
3804 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3805 other_user = insert(:user)
3806 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3810 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3811 |> json_response(:ok)
3813 [%{"id" => id}] = response
3815 assert id == other_user.id
3818 test "returns empty array when status has not been reblogged yet", %{
3824 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3825 |> json_response(:ok)
3827 assert Enum.empty?(response)
3830 test "does not return users who have reblogged the status but are blocked", %{
3831 conn: %{assigns: %{user: user}} = conn,
3834 other_user = insert(:user)
3835 {:ok, user} = User.block(user, other_user)
3837 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3841 |> assign(:user, user)
3842 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3843 |> json_response(:ok)
3845 assert Enum.empty?(response)
3848 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3849 other_user = insert(:user)
3850 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3854 |> assign(:user, nil)
3855 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3856 |> json_response(:ok)
3858 [%{"id" => id}] = response
3859 assert id == other_user.id
3862 test "requires authentification for private posts", %{conn: conn, user: user} do
3863 other_user = insert(:user)
3866 CommonAPI.post(user, %{
3867 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3868 "visibility" => "direct"
3872 |> assign(:user, nil)
3873 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3874 |> json_response(404)
3878 |> assign(:user, other_user)
3879 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3880 |> json_response(200)
3882 assert [] == response
3886 describe "POST /auth/password, with valid parameters" do
3887 setup %{conn: conn} do
3888 user = insert(:user)
3889 conn = post(conn, "/auth/password?email=#{user.email}")
3890 %{conn: conn, user: user}
3893 test "it returns 204", %{conn: conn} do
3894 assert json_response(conn, :no_content)
3897 test "it creates a PasswordResetToken record for user", %{user: user} do
3898 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3902 test "it sends an email to user", %{user: user} do
3903 ObanHelpers.perform_all()
3904 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3906 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3907 notify_email = Config.get([:instance, :notify_email])
3908 instance_name = Config.get([:instance, :name])
3911 from: {instance_name, notify_email},
3912 to: {user.name, user.email},
3913 html_body: email.html_body
3918 describe "POST /auth/password, with invalid parameters" do
3920 user = insert(:user)
3924 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3925 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3926 assert conn.status == 404
3927 assert conn.resp_body == ""
3930 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3931 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3932 conn = post(conn, "/auth/password?email=#{user.email}")
3933 assert conn.status == 400
3934 assert conn.resp_body == ""
3938 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3940 user = insert(:user)
3941 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3945 |> Changeset.change()
3946 |> Changeset.put_embed(:info, info_change)
3949 assert user.info.confirmation_pending
3954 clear_config([:instance, :account_activation_required]) do
3955 Config.put([:instance, :account_activation_required], true)
3958 test "resend account confirmation email", %{conn: conn, user: user} do
3960 |> assign(:user, user)
3961 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3962 |> json_response(:no_content)
3964 ObanHelpers.perform_all()
3966 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3967 notify_email = Config.get([:instance, :notify_email])
3968 instance_name = Config.get([:instance, :name])
3971 from: {instance_name, notify_email},
3972 to: {user.name, user.email},
3973 html_body: email.html_body
3978 describe "GET /api/v1/suggestions" do
3980 user = insert(:user)
3981 other_user = insert(:user)
3982 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3983 url500 = "http://test500?#{host}&#{user.nickname}"
3984 url200 = "http://test200?#{host}&#{user.nickname}"
3987 %{method: :get, url: ^url500} ->
3988 %Tesla.Env{status: 500, body: "bad request"}
3990 %{method: :get, url: ^url200} ->
3994 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3996 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4000 [user: user, other_user: other_user]
4003 clear_config(:suggestions)
4005 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4006 Config.put([:suggestions, :enabled], false)
4010 |> assign(:user, user)
4011 |> get("/api/v1/suggestions")
4012 |> json_response(200)
4017 test "returns error", %{conn: conn, user: user} do
4018 Config.put([:suggestions, :enabled], true)
4019 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4021 assert capture_log(fn ->
4024 |> assign(:user, user)
4025 |> get("/api/v1/suggestions")
4026 |> json_response(500)
4028 assert res == "Something went wrong"
4029 end) =~ "Could not retrieve suggestions"
4032 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4033 Config.put([:suggestions, :enabled], true)
4034 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4038 |> assign(:user, user)
4039 |> get("/api/v1/suggestions")
4040 |> json_response(200)
4045 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4046 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4050 "acct" => other_user.ap_id,
4051 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4052 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4053 "id" => other_user.id